names.c: fix compiler warnings
[s-mailx.git] / maildir.c
blob861414453d5e1a9d5a12aa258f29167e5fac94ab
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
6 */
7 /*
8 * Copyright (c) 2004
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef lint
41 #ifdef DOSCCS
42 static char sccsid[] = "@(#)maildir.c 1.20 (gritter) 12/28/06";
43 #endif
44 #endif /* not lint */
46 #include "config.h"
48 #include "rcv.h"
49 #include "extern.h"
50 #include <time.h>
51 #include <dirent.h>
52 #include <unistd.h>
53 #include <sys/stat.h>
54 #include <errno.h>
57 * Mail -- a mail program
59 * Maildir folder support.
62 static struct mditem {
63 struct message *md_data;
64 unsigned md_hash;
65 } *mdtable;
66 static long mdprime;
68 static sigjmp_buf maildirjmp;
70 static int maildir_setfile1(const char *name, int newmail, int omsgCount);
71 static int mdcmp(const void *a, const void *b);
72 static int subdir(const char *name, const char *sub, int newmail);
73 static void cleantmp(const char *name);
74 static void append(const char *name, const char *sub, const char *fn);
75 static void readin(const char *name, struct message *m);
76 static void maildir_update(void);
77 static void move(struct message *m);
78 static char *mkname(time_t t, enum mflag f, const char *pref);
79 static void maildircatch(int s);
80 static enum okay maildir_append1(const char *name, FILE *fp, off_t off1,
81 long size, enum mflag flag);
82 static enum okay trycreate(const char *name);
83 static enum okay mkmaildir(const char *name);
84 static struct message *mdlook(const char *name, struct message *data);
85 static void mktable(void);
86 static enum okay subdir_remove(const char *name, const char *sub);
88 int
89 maildir_setfile(const char *name, int newmail, int isedit)
91 sighandler_type saveint;
92 struct cw cw;
93 int i = -1, omsgCount;
95 (void)&saveint;
96 (void)&i;
97 omsgCount = msgCount;
98 if (cwget(&cw) == STOP) {
99 fprintf(stderr, "Fatal: Cannot open current directory\n");
100 return -1;
102 if (!newmail)
103 quit();
104 saveint = safe_signal(SIGINT, SIG_IGN);
105 if (chdir(name) < 0) {
106 fprintf(stderr, "Cannot change directory to \"%s\".\n", name);
107 cwrelse(&cw);
108 return -1;
110 if (!newmail) {
111 edit = isedit;
112 if (mb.mb_itf) {
113 fclose(mb.mb_itf);
114 mb.mb_itf = NULL;
116 if (mb.mb_otf) {
117 fclose(mb.mb_otf);
118 mb.mb_otf = NULL;
120 initbox(name);
121 mb.mb_type = MB_MAILDIR;
123 mdtable = NULL;
124 if (sigsetjmp(maildirjmp, 1) == 0) {
125 if (newmail)
126 mktable();
127 if (saveint != SIG_IGN)
128 safe_signal(SIGINT, maildircatch);
129 i = maildir_setfile1(name, newmail, omsgCount);
131 if (newmail)
132 free(mdtable);
133 safe_signal(SIGINT, saveint);
134 if (i < 0) {
135 mb.mb_type = MB_VOID;
136 *mailname = '\0';
137 msgCount = 0;
139 if (cwret(&cw) == STOP) {
140 fputs("Fatal: Cannot change back to current directory.\n",
141 stderr);
142 abort();
144 cwrelse(&cw);
145 setmsize(msgCount);
146 if (newmail && mb.mb_sorted && msgCount > omsgCount) {
147 mb.mb_threaded = 0;
148 sort((void *)-1);
150 if (!newmail)
151 sawcom = 0;
152 if (!newmail && !edit && msgCount == 0) {
153 if (mb.mb_type == MB_MAILDIR && value("emptystart") == NULL)
154 fprintf(stderr, "No mail at %s\n", name);
155 return 1;
157 if (newmail && msgCount > omsgCount)
158 newmailinfo(omsgCount);
159 return 0;
162 static int
163 maildir_setfile1(const char *name, int newmail, int omsgCount)
165 int i;
167 if (!newmail)
168 cleantmp(name);
169 mb.mb_perm = Rflag ? 0 : MB_DELE;
170 if ((i = subdir(name, "cur", newmail)) != 0)
171 return i;
172 if ((i = subdir(name, "new", newmail)) != 0)
173 return i;
174 append(name, NULL, NULL);
175 for (i = newmail?omsgCount:0; i < msgCount; i++)
176 readin(name, &message[i]);
177 if (newmail) {
178 if (msgCount > omsgCount)
179 qsort(&message[omsgCount],
180 msgCount - omsgCount,
181 sizeof *message, mdcmp);
182 } else {
183 if (msgCount)
184 qsort(message, msgCount, sizeof *message, mdcmp);
186 return msgCount;
190 * In combination with the names from mkname(), this comparison function
191 * ensures that the order of messages in a maildir folder created by mailx
192 * remains always the same. In effect, if a mbox folder is transferred to
193 * a maildir folder by 'copy *', the order of the messages in mailx will
194 * not change.
196 static int
197 mdcmp(const void *a, const void *b)
199 long i;
201 if ((i = ((struct message *)a)->m_time -
202 ((struct message *)b)->m_time) == 0)
203 i = strcmp(&((struct message *)a)->m_maildir_file[4],
204 &((struct message *)b)->m_maildir_file[4]);
205 return i;
208 static int
209 subdir(const char *name, const char *sub, int newmail)
211 DIR *dirfd;
212 struct dirent *dp;
214 if ((dirfd = opendir(sub)) == NULL) {
215 fprintf(stderr, "Cannot open directory \"%s/%s\".\n",
216 name, sub);
217 return -1;
219 if (access(sub, W_OK) < 0)
220 mb.mb_perm = 0;
221 while ((dp = readdir(dirfd)) != NULL) {
222 if (dp->d_name[0] == '.' &&
223 (dp->d_name[1] == '\0' ||
224 (dp->d_name[1] == '.' &&
225 dp->d_name[2] == '\0')))
226 continue;
227 if (dp->d_name[0] == '.')
228 continue;
229 if (!newmail || mdlook(dp->d_name, NULL) == NULL)
230 append(name, sub, dp->d_name);
232 closedir(dirfd);
233 return 0;
236 static void
237 cleantmp(const char *name)
239 struct stat st;
240 DIR *dirfd;
241 struct dirent *dp;
242 char *fn = NULL;
243 size_t fnsz = 0, ssz;
244 time_t now;
245 (void)name;
247 if ((dirfd = opendir("tmp")) == NULL)
248 return;
249 time(&now);
250 while ((dp = readdir(dirfd)) != NULL) {
251 if (dp->d_name[0] == '.' &&
252 (dp->d_name[1] == '\0' ||
253 (dp->d_name[1] == '.' &&
254 dp->d_name[2] == '\0')))
255 continue;
256 if (dp->d_name[0] == '.')
257 continue;
258 if ((ssz = strlen(dp->d_name)) + 5 > fnsz) {
259 free(fn);
260 fn = smalloc(fnsz = ssz + 40);
262 strcpy(fn, "tmp/");
263 strcpy(&fn[4], dp->d_name);
264 if (stat(fn, &st) < 0)
265 continue;
266 if (st.st_atime + 36*3600 < now)
267 unlink(fn);
269 free(fn);
270 closedir(dirfd);
273 static void
274 append(const char *name, const char *sub, const char *fn)
276 struct message *m;
277 size_t sz;
278 time_t t = 0;
279 enum mflag f = MUSED|MNOFROM|MNEWEST;
280 const char *cp;
281 char *xp;
282 (void)name;
284 if (fn && sub) {
285 if (strcmp(sub, "new") == 0)
286 f |= MNEW;
287 t = strtol(fn, &xp, 10);
288 if ((cp = strrchr(xp, ',')) != NULL &&
289 cp > &xp[2] && cp[-1] == '2' && cp[-2] == ':') {
290 while (*++cp) {
291 switch (*cp) {
292 case 'F':
293 f |= MFLAGGED;
294 break;
295 case 'R':
296 f |= MANSWERED;
297 break;
298 case 'S':
299 f |= MREAD;
300 break;
301 case 'T':
302 f |= MDELETED;
303 break;
304 case 'D':
305 f |= MDRAFT;
306 break;
311 if (msgCount + 1 >= msgspace) {
312 const int chunk = 64;
313 message = srealloc(message,
314 (msgspace += chunk) * sizeof *message);
315 memset(&message[msgCount], 0, chunk * sizeof *message);
317 if (fn == NULL || sub == NULL)
318 return;
319 m = &message[msgCount++];
320 m->m_maildir_file = smalloc((sz = strlen(sub)) + strlen(fn) + 2);
321 strcpy(m->m_maildir_file, sub);
322 m->m_maildir_file[sz] = '/';
323 strcpy(&m->m_maildir_file[sz+1], fn);
324 m->m_time = t;
325 m->m_flag = f;
326 m->m_maildir_hash = ~pjw(fn);
327 return;
330 static void
331 readin(const char *name, struct message *m)
333 char *buf, *bp;
334 size_t bufsize, buflen, count;
335 long size = 0, lines = 0;
336 off_t offset;
337 FILE *fp;
338 int emptyline = 0;
340 if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) {
341 fprintf(stderr, "Cannot read \"%s/%s\" for message %d\n",
342 name, m->m_maildir_file,
343 (int)(m - &message[0] + 1));
344 m->m_flag |= MHIDDEN;
345 return;
347 buf = smalloc(bufsize = LINESIZE);
348 buflen = 0;
349 count = fsize(fp);
350 fseek(mb.mb_otf, 0L, SEEK_END);
351 offset = ftell(mb.mb_otf);
352 while (fgetline(&buf, &bufsize, &count, &buflen, fp, 1) != NULL) {
353 bp = buf;
354 if (buf[0] == 'F' && buf[1] == 'r' && buf[2] == 'o' &&
355 buf[3] == 'm' && buf[4] == ' ') {
356 putc('>', mb.mb_otf);
357 size++;
359 lines++;
360 size += fwrite(bp, 1, buflen, mb.mb_otf);
361 emptyline = *bp == '\n';
363 if (!emptyline) {
364 putc('\n', mb.mb_otf);
365 lines++;
366 size++;
368 Fclose(fp);
369 fflush(mb.mb_otf);
370 m->m_size = m->m_xsize = size;
371 m->m_lines = m->m_xlines = lines;
372 m->m_block = mailx_blockof(offset);
373 m->m_offset = mailx_offsetof(offset);
374 free(buf);
375 substdate(m);
378 void
379 maildir_quit(void)
381 sighandler_type saveint;
382 struct cw cw;
384 (void)&saveint;
385 if (cwget(&cw) == STOP) {
386 fprintf(stderr, "Fatal: Cannot open current directory\n");
387 return;
389 saveint = safe_signal(SIGINT, SIG_IGN);
390 if (chdir(mailname) < 0) {
391 fprintf(stderr, "Cannot change directory to \"%s\".\n",
392 mailname);
393 cwrelse(&cw);
394 return;
396 if (sigsetjmp(maildirjmp, 1) == 0) {
397 if (saveint != SIG_IGN)
398 safe_signal(SIGINT, maildircatch);
399 maildir_update();
401 safe_signal(SIGINT, saveint);
402 if (cwret(&cw) == STOP) {
403 fputs("Fatal: Cannot change back to current directory.\n",
404 stderr);
405 abort();
407 cwrelse(&cw);
410 static void
411 maildir_update(void)
413 FILE *readstat = NULL;
414 struct message *m;
415 int dodel, c, gotcha = 0, held = 0, modflags = 0;
417 if (mb.mb_perm == 0)
418 goto free;
419 if (Tflag != NULL) {
420 if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
421 Tflag = NULL;
423 if (!edit) {
424 holdbits();
425 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
426 if (m->m_flag & MBOX)
427 c++;
429 if (c > 0)
430 if (makembox() == STOP)
431 goto bypass;
433 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
434 if (readstat != NULL && (m->m_flag & (MREAD|MDELETED)) != 0) {
435 char *id;
436 if ((id = hfield("message-id", m)) != NULL ||
437 (id = hfield("article-id", m)) != NULL)
438 fprintf(readstat, "%s\n", id);
440 if (edit)
441 dodel = m->m_flag & MDELETED;
442 else
443 dodel = !((m->m_flag&MPRESERVE) ||
444 (m->m_flag&MTOUCH) == 0);
445 if (dodel) {
446 if (unlink(m->m_maildir_file) < 0)
447 fprintf(stderr, "Cannot delete file \"%s/%s\" "
448 "for message %d.\n",
449 mailname, m->m_maildir_file,
450 (int)(m - &message[0] + 1));
451 else
452 gotcha++;
453 } else {
454 if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS) ||
455 m->m_flag & (MNEW|MBOXED|MSAVED|MSTATUS|
456 MFLAG|MUNFLAG|
457 MANSWER|MUNANSWER|
458 MDRAFT|MUNDRAFT)) {
459 move(m);
460 modflags++;
462 held++;
465 bypass: if (readstat != NULL)
466 Fclose(readstat);
467 if ((gotcha || modflags) && edit) {
468 printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname);
469 printf(value("bsdcompat") || value("bsdmsgs") ?
470 catgets(catd, CATSET, 170, "complete\n") :
471 catgets(catd, CATSET, 212, "updated.\n"));
472 } else if (held && !edit && mb.mb_perm != 0) {
473 if (held == 1)
474 printf(catgets(catd, CATSET, 155,
475 "Held 1 message in %s\n"), mailname);
476 else if (held > 1)
477 printf(catgets(catd, CATSET, 156,
478 "Held %d messages in %s\n"), held, mailname);
480 fflush(stdout);
481 free: for (m = &message[0]; m < &message[msgCount]; m++)
482 free(m->m_maildir_file);
485 static void
486 move(struct message *m)
488 char *fn, *new;
490 fn = mkname(0, m->m_flag, &m->m_maildir_file[4]);
491 new = savecat("cur/", fn);
492 if (strcmp(m->m_maildir_file, new) == 0)
493 return;
494 if (link(m->m_maildir_file, new) < 0) {
495 fprintf(stderr, "Cannot link \"%s/%s\" to \"%s/%s\": "
496 "message %d not touched.\n",
497 mailname, m->m_maildir_file,
498 mailname, new,
499 (int)(m - &message[0] + 1));
500 return;
502 if (unlink(m->m_maildir_file) < 0)
503 fprintf(stderr, "Cannot unlink \"%s/%s\".\n",
504 mailname, m->m_maildir_file);
507 static char *
508 mkname(time_t t, enum mflag f, const char *pref)
510 static unsigned long count;
511 static pid_t mypid;
512 char *cp;
513 static char *node;
514 int size, n, i;
516 if (pref == NULL) {
517 if (mypid == 0)
518 mypid = getpid();
519 if (node == NULL) {
520 cp = nodename(0);
521 n = size = 0;
522 do {
523 if (n < size + 8)
524 node = srealloc(node, size += 20);
525 switch (*cp) {
526 case '/':
527 node[n++] = '\\', node[n++] = '0',
528 node[n++] = '5', node[n++] = '7';
529 break;
530 case ':':
531 node[n++] = '\\', node[n++] = '0',
532 node[n++] = '7', node[n++] = '2';
533 break;
534 default:
535 node[n++] = *cp;
537 } while (*cp++);
539 size = 60 + strlen(node);
540 cp = salloc(size);
541 n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
542 (unsigned long)t,
543 (unsigned long)mypid, ++count, node);
544 } else {
545 size = (n = strlen(pref)) + 13;
546 cp = salloc(size);
547 strcpy(cp, pref);
548 for (i = n; i > 3; i--)
549 if (cp[i-1] == ',' && cp[i-2] == '2' &&
550 cp[i-3] == ':') {
551 n = i;
552 break;
554 if (i <= 3) {
555 strcpy(&cp[n], ":2,");
556 n += 3;
559 if (n < size - 7) {
560 if (f & MDRAFTED)
561 cp[n++] = 'D';
562 if (f & MFLAGGED)
563 cp[n++] = 'F';
564 if (f & MANSWERED)
565 cp[n++] = 'R';
566 if (f & MREAD)
567 cp[n++] = 'S';
568 if (f & MDELETED)
569 cp[n++] = 'T';
570 cp[n] = '\0';
572 return cp;
575 static void
576 maildircatch(int s)
578 siglongjmp(maildirjmp, s);
581 enum okay
582 maildir_append(const char *name, FILE *fp)
584 char *buf, *bp, *lp;
585 size_t bufsize, buflen, count;
586 off_t off1 = -1, offs;
587 int inhead = 1;
588 int flag = MNEW|MNEWEST;
589 long size = 0;
590 enum okay ok;
592 if (mkmaildir(name) != OKAY)
593 return STOP;
594 buf = smalloc(bufsize = LINESIZE);
595 buflen = 0;
596 count = fsize(fp);
597 offs = ftell(fp);
598 do {
599 bp = fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
600 if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
601 if (off1 != (off_t)-1) {
602 ok = maildir_append1(name, fp, off1,
603 size, flag);
604 if (ok == STOP)
605 return STOP;
606 fseek(fp, offs+buflen, SEEK_SET);
608 off1 = offs + buflen;
609 size = 0;
610 inhead = 1;
611 flag = MNEW;
612 } else
613 size += buflen;
614 offs += buflen;
615 if (bp && buf[0] == '\n')
616 inhead = 0;
617 else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
618 lp = &buf[6];
619 while (whitechar(*lp&0377))
620 lp++;
621 if (*lp == ':')
622 while (*++lp != '\0')
623 switch (*lp) {
624 case 'R':
625 flag |= MREAD;
626 break;
627 case 'O':
628 flag &= ~MNEW;
629 break;
631 } else if (bp && inhead &&
632 ascncasecmp(buf, "x-status", 8) == 0) {
633 lp = &buf[8];
634 while (whitechar(*lp&0377))
635 lp++;
636 if (*lp == ':')
637 while (*++lp != '\0')
638 switch (*lp) {
639 case 'F':
640 flag |= MFLAGGED;
641 break;
642 case 'A':
643 flag |= MANSWERED;
644 break;
645 case 'T':
646 flag |= MDRAFTED;
647 break;
650 } while (bp != NULL);
651 free(buf);
652 return OKAY;
655 static enum okay
656 maildir_append1(const char *name, FILE *fp, off_t off1, long size,
657 enum mflag flag)
659 const int attempts = 43200;
660 struct stat st;
661 char buf[4096];
662 char *fn, *tmp, *new;
663 FILE *op;
664 long n, z;
665 int i;
666 time_t now;
668 for (i = 0; i < attempts; i++) {
669 time(&now);
670 fn = mkname(now, flag, NULL);
671 tmp = salloc(n = strlen(name) + strlen(fn) + 6);
672 snprintf(tmp, n, "%s/tmp/%s", name, fn);
673 if (stat(tmp, &st) < 0 && errno == ENOENT)
674 break;
675 sleep(2);
677 if (i >= attempts) {
678 fprintf(stderr,
679 "Cannot create unique file name in \"%s/tmp\".\n",
680 name);
681 return STOP;
683 if ((op = Fopen(tmp, "w")) == NULL) {
684 fprintf(stderr, "Cannot write to \"%s\".\n", tmp);
685 return STOP;
687 fseek(fp, off1, SEEK_SET);
688 while (size > 0) {
689 z = size > (long)sizeof buf ? (long)sizeof buf : size;
690 if ((n = fread(buf, 1, z, fp)) != z ||
691 (size_t)n != fwrite(buf, 1, n, op)) {
692 fprintf(stderr, "Error writing to \"%s\".\n", tmp);
693 Fclose(op);
694 unlink(tmp);
695 return STOP;
697 size -= n;
699 Fclose(op);
700 new = salloc(n = strlen(name) + strlen(fn) + 6);
701 snprintf(new, n, "%s/new/%s", name, fn);
702 if (link(tmp, new) < 0) {
703 fprintf(stderr, "Cannot link \"%s\" to \"%s\".\n", tmp, new);
704 return STOP;
706 if (unlink(tmp) < 0)
707 fprintf(stderr, "Cannot unlink \"%s\".\n", tmp);
708 return OKAY;
711 static enum okay
712 trycreate(const char *name)
714 struct stat st;
716 if (stat(name, &st) == 0) {
717 if (!S_ISDIR(st.st_mode)) {
718 fprintf(stderr, "\"%s\" is not a directory.\n", name);
719 return STOP;
721 } else if (makedir(name) != OKAY) {
722 fprintf(stderr, "Cannot create directory \"%s\".\n", name);
723 return STOP;
724 } else
725 imap_created_mailbox++;
726 return OKAY;
729 static enum okay
730 mkmaildir(const char *name)
732 char *np;
733 size_t sz;
734 enum okay ok = STOP;
736 if (trycreate(name) == OKAY) {
737 np = ac_alloc((sz = strlen(name)) + 5);
738 strcpy(np, name);
739 strcpy(&np[sz], "/tmp");
740 if (trycreate(np) == OKAY) {
741 strcpy(&np[sz], "/new");
742 if (trycreate(np) == OKAY) {
743 strcpy(&np[sz], "/cur");
744 if (trycreate(np) == OKAY)
745 ok = OKAY;
748 ac_free(np);
750 return ok;
753 static struct message *
754 mdlook(const char *name, struct message *data)
756 struct mditem *md;
757 unsigned c, h, n = 0;
759 if (data && data->m_maildir_hash)
760 h = ~data->m_maildir_hash;
761 else
762 h = pjw(name);
763 h %= mdprime;
764 md = &mdtable[c = h];
765 while (md->md_data != NULL) {
766 if (strcmp(&md->md_data->m_maildir_file[4], name) == 0)
767 break;
768 c += n&1 ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
769 n++;
770 while (c >= (unsigned)mdprime)
771 c -= (unsigned)mdprime;
772 md = &mdtable[c];
774 if (data != NULL && md->md_data == NULL)
775 md->md_data = data;
776 return md->md_data ? md->md_data : NULL;
779 static void
780 mktable(void)
782 int i;
784 mdprime = nextprime(msgCount);
785 mdtable = scalloc(mdprime, sizeof *mdtable);
786 for (i = 0; i < msgCount; i++)
787 mdlook(&message[i].m_maildir_file[4], &message[i]);
790 static enum okay
791 subdir_remove(const char *name, const char *sub)
793 char *path;
794 int pathsize, pathend, namelen, sublen, n;
795 DIR *dirfd;
796 struct dirent *dp;
798 namelen = strlen(name);
799 sublen = strlen(sub);
800 path = smalloc(pathsize = namelen + sublen + 30);
801 strcpy(path, name);
802 path[namelen] = '/';
803 strcpy(&path[namelen+1], sub);
804 path[namelen+sublen+1] = '/';
805 path[pathend = namelen + sublen + 2] = '\0';
806 if ((dirfd = opendir(path)) == NULL) {
807 perror(path);
808 free(path);
809 return STOP;
811 while ((dp = readdir(dirfd)) != NULL) {
812 if (dp->d_name[0] == '.' &&
813 (dp->d_name[1] == '\0' ||
814 (dp->d_name[1] == '.' &&
815 dp->d_name[2] == '\0')))
816 continue;
817 if (dp->d_name[0] == '.')
818 continue;
819 n = strlen(dp->d_name);
820 if (pathend + n + 1 > pathsize)
821 path = srealloc(path, pathsize = pathend + n + 30);
822 strcpy(&path[pathend], dp->d_name);
823 if (unlink(path) < 0) {
824 perror(path);
825 closedir(dirfd);
826 free(path);
827 return STOP;
830 closedir(dirfd);
831 path[pathend] = '\0';
832 if (rmdir(path) < 0) {
833 perror(path);
834 free(path);
835 return STOP;
837 free(path);
838 return OKAY;
841 enum okay
842 maildir_remove(const char *name)
844 if (subdir_remove(name, "tmp") == STOP ||
845 subdir_remove(name, "new") == STOP ||
846 subdir_remove(name, "cur") == STOP)
847 return STOP;
848 if (rmdir(name) < 0) {
849 perror(name);
850 return STOP;
852 return OKAY;