Fix error handling of `shcodec' and `urlcodec'
[s-mailx.git] / folder.c
blob9c86350cc97ee2edf4cfd059c291488d41e260c5
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 *mailp, *dispp;
58 size_t i, j;
59 bool_t rv = TRU1;
60 NYD_ENTER;
62 /* Don't realpath(3) if it's only an update request */
63 if (name != NULL) {
64 #ifdef HAVE_REALPATH
65 enum protocol p = which_protocol(name, TRU1, TRU1, &name);
67 if (p == PROTO_FILE || p == PROTO_MAILDIR) {
68 if (realpath(name, mailname) == NULL && n_err_no != n_ERR_NOENT) {
69 n_err(_("Can't canonicalize %s\n"), n_shexp_quote_cp(name, FAL0));
70 rv = FAL0;
71 goto jdocopy;
73 } else
74 jdocopy:
75 #endif
76 n_strscpy(mailname, name, sizeof(mailname));
79 mailp = mailname;
80 dispp = displayname;
82 /* Don't display an absolute path but "+FOLDER" if under *folder* */
83 /* C99 */{
84 char const *folderp;
86 if(*(folderp = n_folder_query()) != '\0'){
87 i = strlen(folderp);
88 if(!strncmp(folderp, mailp, i)){
89 mailp += i;
90 *dispp++ = '+';
95 /* We want to see the name of the folder .. on the screen */
96 i = strlen(mailp);
97 if (i < sizeof(displayname) -1)
98 memcpy(dispp, mailp, i +1);
99 else {
100 rv = FAL0;
101 /* Avoid disrupting multibyte sequences (if possible) */
102 #ifndef HAVE_C90AMEND1
103 j = sizeof(displayname) / 3 - 1;
104 i -= sizeof(displayname) - (1/* + */ + 3) - j;
105 #else
106 j = field_detect_clip(sizeof(displayname) / 3, mailp, i);
107 i = j + __narrow_suffix(mailp + j, i - j,
108 sizeof(displayname) - (1/* + */ + 3 + 1) - j);
109 #endif
110 snprintf(dispp, sizeof(displayname), "%.*s...%s",
111 (int)j, mailp, mailp + i);
114 n_PS_ROOT_BLOCK((ok_vset(mailbox_resolved, mailname),
115 ok_vset(mailbox_display, displayname)));
116 NYD_LEAVE;
117 return rv;
120 #ifdef HAVE_C90AMEND1
121 SINLINE size_t
122 __narrow_suffix(char const *cp, size_t cpl, size_t maxl)
124 int err;
125 size_t i, ok;
126 NYD_ENTER;
128 for (err = ok = i = 0; cpl > maxl || err;) {
129 int ml = mblen(cp, cpl);
130 if (ml < 0) { /* XXX _narrow_suffix(): mblen() error; action? */
131 (void)mblen(NULL, 0);
132 err = 1;
133 ml = 1;
134 } else {
135 if (!err)
136 ok = i;
137 err = 0;
138 if (ml == 0)
139 break;
141 cp += ml;
142 i += ml;
143 cpl -= ml;
145 NYD_LEAVE;
146 return ok;
148 #endif /* HAVE_C90AMEND1 */
150 static void
151 a_folder_info(void){
152 struct message *mp;
153 int u, n, d, s, hidden, moved;
154 NYD2_ENTER;
156 if(mb.mb_type == MB_VOID){
157 fprintf(n_stdout, _("(Currently no active mailbox)"));
158 goto jleave;
161 s = d = hidden = moved = 0;
162 for (mp = message, n = 0, u = 0; PTRCMP(mp, <, message + msgCount); ++mp) {
163 if (mp->m_flag & MNEW)
164 ++n;
165 if ((mp->m_flag & MREAD) == 0)
166 ++u;
167 if ((mp->m_flag & (MDELETED | MSAVED)) == (MDELETED | MSAVED))
168 ++moved;
169 if ((mp->m_flag & (MDELETED | MSAVED)) == MDELETED)
170 ++d;
171 if ((mp->m_flag & (MDELETED | MSAVED)) == MSAVED)
172 ++s;
173 if (mp->m_flag & MHIDDEN)
174 ++hidden;
177 /* If displayname gets truncated the user effectively has no option to see
178 * the full pathname of the mailbox, so print it at least for '? fi' */
179 fprintf(n_stdout, "%s: ", n_shexp_quote_cp(
180 (_update_mailname(NULL) ? displayname : mailname), FAL0));
181 if (msgCount == 1)
182 fprintf(n_stdout, _("1 message"));
183 else
184 fprintf(n_stdout, _("%d messages"), msgCount);
185 if (n > 0)
186 fprintf(n_stdout, _(" %d new"), n);
187 if (u-n > 0)
188 fprintf(n_stdout, _(" %d unread"), u);
189 if (d > 0)
190 fprintf(n_stdout, _(" %d deleted"), d);
191 if (s > 0)
192 fprintf(n_stdout, _(" %d saved"), s);
193 if (moved > 0)
194 fprintf(n_stdout, _(" %d moved"), moved);
195 if (hidden > 0)
196 fprintf(n_stdout, _(" %d hidden"), hidden);
197 if (mb.mb_perm == 0)
198 fprintf(n_stdout, _(" [Read-only]"));
200 jleave:
201 putc('\n', n_stdout);
202 NYD2_LEAVE;
205 FL int
206 setfile(char const *name, enum fedit_mode fm) /* TODO oh my god */
208 /* TODO This all needs to be converted to URL:: and Mailbox:: */
209 static int shudclob;
211 struct stat stb;
212 size_t offset;
213 char const *who, *orig_name;
214 int rv, omsgCount = 0;
215 FILE *ibuf = NULL, *lckfp = NULL;
216 bool_t isdevnull = FAL0;
217 NYD_ENTER;
219 n_pstate &= ~n_PS_SETFILE_OPENED;
221 /* C99 */{
222 enum fexp_mode fexpm;
224 if((who = shortcut_expand(name)) != NULL){
225 fexpm = FEXP_NSHORTCUT/* XXX | FEXP_NSHELL*/;
226 name = who;
227 }else
228 fexpm = FEXP_FULL/* XXX FEXP_NSHELL*/;
230 if(name[0] == '%'){
231 char const *cp;
233 fm |= FEDIT_SYSBOX; /* TODO fexpand() needs to tell is-valid-user! */
234 if(*(who = &name[1]) == ':')
235 ++who;
236 if((cp = strrchr(who, '/')) != NULL)
237 who = &cp[1];
238 if(*who == '\0')
239 goto jlogname;
240 }else
241 jlogname:
242 who = ok_vlook(LOGNAME);
244 if ((name = fexpand(name, fexpm)) == NULL)
245 goto jem1;
248 /* For at least substdate() users TODO -> eventloop tick */
249 time_current_update(&time_current, FAL0);
251 switch (which_protocol(orig_name = name, TRU1, TRU1, &name)) {
252 case PROTO_FILE:
253 isdevnull = ((n_poption & n_PO_BATCH_FLAG) &&
254 !strcmp(name, n_path_devnull));
255 #ifdef HAVE_REALPATH
256 do { /* TODO we need objects, goes away then */
257 # ifdef HAVE_REALPATH_NULL
258 char *cp;
260 if ((cp = realpath(name, NULL)) != NULL) {
261 name = savestr(cp);
262 (free)(cp);
264 # else
265 char cbuf[PATH_MAX];
267 if (realpath(name, cbuf) != NULL)
268 name = savestr(cbuf);
269 # endif
270 } while (0);
271 #endif
272 rv = 1;
273 break;
274 case PROTO_MAILDIR:
275 shudclob = 1;
276 rv = maildir_setfile(name, fm);
277 goto jleave;
278 #ifdef HAVE_POP3
279 case PROTO_POP3:
280 shudclob = 1;
281 rv = pop3_setfile(orig_name, fm);
282 goto jleave;
283 #endif
284 default:
285 n_err(_("Cannot handle protocol: %s\n"), orig_name);
286 goto jem1;
289 if ((ibuf = n_fopen_any(savecat("file://", name), "r", NULL)) == NULL) {
290 int e = n_err_no;
292 if ((fm & FEDIT_SYSBOX) && e == n_ERR_NOENT) {
293 if (strcmp(who, ok_vlook(LOGNAME)) && getpwnam(who) == NULL) {
294 n_err(_("%s is not a user of this system\n"),
295 n_shexp_quote_cp(who, FAL0));
296 goto jem2;
298 if (!(n_poption & n_PO_QUICKRUN_MASK) && ok_blook(bsdcompat))
299 n_err(_("No mail for %s\n"), who);
301 if (fm & FEDIT_NEWMAIL)
302 goto jleave;
304 mb.mb_type = MB_VOID;
305 if (ok_blook(emptystart)) {
306 if (!(n_poption & n_PO_QUICKRUN_MASK) && !ok_blook(bsdcompat))
307 n_perr(name, e);
308 /* We must avoid returning -1 and causing program exit */
309 rv = 1;
310 goto jleave;
312 n_perr(name, e);
313 goto jem1;
316 if (fstat(fileno(ibuf), &stb) == -1) {
317 if (fm & FEDIT_NEWMAIL)
318 goto jleave;
319 n_perr(_("fstat"), 0);
320 goto jem1;
323 if (S_ISREG(stb.st_mode) || isdevnull) {
324 /* EMPTY */
325 } else {
326 if (fm & FEDIT_NEWMAIL)
327 goto jleave;
328 n_err_no = S_ISDIR(stb.st_mode) ? n_ERR_ISDIR : n_ERR_INVAL;
329 n_perr(name, 0);
330 goto jem1;
333 if (shudclob && !(fm & FEDIT_NEWMAIL) && !quit(FAL0))
334 goto jem2;
336 hold_sigs();
338 #ifdef HAVE_SOCKETS
339 if (!(fm & FEDIT_NEWMAIL) && mb.mb_sock.s_fd >= 0)
340 sclose(&mb.mb_sock); /* TODO sorry? VMAILFS->close(), thank you */
341 #endif
343 /* TODO There is no intermediate VOID box we've switched to: name may
344 * TODO point to the same box that we just have written, so any updates
345 * TODO we won't see! Reopen again in this case. RACY! Goes with VOID! */
346 /* TODO In addition: in case of compressed/hook boxes we lock a temporary! */
347 /* TODO We may uselessly open twice but quit() doesn't say whether we were
348 * TODO modified so we can't tell: Mailbox::is_modified() :-(( */
349 if (/*shudclob && !(fm & FEDIT_NEWMAIL) &&*/ !strcmp(name, mailname)) {
350 name = mailname;
351 Fclose(ibuf);
353 if ((ibuf = n_fopen_any(name, "r", NULL)) == NULL ||
354 fstat(fileno(ibuf), &stb) == -1 ||
355 (!S_ISREG(stb.st_mode) && !isdevnull)) {
356 n_perr(name, 0);
357 rele_sigs();
358 goto jem2;
362 /* Copy the messages into /tmp and set pointers */
363 if (!(fm & FEDIT_NEWMAIL)) {
364 if (isdevnull) {
365 mb.mb_type = MB_VOID;
366 mb.mb_perm = 0;
367 } else {
368 mb.mb_type = MB_FILE;
369 mb.mb_perm = (((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY) ||
370 access(name, W_OK) < 0) ? 0 : MB_DELE | MB_EDIT);
371 if (shudclob) {
372 if (mb.mb_itf) {
373 fclose(mb.mb_itf);
374 mb.mb_itf = NULL;
376 if (mb.mb_otf) {
377 fclose(mb.mb_otf);
378 mb.mb_otf = NULL;
382 shudclob = 1;
383 if (fm & FEDIT_SYSBOX)
384 n_pstate &= ~n_PS_EDIT;
385 else
386 n_pstate |= n_PS_EDIT;
387 initbox(name);
388 offset = 0;
389 } else {
390 fseek(mb.mb_otf, 0L, SEEK_END);
391 /* TODO Doing this without holding a lock is.. And not err checking.. */
392 fseek(ibuf, mailsize, SEEK_SET);
393 offset = mailsize;
394 omsgCount = msgCount;
397 if (isdevnull)
398 lckfp = (FILE*)-1;
399 else if (!(n_pstate & n_PS_EDIT))
400 lckfp = n_dotlock(name, fileno(ibuf), FLT_READ, offset,0,
401 (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX));
402 else if (n_file_lock(fileno(ibuf), FLT_READ, offset,0,
403 (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX)))
404 lckfp = (FILE*)-1;
406 if (lckfp == NULL) {
407 if (!(fm & FEDIT_NEWMAIL)) {
408 char const *emsg = (n_pstate & n_PS_EDIT)
409 ? N_("Unable to lock mailbox, aborting operation")
410 : N_("Unable to (dot) lock mailbox, aborting operation");
411 n_perr(V_(emsg), 0);
413 rele_sigs();
414 if (!(fm & FEDIT_NEWMAIL))
415 goto jem1;
416 goto jleave;
419 mailsize = fsize(ibuf);
421 /* TODO This is too simple minded? We should regenerate an index file
422 * TODO to be able to truly tell whether *anything* has changed! */
423 if ((fm & FEDIT_NEWMAIL) && UICMP(z, mailsize, <=, offset)) {
424 rele_sigs();
425 goto jleave;
427 setptr(ibuf, offset);
428 setmsize(msgCount);
429 if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted) {
430 mb.mb_threaded = 0;
431 c_sort((void*)-1);
434 Fclose(ibuf);
435 ibuf = NULL;
436 if (lckfp != NULL && lckfp != (FILE*)-1) {
437 Pclose(lckfp, FAL0);
438 /*lckfp = NULL;*/
441 if (!(fm & FEDIT_NEWMAIL)) {
442 n_pstate &= ~n_PS_SAW_COMMAND;
443 n_pstate |= n_PS_SETFILE_OPENED;
446 rele_sigs();
448 rv = (msgCount == 0);
449 if(rv)
450 n_err_no = n_ERR_NODATA;
452 if(n_poption & n_PO_EXISTONLY)
453 goto jleave;
455 if(rv){
456 if(!(n_pstate & n_PS_EDIT) || (fm & FEDIT_NEWMAIL)){
457 if(!(fm & FEDIT_NEWMAIL)){
458 if (!ok_blook(emptystart))
459 n_err(_("No mail for %s\n"), who);
461 goto jleave;
465 if(fm & FEDIT_NEWMAIL)
466 newmailinfo(omsgCount);
467 jleave:
468 if (ibuf != NULL) {
469 Fclose(ibuf);
470 if (lckfp != NULL && lckfp != (FILE*)-1)
471 Pclose(lckfp, FAL0);
473 NYD_LEAVE;
474 return rv;
475 jem2:
476 mb.mb_type = MB_VOID;
477 jem1:
478 n_err_no = n_ERR_NOTOBACCO;
479 rv = -1;
480 goto jleave;
483 FL int
484 newmailinfo(int omsgCount)
486 int mdot, i;
487 NYD_ENTER;
489 for (i = 0; i < omsgCount; ++i)
490 message[i].m_flag &= ~MNEWEST;
492 if (msgCount > omsgCount) {
493 for (i = omsgCount; i < msgCount; ++i)
494 message[i].m_flag |= MNEWEST;
495 fprintf(n_stdout, _("New mail has arrived.\n"));
496 if ((i = msgCount - omsgCount) == 1)
497 fprintf(n_stdout, _("Loaded 1 new message.\n"));
498 else
499 fprintf(n_stdout, _("Loaded %d new messages.\n"), i);
500 } else
501 fprintf(n_stdout, _("Loaded %d messages.\n"), msgCount);
503 temporary_folder_hook_check(TRU1);
505 mdot = getmdot(1);
507 if (ok_blook(header))
508 print_headers(omsgCount + 1, msgCount, FAL0);
509 NYD_LEAVE;
510 return mdot;
513 FL void
514 setmsize(int sz)
516 NYD_ENTER;
517 if (n_msgvec != NULL)
518 free(n_msgvec);
519 n_msgvec = scalloc(sz + 1, sizeof *n_msgvec);
520 NYD_LEAVE;
523 FL void
524 print_header_summary(char const *Larg)
526 size_t bot, top, i, j;
527 NYD_ENTER;
529 if (Larg != NULL) {
530 /* Avoid any messages XXX add a make_mua_silent() and use it? */
531 if ((n_poption & (n_PO_VERB | n_PO_EXISTONLY)) == n_PO_EXISTONLY) {
532 n_stdout = freopen(n_path_devnull, "w", stdout);
533 n_stderr = freopen(n_path_devnull, "w", stderr);
535 assert(n_msgvec != NULL);
536 i = (getmsglist(/*TODO make const */n_UNCONST(Larg), n_msgvec, 0) <= 0);
537 if (n_poption & n_PO_EXISTONLY) {
538 n_exit_status = (int)i;
539 goto jleave;
541 if (i)
542 goto jleave;
543 for (bot = msgCount, top = 0, i = 0; (j = n_msgvec[i]) != 0; ++i) {
544 if (bot > j)
545 bot = j;
546 if (top < j)
547 top = j;
549 } else
550 bot = 1, top = msgCount;
551 print_headers(bot, top, (Larg != NULL)); /* TODO should take iterator!! */
552 jleave:
553 NYD_LEAVE;
556 FL void
557 n_folder_announce(enum n_announce_flags af){
558 int vec[2], mdot;
559 NYD_ENTER;
561 mdot = (mb.mb_type == MB_VOID) ? 1 : getmdot(0);
562 dot = &message[mdot - 1];
564 if(af != n_ANNOUNCE_NONE && ok_blook(header) &&
565 ((af & n_ANNOUNCE_MAIN_CALL) ||
566 ((af & n_ANNOUNCE_CHANGE) && !ok_blook(posix))))
567 af |= n_ANNOUNCE_STATUS | n__ANNOUNCE_HEADER;
569 if(af & n_ANNOUNCE_STATUS){
570 a_folder_info();
571 af |= n__ANNOUNCE_ANY;
574 if(af & n__ANNOUNCE_HEADER){
575 if(!(af & n_ANNOUNCE_MAIN_CALL) && ok_blook(bsdannounce))
576 n_OBSOLETE(_("*bsdannounce* is now default behaviour"));
577 vec[0] = mdot;
578 vec[1] = 0;
579 print_header_group(vec); /* XXX errors? */
580 af |= n__ANNOUNCE_ANY;
583 if(af & n__ANNOUNCE_ANY)
584 fflush(n_stdout);
585 NYD_LEAVE;
588 FL int
589 getmdot(int nmail)
591 struct message *mp;
592 char *cp;
593 int mdot;
594 enum mflag avoid = MHIDDEN | MDELETED;
595 NYD_ENTER;
597 if (!nmail) {
598 if (ok_blook(autothread)) {
599 n_OBSOLETE(_("please use *autosort=thread* instead of *autothread*"));
600 c_thread(NULL);
601 } else if ((cp = ok_vlook(autosort)) != NULL) {
602 if (mb.mb_sorted != NULL)
603 free(mb.mb_sorted);
604 mb.mb_sorted = sstrdup(cp);
605 c_sort(NULL);
608 if (mb.mb_type == MB_VOID) {
609 mdot = 1;
610 goto jleave;
613 if (nmail)
614 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
615 if ((mp->m_flag & (MNEWEST | avoid)) == MNEWEST)
616 break;
618 if (!nmail || PTRCMP(mp, >=, message + msgCount)) {
619 if (mb.mb_threaded) {
620 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
621 if ((mp->m_flag & (MNEW | avoid)) == MNEW)
622 break;
623 } else {
624 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
625 if ((mp->m_flag & (MNEW | avoid)) == MNEW)
626 break;
630 if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
631 if (mb.mb_threaded) {
632 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
633 if (mp->m_flag & MFLAGGED)
634 break;
635 } else {
636 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
637 if (mp->m_flag & MFLAGGED)
638 break;
642 if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
643 if (mb.mb_threaded) {
644 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
645 if (!(mp->m_flag & (MREAD | avoid)))
646 break;
647 } else {
648 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
649 if (!(mp->m_flag & (MREAD | avoid)))
650 break;
654 if (nmail &&
655 (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
656 mdot = (int)PTR2SIZE(mp - message + 1);
657 else if (ok_blook(showlast)) {
658 if (mb.mb_threaded) {
659 for (mp = this_in_thread(threadroot, -1); mp;
660 mp = prev_in_thread(mp))
661 if (!(mp->m_flag & avoid))
662 break;
663 mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
664 } else {
665 for (mp = message + msgCount - 1; mp >= message; --mp)
666 if (!(mp->m_flag & avoid))
667 break;
668 mdot = (mp >= message) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
670 } else if (!nmail &&
671 (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
672 mdot = (int)PTR2SIZE(mp - message + 1);
673 else if (mb.mb_threaded) {
674 for (mp = threadroot; mp; mp = next_in_thread(mp))
675 if (!(mp->m_flag & avoid))
676 break;
677 mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : 1;
678 } else {
679 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
680 if (!(mp->m_flag & avoid))
681 break;
682 mdot = PTRCMP(mp, <, message + msgCount)
683 ? (int)PTR2SIZE(mp - message + 1) : 1;
685 jleave:
686 NYD_LEAVE;
687 return mdot;
690 FL void
691 initbox(char const *name)
693 char *tempMesg;
694 NYD_ENTER;
696 if (mb.mb_type != MB_VOID)
697 n_strscpy(prevfile, mailname, PATH_MAX);
699 /* TODO name always NE mailname (but goes away for objects anyway)
700 * TODO Well, not true no more except that in parens */
701 _update_mailname((name != mailname) ? name : NULL);
703 if ((mb.mb_otf = Ftmp(&tempMesg, "tmpmbox", OF_WRONLY | OF_HOLDSIGS)) ==
704 NULL) {
705 n_perr(_("temporary mail message file"), 0);
706 exit(n_EXIT_ERR);
708 if ((mb.mb_itf = safe_fopen(tempMesg, "r", NULL)) == NULL) {
709 n_perr(_("temporary mail message file"), 0);
710 exit(n_EXIT_ERR);
712 Ftmp_release(&tempMesg);
714 message_reset();
715 mb.mb_active = MB_NONE;
716 mb.mb_threaded = 0;
717 if (mb.mb_sorted != NULL) {
718 free(mb.mb_sorted);
719 mb.mb_sorted = NULL;
721 dot = prevdot = threadroot = NULL;
722 n_pstate &= ~n_PS_DID_PRINT_DOT;
723 NYD_LEAVE;
726 FL char const *
727 n_folder_query(void){
728 struct n_string s, *sp = &s;
729 char *cp;
730 char const *rv;
731 bool_t err;
732 NYD_ENTER;
734 sp = n_string_creat_auto(sp);
736 /* *folder* is linked with *folder_resolved*: we only use the latter */
737 for(err = FAL0;;){
738 if((rv = ok_vlook(folder_resolved)) != NULL)
739 break;
741 /* POSIX says:
742 * If directory does not start with a <slash> ('/'), the contents
743 * of HOME shall be prefixed to it.
744 * And:
745 * If folder is unset or set to null, [.] filenames beginning with
746 * '+' shall refer to files in the current directory.
747 * We may have the result already */
748 rv = n_empty;
749 err = FAL0;
751 if((cp = ok_vlook(folder)) == NULL)
752 goto jset;
754 /* Expand the *folder*; skip %: prefix for simplicity of use */
755 if(cp[0] == '%' && cp[1] == ':')
756 cp += 2;
757 if((err = (cp = fexpand(cp, FEXP_NSPECIAL | FEXP_NFOLDER | FEXP_NSHELL)
758 ) == NULL) || *cp == '\0')
759 goto jset;
761 switch(which_protocol(cp, FAL0, FAL0, NULL)){
762 case PROTO_POP3:
763 n_err(_("*folder* can't be set to a flat, read-only POP3 account\n"));
764 err = TRU1;
765 goto jset;
766 default:
767 /* Further expansion desired */
768 break;
771 /* Prefix HOME as necessary */
772 if(*cp != '/'){ /* XXX path_is_absolute() */
773 size_t l1, l2;
774 char const *home;
776 home = ok_vlook(HOME);
777 l1 = strlen(home);
778 l2 = strlen(cp);
780 sp = n_string_reserve(sp, l1 + 1 + l2 +1);
781 sp = n_string_push_buf(sp, home, l1);
782 sp = n_string_push_c(sp, '/');
783 sp = n_string_push_buf(sp, cp, l2);
784 cp = n_string_cp(sp);
785 sp = n_string_drop_ownership(sp);
788 rv = cp;
790 /* TODO Since our visual mailname is resolved via realpath(3) if available
791 * TODO to avoid that we loose track of our currently open folder in case
792 * TODO we chdir away, but still checks the leading path portion against
793 * TODO n_folder_query() to be able to abbreviate to the +FOLDER syntax if
794 * TODO possible, we need to realpath(3) the folder, too */
795 #ifdef HAVE_REALPATH
796 assert(sp->s_len == 0 && sp->s_dat == NULL);
797 # ifndef HAVE_REALPATH_NULL
798 sp = n_string_reserve(sp, PATH_MAX +1);
799 # endif
801 if((sp->s_dat = realpath(cp, sp->s_dat)) != NULL){
802 # ifdef HAVE_REALPATH_NULL
803 n_string_cp(sp = n_string_assign_cp(sp, cp = sp->s_dat));
804 (free)(cp);
805 # endif
806 rv = sp->s_dat;
807 }else if(n_err_no == n_ERR_NOENT)
808 rv = cp;
809 else{
810 n_err(_("Can't canonicalize *folder*: %s\n"),
811 n_shexp_quote_cp(cp, FAL0));
812 err = TRU1;
813 rv = n_empty;
815 sp = n_string_drop_ownership(sp);
816 #endif /* HAVE_REALPATH */
818 /* Always append a solidus to our result path upon success */
819 if(!err){
820 size_t i;
822 if(rv[(i = strlen(rv)) - 1] != '/'){
823 sp = n_string_reserve(sp, i + 1 +1);
824 sp = n_string_push_buf(sp, rv, i);
825 sp = n_string_push_c(sp, '/');
826 rv = n_string_cp(sp);
827 sp = n_string_drop_ownership(sp);
831 jset:
832 n_PS_ROOT_BLOCK(ok_vset(folder_resolved, rv));
835 if(err){
836 n_err(_("*folder* is not resolvable, using CWD\n"));
837 assert(rv != NULL && *rv == '\0');
839 NYD_LEAVE;
840 return rv;
843 FL int
844 n_folder_mbox_prepare_append(FILE *fout, struct stat *st_or_null){
845 /* TODO n_folder_mbox_prepare_append -> Mailbox->append() */
846 struct stat stb;
847 char buf[2];
848 bool_t needsep;
849 int rv;
850 NYD2_ENTER;
852 if(fseek(fout, -2L, SEEK_END)){
853 rv = n_err_no;
855 if(st_or_null == NULL){
856 st_or_null = &stb;
857 if(fstat(fileno(fout), st_or_null))
858 goto jerrno;
861 if(st_or_null->st_size >= 2)
862 goto jleave;
863 if(st_or_null->st_size == 0){
864 rv = n_ERR_NONE;
865 goto jleave;
867 if(fseek(fout, -1L, SEEK_END))
868 goto jerrno;
871 rv = n_ERR_NONE;
873 needsep = FAL0;
874 switch(fread(buf, sizeof *buf, 2, fout)){
875 case 2:
876 if(buf[1] != '\n')
877 needsep = TRU1;
878 break;
879 case 1:
880 if(buf[0] != '\n')
881 needsep = TRU1;
882 break;
883 default:
884 if(ferror(fout))
885 goto jerrno;
886 break;
889 if(fflush(fout) || (needsep && putc('\n', fout) == EOF))
890 jerrno:
891 rv = n_err_no;
892 jleave:
893 NYD2_LEAVE;
894 return rv;
897 /* s-it-mode */