a_folder_info(): print "+[FOLDER]rest" if under folder (as applicable)
[s-mailx.git] / folder.c
blobec28447a70a47ef54a7bafc98fe50b878062b0cc
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Folder (mailbox) initialization, newmail announcement and related.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE folder
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <pwd.h>
44 /* Update mailname (if name != NULL) and displayname, return whether displayname
45 * was large enough to swallow mailname */
46 static bool_t _update_mailname(char const *name);
47 #ifdef HAVE_C90AMEND1 /* TODO unite __narrow_suffix() into one fun! */
48 SINLINE size_t __narrow_suffix(char const *cp, size_t cpl, size_t maxl);
49 #endif
51 /**/
52 static void a_folder_info(void);
54 static bool_t
55 _update_mailname(char const *name) /* TODO 2MUCH work, cache, prop of Object! */
57 char const *foldp;
58 char *mailp, *dispp;
59 size_t i, j, foldlen;
60 bool_t rv;
61 NYD_ENTER;
63 /* Don't realpath(3) if it's only an update request */
64 if (name != NULL) {
65 #ifdef HAVE_REALPATH
66 enum protocol p = which_protocol(name, TRU1, TRU1, &name);
68 if (p == PROTO_FILE || p == PROTO_MAILDIR) {
69 if (realpath(name, mailname) == NULL && n_err_no != n_ERR_NOENT) {
70 n_err(_("Can't canonicalize %s\n"), n_shexp_quote_cp(name, FAL0));
71 rv = FAL0;
72 goto jdocopy;
74 } else
75 jdocopy:
76 #endif
77 n_strscpy(mailname, name, sizeof(mailname));
80 mailp = mailname;
81 dispp = displayname;
83 /* Don't display an absolute path but "+FOLDER" if under *folder* */
84 if(*(foldp = n_folder_query()) != '\0'){
85 foldlen = strlen(foldp);
86 if(strncmp(foldp, mailp, foldlen))
87 foldlen = 0;
88 }else
89 foldlen = 0;
91 /* We want to see the name of the folder .. on the screen */
92 i = strlen(mailp);
93 if(i < sizeof(displayname) - 3 -1){
94 if(foldlen > 0){
95 *dispp++ = '+';
96 *dispp++ = '[';
97 memcpy(dispp, mailp, foldlen);
98 dispp += foldlen;
99 mailp += foldlen;
100 *dispp++ = ']';
101 memcpy(dispp, mailp, i -= foldlen);
102 dispp[i] = '\0';
103 }else
104 memcpy(dispp, mailp, i +1);
105 rv = TRU1;
106 }else{
107 /* Avoid disrupting multibyte sequences (if possible) */
108 #ifndef HAVE_C90AMEND1
109 j = sizeof(displayname) / 3 - 3;
110 i -= sizeof(displayname) - (1/* + */ + 3) - j;
111 #else
112 j = field_detect_clip(sizeof(displayname) / 3, mailp, i);
113 i = j + __narrow_suffix(mailp + j, i - j,
114 sizeof(displayname) - (1/* + */ + 3 + 1) - j);
115 #endif
116 snprintf(dispp, sizeof(displayname), "%s%.*s...%s",
117 (foldlen > 0 ? "[+]" : ""), (int)j, mailp, mailp + i);
118 rv = FAL0;
121 n_PS_ROOT_BLOCK((ok_vset(mailbox_resolved, mailname),
122 ok_vset(mailbox_display, displayname)));
123 NYD_LEAVE;
124 return rv;
127 #ifdef HAVE_C90AMEND1
128 SINLINE size_t
129 __narrow_suffix(char const *cp, size_t cpl, size_t maxl)
131 int err;
132 size_t i, ok;
133 NYD_ENTER;
135 for (err = ok = i = 0; cpl > maxl || err;) {
136 int ml = mblen(cp, cpl);
137 if (ml < 0) { /* XXX _narrow_suffix(): mblen() error; action? */
138 (void)mblen(NULL, 0);
139 err = 1;
140 ml = 1;
141 } else {
142 if (!err)
143 ok = i;
144 err = 0;
145 if (ml == 0)
146 break;
148 cp += ml;
149 i += ml;
150 cpl -= ml;
152 NYD_LEAVE;
153 return ok;
155 #endif /* HAVE_C90AMEND1 */
157 static void
158 a_folder_info(void){
159 struct message *mp;
160 int u, n, d, s, hidden, moved;
161 NYD2_ENTER;
163 if(mb.mb_type == MB_VOID){
164 fprintf(n_stdout, _("(Currently no active mailbox)"));
165 goto jleave;
168 s = d = hidden = moved = 0;
169 for (mp = message, n = 0, u = 0; PTRCMP(mp, <, message + msgCount); ++mp) {
170 if (mp->m_flag & MNEW)
171 ++n;
172 if ((mp->m_flag & MREAD) == 0)
173 ++u;
174 if ((mp->m_flag & (MDELETED | MSAVED)) == (MDELETED | MSAVED))
175 ++moved;
176 if ((mp->m_flag & (MDELETED | MSAVED)) == MDELETED)
177 ++d;
178 if ((mp->m_flag & (MDELETED | MSAVED)) == MSAVED)
179 ++s;
180 if (mp->m_flag & MHIDDEN)
181 ++hidden;
184 /* If displayname gets truncated the user effectively has no option to see
185 * the full pathname of the mailbox, so print it at least for '? fi' */
186 fprintf(n_stdout, "%s: ", n_shexp_quote_cp(
187 (_update_mailname(NULL) ? displayname : mailname), FAL0));
188 if (msgCount == 1)
189 fprintf(n_stdout, _("1 message"));
190 else
191 fprintf(n_stdout, _("%d messages"), msgCount);
192 if (n > 0)
193 fprintf(n_stdout, _(" %d new"), n);
194 if (u-n > 0)
195 fprintf(n_stdout, _(" %d unread"), u);
196 if (d > 0)
197 fprintf(n_stdout, _(" %d deleted"), d);
198 if (s > 0)
199 fprintf(n_stdout, _(" %d saved"), s);
200 if (moved > 0)
201 fprintf(n_stdout, _(" %d moved"), moved);
202 if (hidden > 0)
203 fprintf(n_stdout, _(" %d hidden"), hidden);
204 if (mb.mb_perm == 0)
205 fprintf(n_stdout, _(" [Read-only]"));
207 jleave:
208 putc('\n', n_stdout);
209 NYD2_LEAVE;
212 FL int
213 setfile(char const *name, enum fedit_mode fm) /* TODO oh my god */
215 /* TODO This all needs to be converted to URL:: and Mailbox:: */
216 static int shudclob;
218 struct stat stb;
219 size_t offset;
220 char const *who, *orig_name;
221 int rv, omsgCount = 0;
222 FILE *ibuf = NULL, *lckfp = NULL;
223 bool_t isdevnull = FAL0;
224 NYD_ENTER;
226 n_pstate &= ~n_PS_SETFILE_OPENED;
228 /* C99 */{
229 enum fexp_mode fexpm;
231 if((who = shortcut_expand(name)) != NULL){
232 fexpm = FEXP_NSHORTCUT/* XXX | FEXP_NSHELL*/;
233 name = who;
234 }else
235 fexpm = FEXP_FULL/* XXX FEXP_NSHELL*/;
237 if(name[0] == '%'){
238 char const *cp;
240 fm |= FEDIT_SYSBOX; /* TODO fexpand() needs to tell is-valid-user! */
241 if(*(who = &name[1]) == ':')
242 ++who;
243 if((cp = strrchr(who, '/')) != NULL)
244 who = &cp[1];
245 if(*who == '\0')
246 goto jlogname;
247 }else
248 jlogname:
249 who = ok_vlook(LOGNAME);
251 if ((name = fexpand(name, fexpm)) == NULL)
252 goto jem1;
255 /* For at least substdate() users TODO -> eventloop tick */
256 time_current_update(&time_current, FAL0);
258 switch (which_protocol(orig_name = name, TRU1, TRU1, &name)) {
259 case PROTO_FILE:
260 isdevnull = ((n_poption & n_PO_BATCH_FLAG) &&
261 !strcmp(name, n_path_devnull));
262 #ifdef HAVE_REALPATH
263 do { /* TODO we need objects, goes away then */
264 # ifdef HAVE_REALPATH_NULL
265 char *cp;
267 if ((cp = realpath(name, NULL)) != NULL) {
268 name = savestr(cp);
269 (free)(cp);
271 # else
272 char cbuf[PATH_MAX];
274 if (realpath(name, cbuf) != NULL)
275 name = savestr(cbuf);
276 # endif
277 } while (0);
278 #endif
279 rv = 1;
280 break;
281 case PROTO_MAILDIR:
282 shudclob = 1;
283 rv = maildir_setfile(name, fm);
284 goto jleave;
285 #ifdef HAVE_POP3
286 case PROTO_POP3:
287 shudclob = 1;
288 rv = pop3_setfile(orig_name, fm);
289 goto jleave;
290 #endif
291 default:
292 n_err(_("Cannot handle protocol: %s\n"), orig_name);
293 goto jem1;
296 if ((ibuf = n_fopen_any(savecat("file://", name), "r", NULL)) == NULL) {
297 int e = n_err_no;
299 if ((fm & FEDIT_SYSBOX) && e == n_ERR_NOENT) {
300 if (strcmp(who, ok_vlook(LOGNAME)) && getpwnam(who) == NULL) {
301 n_err(_("%s is not a user of this system\n"),
302 n_shexp_quote_cp(who, FAL0));
303 goto jem2;
305 if (!(n_poption & n_PO_QUICKRUN_MASK) && ok_blook(bsdcompat))
306 n_err(_("No mail for %s\n"), who);
308 if (fm & FEDIT_NEWMAIL)
309 goto jleave;
311 mb.mb_type = MB_VOID;
312 if (ok_blook(emptystart)) {
313 if (!(n_poption & n_PO_QUICKRUN_MASK) && !ok_blook(bsdcompat))
314 n_perr(name, e);
315 /* We must avoid returning -1 and causing program exit */
316 rv = 1;
317 goto jleave;
319 n_perr(name, e);
320 goto jem1;
323 if (fstat(fileno(ibuf), &stb) == -1) {
324 if (fm & FEDIT_NEWMAIL)
325 goto jleave;
326 n_perr(_("fstat"), 0);
327 goto jem1;
330 if (S_ISREG(stb.st_mode) || isdevnull) {
331 /* EMPTY */
332 } else {
333 if (fm & FEDIT_NEWMAIL)
334 goto jleave;
335 n_err_no = S_ISDIR(stb.st_mode) ? n_ERR_ISDIR : n_ERR_INVAL;
336 n_perr(name, 0);
337 goto jem1;
340 if (shudclob && !(fm & FEDIT_NEWMAIL) && !quit(FAL0))
341 goto jem2;
343 hold_sigs();
345 #ifdef HAVE_SOCKETS
346 if (!(fm & FEDIT_NEWMAIL) && mb.mb_sock.s_fd >= 0)
347 sclose(&mb.mb_sock); /* TODO sorry? VMAILFS->close(), thank you */
348 #endif
350 /* TODO There is no intermediate VOID box we've switched to: name may
351 * TODO point to the same box that we just have written, so any updates
352 * TODO we won't see! Reopen again in this case. RACY! Goes with VOID! */
353 /* TODO In addition: in case of compressed/hook boxes we lock a temporary! */
354 /* TODO We may uselessly open twice but quit() doesn't say whether we were
355 * TODO modified so we can't tell: Mailbox::is_modified() :-(( */
356 if (/*shudclob && !(fm & FEDIT_NEWMAIL) &&*/ !strcmp(name, mailname)) {
357 name = mailname;
358 Fclose(ibuf);
360 if ((ibuf = n_fopen_any(name, "r", NULL)) == NULL ||
361 fstat(fileno(ibuf), &stb) == -1 ||
362 (!S_ISREG(stb.st_mode) && !isdevnull)) {
363 n_perr(name, 0);
364 rele_sigs();
365 goto jem2;
369 /* Copy the messages into /tmp and set pointers */
370 if (!(fm & FEDIT_NEWMAIL)) {
371 if (isdevnull) {
372 mb.mb_type = MB_VOID;
373 mb.mb_perm = 0;
374 } else {
375 mb.mb_type = MB_FILE;
376 mb.mb_perm = (((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY) ||
377 access(name, W_OK) < 0) ? 0 : MB_DELE | MB_EDIT);
378 if (shudclob) {
379 if (mb.mb_itf) {
380 fclose(mb.mb_itf);
381 mb.mb_itf = NULL;
383 if (mb.mb_otf) {
384 fclose(mb.mb_otf);
385 mb.mb_otf = NULL;
389 shudclob = 1;
390 if (fm & FEDIT_SYSBOX)
391 n_pstate &= ~n_PS_EDIT;
392 else
393 n_pstate |= n_PS_EDIT;
394 initbox(name);
395 offset = 0;
396 } else {
397 fseek(mb.mb_otf, 0L, SEEK_END);
398 /* TODO Doing this without holding a lock is.. And not err checking.. */
399 fseek(ibuf, mailsize, SEEK_SET);
400 offset = mailsize;
401 omsgCount = msgCount;
404 if (isdevnull)
405 lckfp = (FILE*)-1;
406 else if (!(n_pstate & n_PS_EDIT))
407 lckfp = n_dotlock(name, fileno(ibuf), FLT_READ, offset,0,
408 (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX));
409 else if (n_file_lock(fileno(ibuf), FLT_READ, offset,0,
410 (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX)))
411 lckfp = (FILE*)-1;
413 if (lckfp == NULL) {
414 if (!(fm & FEDIT_NEWMAIL)) {
415 char const *emsg = (n_pstate & n_PS_EDIT)
416 ? N_("Unable to lock mailbox, aborting operation")
417 : N_("Unable to (dot) lock mailbox, aborting operation");
418 n_perr(V_(emsg), 0);
420 rele_sigs();
421 if (!(fm & FEDIT_NEWMAIL))
422 goto jem1;
423 goto jleave;
426 mailsize = fsize(ibuf);
428 /* TODO This is too simple minded? We should regenerate an index file
429 * TODO to be able to truly tell whether *anything* has changed! */
430 if ((fm & FEDIT_NEWMAIL) && UICMP(z, mailsize, <=, offset)) {
431 rele_sigs();
432 goto jleave;
434 setptr(ibuf, offset);
435 setmsize(msgCount);
436 if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted) {
437 mb.mb_threaded = 0;
438 c_sort((void*)-1);
441 Fclose(ibuf);
442 ibuf = NULL;
443 if (lckfp != NULL && lckfp != (FILE*)-1) {
444 Pclose(lckfp, FAL0);
445 /*lckfp = NULL;*/
448 if (!(fm & FEDIT_NEWMAIL)) {
449 n_pstate &= ~n_PS_SAW_COMMAND;
450 n_pstate |= n_PS_SETFILE_OPENED;
453 rele_sigs();
455 rv = (msgCount == 0);
456 if(rv)
457 n_err_no = n_ERR_NODATA;
459 if(n_poption & n_PO_EXISTONLY)
460 goto jleave;
462 if(rv){
463 if(!(n_pstate & n_PS_EDIT) || (fm & FEDIT_NEWMAIL)){
464 if(!(fm & FEDIT_NEWMAIL)){
465 if (!ok_blook(emptystart))
466 n_err(_("No mail for %s\n"), who);
468 goto jleave;
472 if(fm & FEDIT_NEWMAIL)
473 newmailinfo(omsgCount);
474 jleave:
475 if (ibuf != NULL) {
476 Fclose(ibuf);
477 if (lckfp != NULL && lckfp != (FILE*)-1)
478 Pclose(lckfp, FAL0);
480 NYD_LEAVE;
481 return rv;
482 jem2:
483 mb.mb_type = MB_VOID;
484 jem1:
485 n_err_no = n_ERR_NOTOBACCO;
486 rv = -1;
487 goto jleave;
490 FL int
491 newmailinfo(int omsgCount)
493 int mdot, i;
494 NYD_ENTER;
496 for (i = 0; i < omsgCount; ++i)
497 message[i].m_flag &= ~MNEWEST;
499 if (msgCount > omsgCount) {
500 for (i = omsgCount; i < msgCount; ++i)
501 message[i].m_flag |= MNEWEST;
502 fprintf(n_stdout, _("New mail has arrived.\n"));
503 if ((i = msgCount - omsgCount) == 1)
504 fprintf(n_stdout, _("Loaded 1 new message.\n"));
505 else
506 fprintf(n_stdout, _("Loaded %d new messages.\n"), i);
507 } else
508 fprintf(n_stdout, _("Loaded %d messages.\n"), msgCount);
510 temporary_folder_hook_check(TRU1);
512 mdot = getmdot(1);
514 if (ok_blook(header))
515 print_headers(omsgCount + 1, msgCount, FAL0);
516 NYD_LEAVE;
517 return mdot;
520 FL void
521 setmsize(int sz)
523 NYD_ENTER;
524 if (n_msgvec != NULL)
525 free(n_msgvec);
526 n_msgvec = scalloc(sz + 1, sizeof *n_msgvec);
527 NYD_LEAVE;
530 FL void
531 print_header_summary(char const *Larg)
533 size_t bot, top, i, j;
534 NYD_ENTER;
536 if (Larg != NULL) {
537 /* Avoid any messages XXX add a make_mua_silent() and use it? */
538 if ((n_poption & (n_PO_VERB | n_PO_EXISTONLY)) == n_PO_EXISTONLY) {
539 n_stdout = freopen(n_path_devnull, "w", stdout);
540 n_stderr = freopen(n_path_devnull, "w", stderr);
542 assert(n_msgvec != NULL);
543 i = (getmsglist(/*TODO make const */n_UNCONST(Larg), n_msgvec, 0) <= 0);
544 if (n_poption & n_PO_EXISTONLY) {
545 n_exit_status = (int)i;
546 goto jleave;
548 if (i)
549 goto jleave;
550 for (bot = msgCount, top = 0, i = 0; (j = n_msgvec[i]) != 0; ++i) {
551 if (bot > j)
552 bot = j;
553 if (top < j)
554 top = j;
556 } else
557 bot = 1, top = msgCount;
558 print_headers(bot, top, (Larg != NULL)); /* TODO should take iterator!! */
559 jleave:
560 NYD_LEAVE;
563 FL void
564 n_folder_announce(enum n_announce_flags af){
565 int vec[2], mdot;
566 NYD_ENTER;
568 mdot = (mb.mb_type == MB_VOID) ? 1 : getmdot(0);
569 dot = &message[mdot - 1];
571 if(af != n_ANNOUNCE_NONE && ok_blook(header) &&
572 ((af & n_ANNOUNCE_MAIN_CALL) ||
573 ((af & n_ANNOUNCE_CHANGE) && !ok_blook(posix))))
574 af |= n_ANNOUNCE_STATUS | n__ANNOUNCE_HEADER;
576 if(af & n_ANNOUNCE_STATUS){
577 a_folder_info();
578 af |= n__ANNOUNCE_ANY;
581 if(af & n__ANNOUNCE_HEADER){
582 if(!(af & n_ANNOUNCE_MAIN_CALL) && ok_blook(bsdannounce))
583 n_OBSOLETE(_("*bsdannounce* is now default behaviour"));
584 vec[0] = mdot;
585 vec[1] = 0;
586 print_header_group(vec); /* XXX errors? */
587 af |= n__ANNOUNCE_ANY;
590 if(af & n__ANNOUNCE_ANY)
591 fflush(n_stdout);
592 NYD_LEAVE;
595 FL int
596 getmdot(int nmail)
598 struct message *mp;
599 char *cp;
600 int mdot;
601 enum mflag avoid = MHIDDEN | MDELETED;
602 NYD_ENTER;
604 if (!nmail) {
605 if (ok_blook(autothread)) {
606 n_OBSOLETE(_("please use *autosort=thread* instead of *autothread*"));
607 c_thread(NULL);
608 } else if ((cp = ok_vlook(autosort)) != NULL) {
609 if (mb.mb_sorted != NULL)
610 free(mb.mb_sorted);
611 mb.mb_sorted = sstrdup(cp);
612 c_sort(NULL);
615 if (mb.mb_type == MB_VOID) {
616 mdot = 1;
617 goto jleave;
620 if (nmail)
621 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
622 if ((mp->m_flag & (MNEWEST | avoid)) == MNEWEST)
623 break;
625 if (!nmail || PTRCMP(mp, >=, message + msgCount)) {
626 if (mb.mb_threaded) {
627 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
628 if ((mp->m_flag & (MNEW | avoid)) == MNEW)
629 break;
630 } else {
631 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
632 if ((mp->m_flag & (MNEW | avoid)) == MNEW)
633 break;
637 if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
638 if (mb.mb_threaded) {
639 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
640 if (mp->m_flag & MFLAGGED)
641 break;
642 } else {
643 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
644 if (mp->m_flag & MFLAGGED)
645 break;
649 if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
650 if (mb.mb_threaded) {
651 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
652 if (!(mp->m_flag & (MREAD | avoid)))
653 break;
654 } else {
655 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
656 if (!(mp->m_flag & (MREAD | avoid)))
657 break;
661 if (nmail &&
662 (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
663 mdot = (int)PTR2SIZE(mp - message + 1);
664 else if (ok_blook(showlast)) {
665 if (mb.mb_threaded) {
666 for (mp = this_in_thread(threadroot, -1); mp;
667 mp = prev_in_thread(mp))
668 if (!(mp->m_flag & avoid))
669 break;
670 mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
671 } else {
672 for (mp = message + msgCount - 1; mp >= message; --mp)
673 if (!(mp->m_flag & avoid))
674 break;
675 mdot = (mp >= message) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
677 } else if (!nmail &&
678 (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
679 mdot = (int)PTR2SIZE(mp - message + 1);
680 else if (mb.mb_threaded) {
681 for (mp = threadroot; mp; mp = next_in_thread(mp))
682 if (!(mp->m_flag & avoid))
683 break;
684 mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : 1;
685 } else {
686 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
687 if (!(mp->m_flag & avoid))
688 break;
689 mdot = PTRCMP(mp, <, message + msgCount)
690 ? (int)PTR2SIZE(mp - message + 1) : 1;
692 jleave:
693 NYD_LEAVE;
694 return mdot;
697 FL void
698 initbox(char const *name)
700 char *tempMesg;
701 NYD_ENTER;
703 if (mb.mb_type != MB_VOID)
704 n_strscpy(prevfile, mailname, PATH_MAX);
706 /* TODO name always NE mailname (but goes away for objects anyway)
707 * TODO Well, not true no more except that in parens */
708 _update_mailname((name != mailname) ? name : NULL);
710 if ((mb.mb_otf = Ftmp(&tempMesg, "tmpmbox", OF_WRONLY | OF_HOLDSIGS)) ==
711 NULL) {
712 n_perr(_("temporary mail message file"), 0);
713 exit(n_EXIT_ERR);
715 if ((mb.mb_itf = safe_fopen(tempMesg, "r", NULL)) == NULL) {
716 n_perr(_("temporary mail message file"), 0);
717 exit(n_EXIT_ERR);
719 Ftmp_release(&tempMesg);
721 message_reset();
722 mb.mb_active = MB_NONE;
723 mb.mb_threaded = 0;
724 if (mb.mb_sorted != NULL) {
725 free(mb.mb_sorted);
726 mb.mb_sorted = NULL;
728 dot = prevdot = threadroot = NULL;
729 n_pstate &= ~n_PS_DID_PRINT_DOT;
730 NYD_LEAVE;
733 FL char const *
734 n_folder_query(void){
735 struct n_string s, *sp = &s;
736 char *cp;
737 char const *rv;
738 bool_t err;
739 NYD_ENTER;
741 sp = n_string_creat_auto(sp);
743 /* *folder* is linked with *folder_resolved*: we only use the latter */
744 for(err = FAL0;;){
745 if((rv = ok_vlook(folder_resolved)) != NULL)
746 break;
748 /* POSIX says:
749 * If directory does not start with a <slash> ('/'), the contents
750 * of HOME shall be prefixed to it.
751 * And:
752 * If folder is unset or set to null, [.] filenames beginning with
753 * '+' shall refer to files in the current directory.
754 * We may have the result already */
755 rv = n_empty;
756 err = FAL0;
758 if((cp = ok_vlook(folder)) == NULL)
759 goto jset;
761 /* Expand the *folder*; skip %: prefix for simplicity of use */
762 if(cp[0] == '%' && cp[1] == ':')
763 cp += 2;
764 if((err = (cp = fexpand(cp, FEXP_NSPECIAL | FEXP_NFOLDER | FEXP_NSHELL)
765 ) == NULL) || *cp == '\0')
766 goto jset;
768 switch(which_protocol(cp, FAL0, FAL0, NULL)){
769 case PROTO_POP3:
770 n_err(_("*folder* can't be set to a flat, read-only POP3 account\n"));
771 err = TRU1;
772 goto jset;
773 default:
774 /* Further expansion desired */
775 break;
778 /* Prefix HOME as necessary */
779 if(*cp != '/'){ /* XXX path_is_absolute() */
780 size_t l1, l2;
781 char const *home;
783 home = ok_vlook(HOME);
784 l1 = strlen(home);
785 l2 = strlen(cp);
787 sp = n_string_reserve(sp, l1 + 1 + l2 +1);
788 sp = n_string_push_buf(sp, home, l1);
789 sp = n_string_push_c(sp, '/');
790 sp = n_string_push_buf(sp, cp, l2);
791 cp = n_string_cp(sp);
792 sp = n_string_drop_ownership(sp);
795 rv = cp;
797 /* TODO Since our visual mailname is resolved via realpath(3) if available
798 * TODO to avoid that we loose track of our currently open folder in case
799 * TODO we chdir away, but still checks the leading path portion against
800 * TODO n_folder_query() to be able to abbreviate to the +FOLDER syntax if
801 * TODO possible, we need to realpath(3) the folder, too */
802 #ifdef HAVE_REALPATH
803 assert(sp->s_len == 0 && sp->s_dat == NULL);
804 # ifndef HAVE_REALPATH_NULL
805 sp = n_string_reserve(sp, PATH_MAX +1);
806 # endif
808 if((sp->s_dat = realpath(cp, sp->s_dat)) != NULL){
809 # ifdef HAVE_REALPATH_NULL
810 n_string_cp(sp = n_string_assign_cp(sp, cp = sp->s_dat));
811 (free)(cp);
812 # endif
813 rv = sp->s_dat;
814 }else if(n_err_no == n_ERR_NOENT)
815 rv = cp;
816 else{
817 n_err(_("Can't canonicalize *folder*: %s\n"),
818 n_shexp_quote_cp(cp, FAL0));
819 err = TRU1;
820 rv = n_empty;
822 sp = n_string_drop_ownership(sp);
823 #endif /* HAVE_REALPATH */
825 /* Always append a solidus to our result path upon success */
826 if(!err){
827 size_t i;
829 if(rv[(i = strlen(rv)) - 1] != '/'){
830 sp = n_string_reserve(sp, i + 1 +1);
831 sp = n_string_push_buf(sp, rv, i);
832 sp = n_string_push_c(sp, '/');
833 rv = n_string_cp(sp);
834 sp = n_string_drop_ownership(sp);
838 jset:
839 n_PS_ROOT_BLOCK(ok_vset(folder_resolved, rv));
842 if(err){
843 n_err(_("*folder* is not resolvable, using CWD\n"));
844 assert(rv != NULL && *rv == '\0');
846 NYD_LEAVE;
847 return rv;
850 FL int
851 n_folder_mbox_prepare_append(FILE *fout, struct stat *st_or_null){
852 /* TODO n_folder_mbox_prepare_append -> Mailbox->append() */
853 struct stat stb;
854 char buf[2];
855 bool_t needsep;
856 int rv;
857 NYD2_ENTER;
859 if(fseek(fout, -2L, SEEK_END)){
860 rv = n_err_no;
862 if(st_or_null == NULL){
863 st_or_null = &stb;
864 if(fstat(fileno(fout), st_or_null))
865 goto jerrno;
868 if(st_or_null->st_size >= 2)
869 goto jleave;
870 if(st_or_null->st_size == 0){
871 rv = n_ERR_NONE;
872 goto jleave;
874 if(fseek(fout, -1L, SEEK_END))
875 goto jerrno;
878 rv = n_ERR_NONE;
880 needsep = FAL0;
881 switch(fread(buf, sizeof *buf, 2, fout)){
882 case 2:
883 if(buf[1] != '\n')
884 needsep = TRU1;
885 break;
886 case 1:
887 if(buf[0] != '\n')
888 needsep = TRU1;
889 break;
890 default:
891 if(ferror(fout))
892 goto jerrno;
893 break;
896 if(fflush(fout) || (needsep && putc('\n', fout) == EOF))
897 jerrno:
898 rv = n_err_no;
899 jleave:
900 NYD2_LEAVE;
901 return rv;
904 /* s-it-mode */