cc-test.sh, nail.1: add simple test for -e, -H and -L; improve doc
[s-mailx.git] / maildir.c
blob8b465b0745eb98a5cdfb1bbcb052364d1b881970
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Maildir folder support. FIXME rewrite - why do we chdir(2)??
3 *@ FIXME indeed - my S-Postman Python (!) is faster dealing with maildir!!
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
7 */
8 /*
9 * Copyright (c) 2004
10 * Gunnar Ritter. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
40 #undef n_FILE
41 #define n_FILE maildir
43 #ifndef HAVE_AMALGAMATION
44 # include "nail.h"
45 #endif
47 #include <dirent.h>
49 struct mditem {
50 struct message *md_data;
51 unsigned md_hash;
54 static struct mditem *_maildir_table;
55 static ui32_t _maildir_prime;
56 static sigjmp_buf _maildir_jmp;
58 static void __maildircatch(int s);
59 static void __maildircatch_hold(int s);
61 static unsigned a_maildir_hash(char const *cp);
63 /* Do some cleanup in the tmp/ subdir */
64 static void _cleantmp(void);
66 static int _maildir_setfile1(char const *name, enum fedit_mode fm,
67 int omsgCount);
69 /* In combination with the names from mkname(), this comparison function
70 * ensures that the order of messages in a maildir folder created by mailx
71 * remains always the same. In effect, if a mbox folder is transferred to
72 * a maildir folder by 'copy *', the message order wont' change */
73 static int mdcmp(void const *a, void const *b);
75 static int _maildir_subdir(char const *name, char const *sub,
76 enum fedit_mode fm);
78 static void _maildir_append(char const *name, char const *sub,
79 char const *fn);
81 static void readin(char const *name, struct message *m);
83 static void maildir_update(void);
85 static void _maildir_move(struct message *m);
87 static char * mkname(time_t t, enum mflag f, char const *pref);
89 static enum okay maildir_append1(char const *name, FILE *fp, off_t off1,
90 long size, enum mflag flag);
92 static enum okay trycreate(char const *name);
94 static enum okay mkmaildir(char const *name);
96 static struct message * mdlook(char const *name, struct message *data);
98 static void mktable(void);
100 static enum okay subdir_remove(char const *name, char const *sub);
102 static void
103 __maildircatch(int s)
105 NYD_X; /* Signal handler */
106 siglongjmp(_maildir_jmp, s);
109 static void
110 __maildircatch_hold(int s)
112 NYD_X; /* Signal handler */
113 n_UNUSED(s);
114 /* TODO no STDIO in signal handler, no _() tr's -- pre-translate interrupt
115 * TODO globally; */
116 n_err_sighdl(_("\nImportant operation in progress: "
117 "interrupt again to forcefully abort\n"));
118 safe_signal(SIGINT, &__maildircatch);
121 static unsigned
122 a_maildir_hash(char const *cp) /* TODO obsolete that -> n_torek_hash */
124 unsigned h = 0, g;
125 NYD_ENTER;
127 cp--;
128 while (*++cp) {
129 h = (h << 4 & 0xffffffff) + (*cp&0377);
130 if ((g = h & 0xf0000000) != 0) {
131 h = h ^ g >> 24;
132 h = h ^ g;
135 NYD_LEAVE;
136 return h;
139 static void
140 _cleantmp(void)
142 char dep[PATH_MAX];
143 struct stat st;
144 time_t now;
145 DIR *dirp;
146 struct dirent *dp;
147 NYD_ENTER;
149 if ((dirp = opendir("tmp")) == NULL)
150 goto jleave;
152 now = n_time_epoch();
153 while ((dp = readdir(dirp)) != NULL) {
154 if (dp->d_name[0] == '.')
155 continue;
156 sstpcpy(sstpcpy(dep, "tmp/"), dp->d_name);
157 if (stat(dep, &st) == -1)
158 continue;
159 if (st.st_atime + 36*3600 < now)
160 unlink(dep);
162 closedir(dirp);
163 jleave:
164 NYD_LEAVE;
167 static int
168 _maildir_setfile1(char const *name, enum fedit_mode fm, int omsgCount)
170 int i;
171 NYD_ENTER;
173 if (!(fm & FEDIT_NEWMAIL))
174 _cleantmp();
176 mb.mb_perm = ((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY))
177 ? 0 : MB_DELE;
178 if ((i = _maildir_subdir(name, "cur", fm)) != 0)
179 goto jleave;
180 if ((i = _maildir_subdir(name, "new", fm)) != 0)
181 goto jleave;
182 _maildir_append(name, NULL, NULL);
184 srelax_hold();
185 for (i = ((fm & FEDIT_NEWMAIL) ? omsgCount : 0); i < msgCount; ++i) {
186 readin(name, message + i);
187 srelax();
189 srelax_rele();
191 if (fm & FEDIT_NEWMAIL) {
192 if (msgCount > omsgCount)
193 qsort(&message[omsgCount], msgCount - omsgCount, sizeof *message,
194 &mdcmp);
195 } else if (msgCount)
196 qsort(message, msgCount, sizeof *message, &mdcmp);
197 i = msgCount;
198 jleave:
199 NYD_LEAVE;
200 return i;
203 static int
204 mdcmp(void const *a, void const *b)
206 struct message const *mpa = a, *mpb = b;
207 long i;
208 NYD_ENTER;
210 if ((i = mpa->m_time - mpb->m_time) == 0)
211 i = strcmp(mpa->m_maildir_file + 4, mpb->m_maildir_file + 4);
212 NYD_LEAVE;
213 return i;
216 static int
217 _maildir_subdir(char const *name, char const *sub, enum fedit_mode fm)
219 DIR *dirp;
220 struct dirent *dp;
221 int rv;
222 NYD_ENTER;
224 if ((dirp = opendir(sub)) == NULL) {
225 n_err(_("Cannot open directory %s\n"),
226 n_shexp_quote_cp(savecatsep(name, '/', sub), FAL0));
227 rv = -1;
228 goto jleave;
230 if (access(sub, W_OK) == -1)
231 mb.mb_perm = 0;
232 while ((dp = readdir(dirp)) != NULL) {
233 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
234 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
235 continue;
236 if (dp->d_name[0] == '.')
237 continue;
238 if (!(fm & FEDIT_NEWMAIL) || mdlook(dp->d_name, NULL) == NULL)
239 _maildir_append(name, sub, dp->d_name);
241 closedir(dirp);
242 rv = 0;
243 jleave:
244 NYD_LEAVE;
245 return rv;
248 static void
249 _maildir_append(char const *name, char const *sub, char const *fn)
251 struct message *m;
252 size_t sz, i;
253 time_t t = 0;
254 enum mflag f = MUSED | MNOFROM | MNEWEST;
255 char const *cp, *xp;
256 NYD_ENTER;
257 n_UNUSED(name);
259 if (fn != NULL && sub != NULL) {
260 if (!strcmp(sub, "new"))
261 f |= MNEW;
263 /* C99 */{
264 si64_t tib;
266 (void)/*TODO*/n_idec_si64_cp(&tib, fn, 10, &xp);
267 t = (time_t)tib;
270 if ((cp = strrchr(xp, ',')) != NULL && PTRCMP(cp, >, xp + 2) &&
271 cp[-1] == '2' && cp[-2] == ':') {
272 while (*++cp != '\0') {
273 switch (*cp) {
274 case 'F':
275 f |= MFLAGGED;
276 break;
277 case 'R':
278 f |= MANSWERED;
279 break;
280 case 'S':
281 f |= MREAD;
282 break;
283 case 'T':
284 f |= MDELETED;
285 break;
286 case 'D':
287 f |= MDRAFT;
288 break;
294 /* Ensure room (and a NULLified last entry) */
295 ++msgCount;
296 message_append(NULL);
297 --msgCount;
299 if (fn == NULL || sub == NULL)
300 goto jleave;
302 m = message + msgCount++;
303 i = strlen(fn);
304 m->m_maildir_file = smalloc((sz = strlen(sub)) + i + 1 +1);
305 memcpy(m->m_maildir_file, sub, sz);
306 m->m_maildir_file[sz] = '/';
307 memcpy(m->m_maildir_file + sz + 1, fn, i +1);
308 m->m_time = t;
309 m->m_flag = f;
310 m->m_maildir_hash = ~a_maildir_hash(fn);
311 jleave:
312 NYD_LEAVE;
313 return;
316 static void
317 readin(char const *name, struct message *m)
319 char *buf;
320 size_t bufsize, buflen, cnt;
321 long size = 0, lines = 0;
322 off_t offset;
323 FILE *fp;
324 int emptyline = 0;
325 NYD_ENTER;
327 if ((fp = Fopen(m->m_maildir_file, "r")) == NULL) {
328 n_err(_("Cannot read %s for message %lu\n"),
329 n_shexp_quote_cp(savecatsep(name, '/', m->m_maildir_file), FAL0),
330 (ul_i)PTR2SIZE(m - message + 1));
331 m->m_flag |= MHIDDEN;
332 goto jleave;
335 cnt = fsize(fp);
336 fseek(mb.mb_otf, 0L, SEEK_END);
337 offset = ftell(mb.mb_otf);
338 buf = smalloc(bufsize = LINESIZE);
339 buflen = 0;
340 while (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1) != NULL) {
341 /* Since we simply copy over data without doing any transfer
342 * encoding reclassification/adjustment we *have* to perform
343 * RFC 4155 compliant From_ quoting here */
344 if (emptyline && is_head(buf, buflen, FAL0)) {
345 putc('>', mb.mb_otf);
346 ++size;
348 size += fwrite(buf, 1, buflen, mb.mb_otf);/*XXX err hdling*/
349 emptyline = (*buf == '\n');
350 ++lines;
352 free(buf);
353 if (!emptyline) {
354 putc('\n', mb.mb_otf);
355 ++lines;
356 ++size;
359 Fclose(fp);
360 fflush(mb.mb_otf);
361 m->m_size = m->m_xsize = size;
362 m->m_lines = m->m_xlines = lines;
363 m->m_block = mailx_blockof(offset);
364 m->m_offset = mailx_offsetof(offset);
365 substdate(m);
366 jleave:
367 NYD_LEAVE;
370 static void
371 maildir_update(void)
373 struct message *m;
374 int dodel, c, gotcha = 0, held = 0, modflags = 0;
375 NYD_ENTER;
377 if (mb.mb_perm == 0)
378 goto jfree;
380 if (!(n_pstate & n_PS_EDIT)) {
381 holdbits();
382 for (m = message, c = 0; PTRCMP(m, <, message + msgCount); ++m) {
383 if (m->m_flag & MBOX)
384 c++;
386 if (c > 0)
387 if (makembox() == STOP)
388 goto jbypass;
390 for (m = message, gotcha = 0, held = 0; PTRCMP(m, <, message + msgCount);
391 ++m) {
392 if (n_pstate & n_PS_EDIT)
393 dodel = m->m_flag & MDELETED;
394 else
395 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
396 if (dodel) {
397 if (unlink(m->m_maildir_file) < 0)
398 n_err(_("Cannot delete file %s for message %lu\n"),
399 n_shexp_quote_cp(savecatsep(mailname, '/', m->m_maildir_file),
400 FAL0), (ul_i)PTR2SIZE(m - message + 1));
401 else
402 ++gotcha;
403 } else {
404 if ((m->m_flag & (MREAD | MSTATUS)) == (MREAD | MSTATUS) ||
405 (m->m_flag & (MNEW | MBOXED | MSAVED | MSTATUS | MFLAG |
406 MUNFLAG | MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))) {
407 _maildir_move(m);
408 ++modflags;
410 ++held;
413 jbypass:
414 if ((gotcha || modflags) && (n_pstate & n_PS_EDIT)) {
415 fprintf(n_stdout, "%s %s\n",
416 n_shexp_quote_cp(displayname, FAL0),
417 ((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
418 ? _("complete") : _("updated.")));
419 } else if (held && !(n_pstate & n_PS_EDIT) && mb.mb_perm != 0) {
420 if (held == 1)
421 fprintf(n_stdout, _("Held 1 message in %s\n"), displayname);
422 else
423 fprintf(n_stdout, _("Held %d messages in %s\n"), held, displayname);
425 fflush(n_stdout);
426 jfree:
427 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
428 free(m->m_maildir_file);
429 NYD_LEAVE;
432 static void
433 _maildir_move(struct message *m)
435 char *fn, *new;
436 NYD_ENTER;
438 fn = mkname(0, m->m_flag, m->m_maildir_file + 4);
439 new = savecat("cur/", fn);
440 if (!strcmp(m->m_maildir_file, new))
441 goto jleave;
442 if (link(m->m_maildir_file, new) == -1) {
443 n_err(_("Cannot link %s to %s: message %lu not touched\n"),
444 n_shexp_quote_cp(savecatsep(mailname, '/', m->m_maildir_file), FAL0),
445 n_shexp_quote_cp(savecatsep(mailname, '/', new), FAL0),
446 (ul_i)PTR2SIZE(m - message + 1));
447 goto jleave;
449 if (unlink(m->m_maildir_file) == -1)
450 n_err(_("Cannot unlink %s\n"),
451 n_shexp_quote_cp(savecatsep(mailname, '/', m->m_maildir_file), FAL0));
452 jleave:
453 NYD_LEAVE;
456 static char *
457 mkname(time_t t, enum mflag f, char const *pref)
459 static unsigned long cnt;
460 static pid_t mypid; /* XXX This should possibly be global, somehow */
461 static char *node;
463 char *cp;
464 int size, n, i;
465 NYD_ENTER;
467 if (pref == NULL) {
468 if (mypid == 0)
469 mypid = getpid();
470 if (node == NULL) {
471 cp = n_nodename(FAL0);
472 n = size = 0;
473 do {
474 if (UICMP(32, n, <, size + 8))
475 node = srealloc(node, size += 20);
476 switch (*cp) {
477 case '/':
478 node[n++] = '\\', node[n++] = '0',
479 node[n++] = '5', node[n++] = '7';
480 break;
481 case ':':
482 node[n++] = '\\', node[n++] = '0',
483 node[n++] = '7', node[n++] = '2';
484 break;
485 default:
486 node[n++] = *cp;
488 } while (*cp++ != '\0');
490 size = 60 + strlen(node);
491 cp = salloc(size);
492 n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
493 (ul_i)t, (ul_i)mypid, ++cnt, node);
494 } else {
495 size = (n = strlen(pref)) + 13;
496 cp = salloc(size);
497 memcpy(cp, pref, n +1);
498 for (i = n; i > 3; --i)
499 if (cp[i - 1] == ',' && cp[i - 2] == '2' && cp[i - 3] == ':') {
500 n = i;
501 break;
503 if (i <= 3) {
504 memcpy(cp + n, ":2,", 3 +1);
505 n += 3;
508 if (n < size - 7) {
509 if (f & MDRAFTED)
510 cp[n++] = 'D';
511 if (f & MFLAGGED)
512 cp[n++] = 'F';
513 if (f & MANSWERED)
514 cp[n++] = 'R';
515 if (f & MREAD)
516 cp[n++] = 'S';
517 if (f & MDELETED)
518 cp[n++] = 'T';
519 cp[n] = '\0';
521 NYD_LEAVE;
522 return cp;
525 static enum okay
526 maildir_append1(char const *name, FILE *fp, off_t off1, long size,
527 enum mflag flag)
529 char buf[4096], *fn, *tfn, *nfn;
530 struct stat st;
531 FILE *op;
532 time_t now;
533 size_t nlen, flen, n;
534 enum okay rv = STOP;
535 NYD_ENTER;
537 nlen = strlen(name);
539 /* Create a unique temporary file */
540 for (nfn = (char*)0xA /* XXX no magic */;; n_msleep(500, FAL0)) {
541 now = n_time_epoch();
542 flen = strlen(fn = mkname(now, flag, NULL));
543 tfn = salloc(n = nlen + flen + 6);
544 snprintf(tfn, n, "%s/tmp/%s", name, fn);
546 /* Use "wx" for O_EXCL XXX stat(2) rather redundant; coverity:TOCTOU */
547 if ((!stat(tfn, &st) || n_err_no == n_ERR_NOENT) &&
548 (op = Fopen(tfn, "wx")) != NULL)
549 break;
551 nfn = (char*)(PTR2SIZE(nfn) - 1);
552 if (nfn == NULL) {
553 n_err(_("Can't create an unique file name in %s\n"),
554 n_shexp_quote_cp(savecat(name, "/tmp"), FAL0));
555 goto jleave;
559 if (fseek(fp, off1, SEEK_SET) == -1)
560 goto jtmperr;
561 while (size > 0) {
562 size_t z = UICMP(z, size, >, sizeof buf) ? sizeof buf : (size_t)size;
564 if (z != (n = fread(buf, 1, z, fp)) || n != fwrite(buf, 1, n, op)) {
565 jtmperr:
566 n_err(_("Error writing to %s\n"), n_shexp_quote_cp(tfn, FAL0));
567 Fclose(op);
568 goto jerr;
570 size -= n;
572 Fclose(op);
574 nfn = salloc(n = nlen + flen + 6);
575 snprintf(nfn, n, "%s/new/%s", name, fn);
576 if (link(tfn, nfn) == -1) {
577 n_err(_("Cannot link %s to %s\n"), n_shexp_quote_cp(tfn, FAL0),
578 n_shexp_quote_cp(nfn, FAL0));
579 goto jerr;
581 rv = OKAY;
582 jerr:
583 if (unlink(tfn) == -1)
584 n_err(_("Cannot unlink %s\n"), n_shexp_quote_cp(tfn, FAL0));
585 jleave:
586 NYD_LEAVE;
587 return rv;
590 static enum okay
591 trycreate(char const *name)
593 struct stat st;
594 enum okay rv = STOP;
595 NYD_ENTER;
597 if (!stat(name, &st)) {
598 if (!S_ISDIR(st.st_mode)) {
599 n_err(_("%s is not a directory\n"), n_shexp_quote_cp(name, FAL0));
600 goto jleave;
602 } else if (!n_path_mkdir(name)) {
603 n_err(_("Cannot create directory %s\n"), n_shexp_quote_cp(name, FAL0));
604 goto jleave;
606 rv = OKAY;
607 jleave:
608 NYD_LEAVE;
609 return rv;
612 static enum okay
613 mkmaildir(char const *name) /* TODO proper cleanup on error; use path[] loop */
615 char *np;
616 size_t sz;
617 enum okay rv = STOP;
618 NYD_ENTER;
620 if (trycreate(name) == OKAY) {
621 np = ac_alloc((sz = strlen(name)) + 4 +1);
622 memcpy(np, name, sz);
623 memcpy(np + sz, "/tmp", 4 +1);
624 if (trycreate(np) == OKAY) {
625 memcpy(np + sz, "/new", 4 +1);
626 if (trycreate(np) == OKAY) {
627 memcpy(np + sz, "/cur", 4 +1);
628 rv = trycreate(np);
631 ac_free(np);
633 NYD_LEAVE;
634 return rv;
637 static struct message *
638 mdlook(char const *name, struct message *data)
640 struct mditem *md;
641 ui32_t c, h, n = 0;
642 NYD_ENTER;
644 if (data && data->m_maildir_hash)
645 h = ~data->m_maildir_hash;
646 else
647 h = a_maildir_hash(name);
648 h %= _maildir_prime;
649 c = h;
650 md = _maildir_table + c;
652 while (md->md_data != NULL) {
653 if (!strcmp(md->md_data->m_maildir_file + 4, name))
654 break;
655 c += (n & 1) ? -((n+1)/2) * ((n+1)/2) : ((n+1)/2) * ((n+1)/2);
656 n++;
657 while (c >= _maildir_prime)
658 c -= _maildir_prime;
659 md = _maildir_table + c;
661 if (data != NULL && md->md_data == NULL)
662 md->md_data = data;
663 NYD_LEAVE;
664 return md->md_data;
667 static void
668 mktable(void)
670 struct message *mp;
671 size_t i;
672 NYD_ENTER;
674 _maildir_prime = n_prime_next(msgCount);
675 _maildir_table = scalloc(_maildir_prime, sizeof *_maildir_table);
676 for (mp = message, i = msgCount; i-- != 0; ++mp)
677 mdlook(mp->m_maildir_file + 4, mp);
678 NYD_LEAVE;
681 static enum okay
682 subdir_remove(char const *name, char const *sub)
684 char *path;
685 int pathsize, pathend, namelen, sublen, n;
686 DIR *dirp;
687 struct dirent *dp;
688 enum okay rv = STOP;
689 NYD_ENTER;
691 namelen = strlen(name);
692 sublen = strlen(sub);
693 path = smalloc(pathsize = namelen + sublen + 30 +1);
694 memcpy(path, name, namelen);
695 path[namelen] = '/';
696 memcpy(path + namelen + 1, sub, sublen);
697 path[namelen + sublen + 1] = '/';
698 path[pathend = namelen + sublen + 2] = '\0';
700 if ((dirp = opendir(path)) == NULL) {
701 n_perr(path, 0);
702 goto jleave;
704 while ((dp = readdir(dirp)) != NULL) {
705 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
706 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
707 continue;
708 if (dp->d_name[0] == '.')
709 continue;
710 n = strlen(dp->d_name);
711 if (UICMP(32, pathend + n +1, >, pathsize))
712 path = srealloc(path, pathsize = pathend + n + 30);
713 memcpy(path + pathend, dp->d_name, n +1);
714 if (unlink(path) == -1) {
715 n_perr(path, 0);
716 closedir(dirp);
717 goto jleave;
720 closedir(dirp);
722 path[pathend] = '\0';
723 if (rmdir(path) == -1) {
724 n_perr(path, 0);
725 goto jleave;
727 rv = OKAY;
728 jleave:
729 free(path);
730 NYD_LEAVE;
731 return rv;
734 FL int
735 maildir_setfile(char const * volatile name, enum fedit_mode fm)
737 sighandler_type volatile saveint;
738 struct cw cw;
739 int omsgCount;
740 int volatile i = -1;
741 NYD_ENTER;
743 omsgCount = msgCount;
744 if (cwget(&cw) == STOP) {
745 n_alert(_("Cannot open current directory"));
746 goto jleave;
749 if (!(fm & FEDIT_NEWMAIL) && !quit(FAL0))
750 goto jleave;
752 saveint = safe_signal(SIGINT, SIG_IGN);
754 if (!(fm & FEDIT_NEWMAIL)) {
755 if (fm & FEDIT_SYSBOX)
756 n_pstate &= ~n_PS_EDIT;
757 else
758 n_pstate |= n_PS_EDIT;
759 if (mb.mb_itf) {
760 fclose(mb.mb_itf);
761 mb.mb_itf = NULL;
763 if (mb.mb_otf) {
764 fclose(mb.mb_otf);
765 mb.mb_otf = NULL;
767 initbox(name);
768 mb.mb_type = MB_MAILDIR;
771 if (chdir(name) < 0) {
772 n_err(_("Cannot change directory to %s\n"), n_shexp_quote_cp(name, FAL0));
773 mb.mb_type = MB_VOID;
774 *mailname = '\0';
775 msgCount = 0;
776 cwrelse(&cw);
777 safe_signal(SIGINT, saveint);
778 goto jleave;
781 _maildir_table = NULL;
782 if (sigsetjmp(_maildir_jmp, 1) == 0) {
783 if (fm & FEDIT_NEWMAIL)
784 mktable();
785 if (saveint != SIG_IGN)
786 safe_signal(SIGINT, &__maildircatch);
787 i = _maildir_setfile1(name, fm, omsgCount);
789 if ((fm & FEDIT_NEWMAIL) && _maildir_table != NULL)
790 free(_maildir_table);
792 safe_signal(SIGINT, saveint);
794 if (i < 0) {
795 mb.mb_type = MB_VOID;
796 *mailname = '\0';
797 msgCount = 0;
800 if (cwret(&cw) == STOP)
801 n_panic(_("Cannot change back to current directory"));
802 cwrelse(&cw);
804 setmsize(msgCount);
805 if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted && msgCount > omsgCount) {
806 mb.mb_threaded = 0;
807 c_sort((void*)-1);
810 if (!(fm & FEDIT_NEWMAIL)) {
811 n_pstate &= ~n_PS_SAW_COMMAND;
812 n_pstate |= n_PS_SETFILE_OPENED;
815 if ((n_poption & n_PO_EXISTONLY) && !(n_poption & n_PO_HEADERLIST)) {
816 i = (msgCount == 0);
817 goto jleave;
820 if (!(fm & FEDIT_NEWMAIL) && (fm & FEDIT_SYSBOX) && msgCount == 0) {
821 if (mb.mb_type == MB_MAILDIR /* XXX ?? */ && !ok_blook(emptystart))
822 n_err(_("No mail at %s\n"), n_shexp_quote_cp(name, FAL0));
823 i = 1;
824 goto jleave;
827 if ((fm & FEDIT_NEWMAIL) && msgCount > omsgCount)
828 newmailinfo(omsgCount);
829 i = 0;
830 jleave:
831 NYD_LEAVE;
832 return i;
835 FL bool_t
836 maildir_quit(bool_t hold_sigs_on)
838 sighandler_type saveint;
839 struct cw cw;
840 bool_t rv;
841 NYD_ENTER;
843 if(hold_sigs_on)
844 rele_sigs();
846 rv = FAL0;
848 if (cwget(&cw) == STOP) {
849 n_alert(_("Cannot open current directory"));
850 goto jleave;
853 saveint = safe_signal(SIGINT, SIG_IGN);
855 if (chdir(mailname) == -1) {
856 n_err(_("Cannot change directory to %s\n"),
857 n_shexp_quote_cp(mailname, FAL0));
858 cwrelse(&cw);
859 safe_signal(SIGINT, saveint);
860 goto jleave;
863 if (sigsetjmp(_maildir_jmp, 1) == 0) {
864 if (saveint != SIG_IGN)
865 safe_signal(SIGINT, &__maildircatch_hold);
866 maildir_update();
869 safe_signal(SIGINT, saveint);
871 if (cwret(&cw) == STOP)
872 n_panic(_("Cannot change back to current directory"));
873 cwrelse(&cw);
874 rv = TRU1;
875 jleave:
876 if(hold_sigs_on)
877 hold_sigs();
878 NYD_LEAVE;
879 return rv;
882 FL enum okay
883 maildir_append(char const *name, FILE *fp, long offset)
885 char *buf, *bp, *lp;
886 size_t bufsize, buflen, cnt;
887 off_t off1 = -1, offs;
888 long size;
889 int flag;
890 enum {_NONE = 0, _INHEAD = 1<<0, _NLSEP = 1<<1} state;
891 enum okay rv;
892 NYD_ENTER;
894 if ((rv = mkmaildir(name)) != OKAY)
895 goto jleave;
897 buf = smalloc(bufsize = LINESIZE); /* TODO line pool; signals */
898 buflen = 0;
899 cnt = fsize(fp);
900 offs = offset /* BSD will move due to O_APPEND! ftell(fp) */;
901 size = 0;
903 srelax_hold();
904 for (flag = MNEW, state = _NLSEP;;) {
905 bp = fgetline(&buf, &bufsize, &cnt, &buflen, fp, 1);
907 if (bp == NULL ||
908 ((state & (_INHEAD | _NLSEP)) == _NLSEP &&
909 is_head(buf, buflen, FAL0))) {
910 if (off1 != (off_t)-1) {
911 if ((rv = maildir_append1(name, fp, off1, size, flag)) == STOP)
912 goto jfree;
913 srelax();
914 if (fseek(fp, offs + buflen, SEEK_SET) == -1) {
915 rv = STOP;
916 goto jfree;
919 off1 = offs + buflen;
920 size = 0;
921 state = _INHEAD;
922 flag = MNEW;
924 if (bp == NULL)
925 break;
926 } else
927 size += buflen;
928 offs += buflen;
930 state &= ~_NLSEP;
931 if (buf[0] == '\n') {
932 state &= ~_INHEAD;
933 state |= _NLSEP;
934 } else if (state & _INHEAD) {
935 if (!ascncasecmp(buf, "status", 6)) {
936 lp = buf + 6;
937 while (whitechar(*lp))
938 ++lp;
939 if (*lp == ':')
940 while (*++lp != '\0')
941 switch (*lp) {
942 case 'R':
943 flag |= MREAD;
944 break;
945 case 'O':
946 flag &= ~MNEW;
947 break;
949 } else if (!ascncasecmp(buf, "x-status", 8)) {
950 lp = buf + 8;
951 while (whitechar(*lp))
952 ++lp;
953 if (*lp == ':') {
954 while (*++lp != '\0')
955 switch (*lp) {
956 case 'F':
957 flag |= MFLAGGED;
958 break;
959 case 'A':
960 flag |= MANSWERED;
961 break;
962 case 'T':
963 flag |= MDRAFTED;
964 break;
970 assert(rv == OKAY);
971 jfree:
972 srelax_rele();
973 free(buf);
974 jleave:
975 NYD_LEAVE;
976 return rv;
979 FL enum okay
980 maildir_remove(char const *name)
982 enum okay rv = STOP;
983 NYD_ENTER;
985 if (subdir_remove(name, "tmp") == STOP ||
986 subdir_remove(name, "new") == STOP ||
987 subdir_remove(name, "cur") == STOP)
988 goto jleave;
989 if (rmdir(name) == -1) {
990 n_perr(name, 0);
991 goto jleave;
993 rv = OKAY;
994 jleave:
995 NYD_LEAVE;
996 return rv;
999 /* s-it-mode */