"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/folder.c" (25 Mar 2018, 26600 Bytes) of package /linux/misc/s-nail-14.9.10.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "folder.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.9_vs_14.9.10.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ Folder (mailbox) initialization, newmail announcement and related.
    3  *
    4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
    5  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    6  */
    7 /*
    8  * Copyright (c) 1980, 1993
    9  *      The Regents of the University of California.  All rights reserved.
   10  *
   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.
   22  *
   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.
   34  */
   35 #undef n_FILE
   36 #define n_FILE folder
   37 
   38 #ifndef HAVE_AMALGAMATION
   39 # include "nail.h"
   40 #endif
   41 
   42 #include <pwd.h>
   43 
   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 n_INLINE size_t __narrow_suffix(char const *cp, size_t cpl, size_t maxl);
   49 #endif
   50 
   51 /**/
   52 static void a_folder_info(void);
   53 
   54 static bool_t
   55 _update_mailname(char const *name) /* TODO 2MUCH work, cache, prop of Object! */
   56 {
   57    char const *foldp;
   58    char *mailp, *dispp;
   59    size_t i, j, foldlen;
   60    bool_t rv;
   61    NYD_ENTER;
   62 
   63    /* Don't realpath(3) if it's only an update request */
   64    if(name != NULL){
   65 #ifdef HAVE_REALPATH
   66       char const *adjname;
   67       enum protocol p;
   68 
   69       p = which_protocol(name, TRU1, TRU1, &adjname);
   70 
   71       if(p == PROTO_FILE || p == PROTO_MAILDIR){
   72          name = adjname;
   73          if (realpath(name, mailname) == NULL && n_err_no != n_ERR_NOENT) {
   74             n_err(_("Can't canonicalize %s\n"), n_shexp_quote_cp(name, FAL0));
   75             goto jdocopy;
   76          }
   77       }else
   78 jdocopy:
   79 #endif
   80          n_strscpy(mailname, name, sizeof(mailname));
   81    }
   82 
   83    mailp = mailname;
   84    dispp = displayname;
   85 
   86    /* Don't display an absolute path but "+FOLDER" if under *folder* */
   87    if(*(foldp = n_folder_query()) != '\0'){
   88       foldlen = strlen(foldp);
   89       if(strncmp(foldp, mailp, foldlen))
   90          foldlen = 0;
   91    }else
   92       foldlen = 0;
   93 
   94    /* We want to see the name of the folder .. on the screen */
   95    i = strlen(mailp);
   96    if(i < sizeof(displayname) - 3 -1){
   97       if(foldlen > 0){
   98          *dispp++ = '+';
   99          *dispp++ = '[';
  100          memcpy(dispp, mailp, foldlen);
  101          dispp += foldlen;
  102          mailp += foldlen;
  103          *dispp++ = ']';
  104          memcpy(dispp, mailp, i -= foldlen);
  105          dispp[i] = '\0';
  106       }else
  107          memcpy(dispp, mailp, i +1);
  108       rv = TRU1;
  109    }else{
  110       /* Avoid disrupting multibyte sequences (if possible) */
  111 #ifndef HAVE_C90AMEND1
  112       j = sizeof(displayname) / 3 - 3;
  113       i -= sizeof(displayname) - (1/* + */ + 3) - j;
  114 #else
  115       j = field_detect_clip(sizeof(displayname) / 3, mailp, i);
  116       i = j + __narrow_suffix(mailp + j, i - j,
  117          sizeof(displayname) - (1/* + */ + 3 + 1) - j);
  118 #endif
  119       snprintf(dispp, sizeof(displayname), "%s%.*s...%s",
  120          (foldlen > 0 ? "[+]" : ""), (int)j, mailp, mailp + i);
  121       rv = FAL0;
  122    }
  123 
  124    n_PS_ROOT_BLOCK((ok_vset(mailbox_resolved, mailname),
  125       ok_vset(mailbox_display, displayname)));
  126    NYD_LEAVE;
  127    return rv;
  128 }
  129 
  130 #ifdef HAVE_C90AMEND1
  131 n_INLINE size_t
  132 __narrow_suffix(char const *cp, size_t cpl, size_t maxl)
  133 {
  134    int err;
  135    size_t i, ok;
  136    NYD_ENTER;
  137 
  138    for (err = ok = i = 0; cpl > maxl || err;) {
  139       int ml = mblen(cp, cpl);
  140       if (ml < 0) { /* XXX _narrow_suffix(): mblen() error; action? */
  141          (void)mblen(NULL, 0);
  142          err = 1;
  143          ml = 1;
  144       } else {
  145          if (!err)
  146             ok = i;
  147          err = 0;
  148          if (ml == 0)
  149             break;
  150       }
  151       cp += ml;
  152       i += ml;
  153       cpl -= ml;
  154    }
  155    NYD_LEAVE;
  156    return ok;
  157 }
  158 #endif /* HAVE_C90AMEND1 */
  159 
  160 static void
  161 a_folder_info(void){
  162    struct message *mp;
  163    int u, n, d, s, hidden, moved;
  164    NYD2_ENTER;
  165 
  166    if(mb.mb_type == MB_VOID){
  167       fprintf(n_stdout, _("(Currently no active mailbox)"));
  168       goto jleave;
  169    }
  170 
  171    s = d = hidden = moved = 0;
  172    for (mp = message, n = 0, u = 0; PTRCMP(mp, <, message + msgCount); ++mp) {
  173       if (mp->m_flag & MNEW)
  174          ++n;
  175       if ((mp->m_flag & MREAD) == 0)
  176          ++u;
  177       if ((mp->m_flag & (MDELETED | MSAVED)) == (MDELETED | MSAVED))
  178          ++moved;
  179       if ((mp->m_flag & (MDELETED | MSAVED)) == MDELETED)
  180          ++d;
  181       if ((mp->m_flag & (MDELETED | MSAVED)) == MSAVED)
  182          ++s;
  183       if (mp->m_flag & MHIDDEN)
  184          ++hidden;
  185    }
  186 
  187    /* If displayname gets truncated the user effectively has no option to see
  188     * the full pathname of the mailbox, so print it at least for '? fi' */
  189    fprintf(n_stdout, "%s: ", n_shexp_quote_cp(
  190       (_update_mailname(NULL) ? displayname : mailname), FAL0));
  191    if (msgCount == 1)
  192       fprintf(n_stdout, _("1 message"));
  193    else
  194       fprintf(n_stdout, _("%d messages"), msgCount);
  195    if (n > 0)
  196       fprintf(n_stdout, _(" %d new"), n);
  197    if (u-n > 0)
  198       fprintf(n_stdout, _(" %d unread"), u);
  199    if (d > 0)
  200       fprintf(n_stdout, _(" %d deleted"), d);
  201    if (s > 0)
  202       fprintf(n_stdout, _(" %d saved"), s);
  203    if (moved > 0)
  204       fprintf(n_stdout, _(" %d moved"), moved);
  205    if (hidden > 0)
  206       fprintf(n_stdout, _(" %d hidden"), hidden);
  207    if (mb.mb_perm == 0)
  208       fprintf(n_stdout, _(" [Read-only]"));
  209 #ifdef HAVE_IMAP
  210    if (mb.mb_type == MB_CACHE)
  211       fprintf(n_stdout, _(" [Disconnected]"));
  212 #endif
  213 
  214 jleave:
  215    putc('\n', n_stdout);
  216    NYD2_LEAVE;
  217 }
  218 
  219 FL int
  220 setfile(char const *name, enum fedit_mode fm) /* TODO oh my god */
  221 {
  222    /* TODO This all needs to be converted to URL:: and Mailbox:: */
  223    static int shudclob;
  224 
  225    struct stat stb;
  226    size_t offset;
  227    char const *who, *orig_name;
  228    int rv, omsgCount = 0;
  229    FILE *ibuf = NULL, *lckfp = NULL;
  230    bool_t isdevnull = FAL0;
  231    NYD_ENTER;
  232 
  233    n_pstate &= ~n_PS_SETFILE_OPENED;
  234 
  235    /* C99 */{
  236       enum fexp_mode fexpm;
  237 
  238       if((who = shortcut_expand(name)) != NULL){
  239          fexpm = FEXP_NSHORTCUT/* XXX | FEXP_NSHELL*/;
  240          name = who;
  241       }else
  242          fexpm = FEXP_NSHELL;
  243 
  244       if(name[0] == '%'){
  245          char const *cp;
  246 
  247          fm |= FEDIT_SYSBOX; /* TODO fexpand() needs to tell is-valid-user! */
  248          if(*(who = &name[1]) == ':')
  249             ++who;
  250          if((cp = strrchr(who, '/')) != NULL)
  251             who = &cp[1];
  252          if(*who == '\0')
  253             goto jlogname;
  254       }else
  255 jlogname:
  256          who = ok_vlook(LOGNAME);
  257 
  258       if ((name = fexpand(name, fexpm)) == NULL)
  259          goto jem1;
  260    }
  261 
  262    /* For at least substdate() users TODO -> eventloop tick */
  263    time_current_update(&time_current, FAL0);
  264 
  265    switch (which_protocol(orig_name = name, TRU1, TRU1, &name)) {
  266    case PROTO_FILE:
  267       isdevnull = ((n_poption & n_PO_BATCH_FLAG) &&
  268             !strcmp(name, n_path_devnull));
  269 #ifdef HAVE_REALPATH
  270       do { /* TODO we need objects, goes away then */
  271 # ifdef HAVE_REALPATH_NULL
  272          char *cp;
  273 
  274          if ((cp = realpath(name, NULL)) != NULL) {
  275             name = savestr(cp);
  276             (free)(cp);
  277          }
  278 # else
  279          char cbuf[PATH_MAX];
  280 
  281          if (realpath(name, cbuf) != NULL)
  282             name = savestr(cbuf);
  283 # endif
  284       } while (0);
  285 #endif
  286       rv = 1;
  287       break;
  288    case PROTO_MAILDIR:
  289       shudclob = 1;
  290       rv = maildir_setfile(name, fm);
  291       goto jleave;
  292 #ifdef HAVE_POP3
  293    case PROTO_POP3:
  294       shudclob = 1;
  295       rv = pop3_setfile(orig_name, fm);
  296       goto jleave;
  297 #endif
  298 #ifdef HAVE_IMAP
  299    case PROTO_IMAP:
  300       shudclob = 1;
  301       if((fm & FEDIT_NEWMAIL) && mb.mb_type == MB_CACHE)
  302          rv = 1;
  303       else
  304          rv = imap_setfile(orig_name, fm);
  305       goto jleave;
  306 #endif
  307    default:
  308       n_err(_("Cannot handle protocol: %s\n"), orig_name);
  309       goto jem1;
  310    }
  311 
  312    if ((ibuf = n_fopen_any(savecat("file://", name), "r", NULL)) == NULL) {
  313       int e = n_err_no;
  314 
  315       if ((fm & FEDIT_SYSBOX) && e == n_ERR_NOENT) {
  316          if (strcmp(who, ok_vlook(LOGNAME)) && getpwnam(who) == NULL) {
  317             n_err(_("%s is not a user of this system\n"),
  318                n_shexp_quote_cp(who, FAL0));
  319             goto jem2;
  320          }
  321          if (!(n_poption & n_PO_QUICKRUN_MASK) && ok_blook(bsdcompat))
  322             n_err(_("No mail for %s\n"), who);
  323       }
  324       if (fm & FEDIT_NEWMAIL)
  325          goto jleave;
  326 
  327       mb.mb_type = MB_VOID;
  328       if (ok_blook(emptystart)) {
  329          if (!(n_poption & n_PO_QUICKRUN_MASK) && !ok_blook(bsdcompat))
  330             n_perr(name, e);
  331          /* We must avoid returning -1 and causing program exit */
  332          rv = 1;
  333          goto jleave;
  334       }
  335       n_perr(name, e);
  336       goto jem1;
  337    }
  338 
  339    if (fstat(fileno(ibuf), &stb) == -1) {
  340       if (fm & FEDIT_NEWMAIL)
  341          goto jleave;
  342       n_perr(_("fstat"), 0);
  343       goto jem1;
  344    }
  345 
  346    if (S_ISREG(stb.st_mode) || isdevnull) {
  347       /* EMPTY */
  348    } else {
  349       if (fm & FEDIT_NEWMAIL)
  350          goto jleave;
  351       n_err_no = S_ISDIR(stb.st_mode) ? n_ERR_ISDIR : n_ERR_INVAL;
  352       n_perr(name, 0);
  353       goto jem1;
  354    }
  355 
  356    if (shudclob && !(fm & FEDIT_NEWMAIL) && !quit(FAL0))
  357       goto jem2;
  358 
  359    hold_sigs();
  360 
  361 #ifdef HAVE_SOCKETS
  362    if (!(fm & FEDIT_NEWMAIL) && mb.mb_sock.s_fd >= 0)
  363       sclose(&mb.mb_sock); /* TODO sorry? VMAILFS->close(), thank you */
  364 #endif
  365 
  366    /* TODO There is no intermediate VOID box we've switched to: name may
  367     * TODO point to the same box that we just have written, so any updates
  368     * TODO we won't see!  Reopen again in this case.  RACY! Goes with VOID! */
  369    /* TODO In addition: in case of compressed/hook boxes we lock a temporary! */
  370    /* TODO We may uselessly open twice but quit() doesn't say whether we were
  371     * TODO modified so we can't tell: Mailbox::is_modified() :-(( */
  372    if (/*shudclob && !(fm & FEDIT_NEWMAIL) &&*/ !strcmp(name, mailname)) {
  373       name = mailname;
  374       Fclose(ibuf);
  375 
  376       if ((ibuf = n_fopen_any(name, "r", NULL)) == NULL ||
  377             fstat(fileno(ibuf), &stb) == -1 ||
  378             (!S_ISREG(stb.st_mode) && !isdevnull)) {
  379          n_perr(name, 0);
  380          rele_sigs();
  381          goto jem2;
  382       }
  383    }
  384 
  385    /* Copy the messages into /tmp and set pointers */
  386    if (!(fm & FEDIT_NEWMAIL)) {
  387       if (isdevnull) {
  388          mb.mb_type = MB_VOID;
  389          mb.mb_perm = 0;
  390       } else {
  391          mb.mb_type = MB_FILE;
  392          mb.mb_perm = (((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY) ||
  393                access(name, W_OK) < 0) ? 0 : MB_DELE | MB_EDIT);
  394          if (shudclob) {
  395             if (mb.mb_itf) {
  396                fclose(mb.mb_itf);
  397                mb.mb_itf = NULL;
  398             }
  399             if (mb.mb_otf) {
  400                fclose(mb.mb_otf);
  401                mb.mb_otf = NULL;
  402             }
  403          }
  404       }
  405       shudclob = 1;
  406       if (fm & FEDIT_SYSBOX)
  407          n_pstate &= ~n_PS_EDIT;
  408       else
  409          n_pstate |= n_PS_EDIT;
  410       initbox(name);
  411       offset = 0;
  412    } else {
  413       fseek(mb.mb_otf, 0L, SEEK_END);
  414       /* TODO Doing this without holding a lock is.. And not err checking.. */
  415       fseek(ibuf, mailsize, SEEK_SET);
  416       offset = mailsize;
  417       omsgCount = msgCount;
  418    }
  419 
  420    if (isdevnull)
  421       lckfp = (FILE*)-1;
  422    else if (!(n_pstate & n_PS_EDIT))
  423       lckfp = n_dotlock(name, fileno(ibuf), FLT_READ, offset,0,
  424             (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX));
  425    else if (n_file_lock(fileno(ibuf), FLT_READ, offset,0,
  426          (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX)))
  427       lckfp = (FILE*)-1;
  428 
  429    if (lckfp == NULL) {
  430       if (!(fm & FEDIT_NEWMAIL)) {
  431          char const *emsg = (n_pstate & n_PS_EDIT)
  432                ? N_("Unable to lock mailbox, aborting operation")
  433                : N_("Unable to (dot) lock mailbox, aborting operation");
  434          n_perr(V_(emsg), 0);
  435       }
  436       rele_sigs();
  437       if (!(fm & FEDIT_NEWMAIL))
  438          goto jem1;
  439       goto jleave;
  440    }
  441 
  442    mailsize = fsize(ibuf);
  443 
  444    /* TODO This is too simple minded?  We should regenerate an index file
  445     * TODO to be able to truly tell whether *anything* has changed! */
  446    if ((fm & FEDIT_NEWMAIL) && UICMP(z, mailsize, <=, offset)) {
  447       rele_sigs();
  448       goto jleave;
  449    }
  450    setptr(ibuf, offset);
  451    setmsize(msgCount);
  452    if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted) {
  453       mb.mb_threaded = 0;
  454       c_sort((void*)-1);
  455    }
  456 
  457    Fclose(ibuf);
  458    ibuf = NULL;
  459    if (lckfp != NULL && lckfp != (FILE*)-1) {
  460       Pclose(lckfp, FAL0);
  461       /*lckfp = NULL;*/
  462    }
  463 
  464    if (!(fm & FEDIT_NEWMAIL)) {
  465       n_pstate &= ~n_PS_SAW_COMMAND;
  466       n_pstate |= n_PS_SETFILE_OPENED;
  467    }
  468 
  469    rele_sigs();
  470 
  471    rv = (msgCount == 0);
  472    if(rv)
  473       n_err_no = n_ERR_NODATA;
  474 
  475    if(n_poption & n_PO_EXISTONLY)
  476       goto jleave;
  477 
  478    if(rv){
  479       if(!(n_pstate & n_PS_EDIT) || (fm & FEDIT_NEWMAIL)){
  480          if(!(fm & FEDIT_NEWMAIL)){
  481             if (!ok_blook(emptystart))
  482                n_err(_("No mail for %s\n"), who);
  483          }
  484          goto jleave;
  485       }
  486    }
  487 
  488    if(fm & FEDIT_NEWMAIL)
  489       newmailinfo(omsgCount);
  490 jleave:
  491    if (ibuf != NULL) {
  492       Fclose(ibuf);
  493       if (lckfp != NULL && lckfp != (FILE*)-1)
  494          Pclose(lckfp, FAL0);
  495    }
  496    NYD_LEAVE;
  497    return rv;
  498 jem2:
  499    mb.mb_type = MB_VOID;
  500 jem1:
  501    n_err_no = n_ERR_NOTOBACCO;
  502    rv = -1;
  503    goto jleave;
  504 }
  505 
  506 FL int
  507 newmailinfo(int omsgCount)
  508 {
  509    int mdot, i;
  510    NYD_ENTER;
  511 
  512    for (i = 0; i < omsgCount; ++i)
  513       message[i].m_flag &= ~MNEWEST;
  514 
  515    if (msgCount > omsgCount) {
  516       for (i = omsgCount; i < msgCount; ++i)
  517          message[i].m_flag |= MNEWEST;
  518       fprintf(n_stdout, _("New mail has arrived.\n"));
  519       if ((i = msgCount - omsgCount) == 1)
  520          fprintf(n_stdout, _("Loaded 1 new message.\n"));
  521       else
  522          fprintf(n_stdout, _("Loaded %d new messages.\n"), i);
  523    } else
  524       fprintf(n_stdout, _("Loaded %d messages.\n"), msgCount);
  525 
  526    temporary_folder_hook_check(TRU1);
  527 
  528    mdot = getmdot(1);
  529 
  530    if (ok_blook(header))
  531       print_headers(omsgCount + 1, msgCount, FAL0);
  532    NYD_LEAVE;
  533    return mdot;
  534 }
  535 
  536 FL void
  537 setmsize(int sz)
  538 {
  539    NYD_ENTER;
  540    if (n_msgvec != NULL)
  541       free(n_msgvec);
  542    n_msgvec = scalloc(sz + 1, sizeof *n_msgvec);
  543    NYD_LEAVE;
  544 }
  545 
  546 FL void
  547 print_header_summary(char const *Larg)
  548 {
  549    size_t bot, top, i, j;
  550    NYD_ENTER;
  551 
  552    if (Larg != NULL) {
  553       /* Avoid any messages XXX add a make_mua_silent() and use it? */
  554       if ((n_poption & (n_PO_VERB | n_PO_EXISTONLY)) == n_PO_EXISTONLY) {
  555          n_stdout = freopen(n_path_devnull, "w", stdout);
  556          n_stderr = freopen(n_path_devnull, "w", stderr);
  557       }
  558       assert(n_msgvec != NULL);
  559       i = (getmsglist(/*TODO make const */n_UNCONST(Larg), n_msgvec, 0) <= 0);
  560       if (n_poption & n_PO_EXISTONLY) {
  561          n_exit_status = (int)i;
  562          goto jleave;
  563       }
  564       if (i)
  565          goto jleave;
  566       for (bot = msgCount, top = 0, i = 0; (j = n_msgvec[i]) != 0; ++i) {
  567          if (bot > j)
  568             bot = j;
  569          if (top < j)
  570             top = j;
  571       }
  572    } else
  573       bot = 1, top = msgCount;
  574    print_headers(bot, top, (Larg != NULL)); /* TODO should take iterator!! */
  575 jleave:
  576    NYD_LEAVE;
  577 }
  578 
  579 FL void
  580 n_folder_announce(enum n_announce_flags af){
  581    int vec[2], mdot;
  582    NYD_ENTER;
  583 
  584    mdot = (mb.mb_type == MB_VOID) ? 1 : getmdot(0);
  585    dot = &message[mdot - 1];
  586 
  587    if(af != n_ANNOUNCE_NONE && ok_blook(header) &&
  588          ((af & n_ANNOUNCE_MAIN_CALL) ||
  589           ((af & n_ANNOUNCE_CHANGE) && !ok_blook(posix))))
  590       af |= n_ANNOUNCE_STATUS | n__ANNOUNCE_HEADER;
  591 
  592    if(af & n_ANNOUNCE_STATUS){
  593       a_folder_info();
  594       af |= n__ANNOUNCE_ANY;
  595    }
  596 
  597    if(af & n__ANNOUNCE_HEADER){
  598       if(!(af & n_ANNOUNCE_MAIN_CALL) && ok_blook(bsdannounce))
  599          n_OBSOLETE(_("*bsdannounce* is now default behaviour"));
  600       vec[0] = mdot;
  601       vec[1] = 0;
  602       print_header_group(vec); /* XXX errors? */
  603       af |= n__ANNOUNCE_ANY;
  604    }
  605 
  606    if(af & n__ANNOUNCE_ANY)
  607       fflush(n_stdout);
  608    NYD_LEAVE;
  609 }
  610 
  611 FL int
  612 getmdot(int nmail)
  613 {
  614    struct message *mp;
  615    char *cp;
  616    int mdot;
  617    enum mflag avoid = MHIDDEN | MDELETED;
  618    NYD_ENTER;
  619 
  620    if (!nmail) {
  621       if (ok_blook(autothread)) {
  622          n_OBSOLETE(_("please use *autosort=thread* instead of *autothread*"));
  623          c_thread(NULL);
  624       } else if ((cp = ok_vlook(autosort)) != NULL) {
  625          if (mb.mb_sorted != NULL)
  626             free(mb.mb_sorted);
  627          mb.mb_sorted = sstrdup(cp);
  628          c_sort(NULL);
  629       }
  630    }
  631    if (mb.mb_type == MB_VOID) {
  632       mdot = 1;
  633       goto jleave;
  634    }
  635 
  636    if (nmail)
  637       for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
  638          if ((mp->m_flag & (MNEWEST | avoid)) == MNEWEST)
  639             break;
  640 
  641    if (!nmail || PTRCMP(mp, >=, message + msgCount)) {
  642       if (mb.mb_threaded) {
  643          for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
  644             if ((mp->m_flag & (MNEW | avoid)) == MNEW)
  645                break;
  646       } else {
  647          for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
  648             if ((mp->m_flag & (MNEW | avoid)) == MNEW)
  649                break;
  650       }
  651    }
  652 
  653    if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
  654       if (mb.mb_threaded) {
  655          for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
  656             if (mp->m_flag & MFLAGGED)
  657                break;
  658       } else {
  659          for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
  660             if (mp->m_flag & MFLAGGED)
  661                break;
  662       }
  663    }
  664 
  665    if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
  666       if (mb.mb_threaded) {
  667          for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
  668             if (!(mp->m_flag & (MREAD | avoid)))
  669                break;
  670       } else {
  671          for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
  672             if (!(mp->m_flag & (MREAD | avoid)))
  673                break;
  674       }
  675    }
  676 
  677    if (nmail &&
  678          (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
  679       mdot = (int)PTR2SIZE(mp - message + 1);
  680    else if (ok_blook(showlast)) {
  681       if (mb.mb_threaded) {
  682          for (mp = this_in_thread(threadroot, -1); mp;
  683                mp = prev_in_thread(mp))
  684             if (!(mp->m_flag & avoid))
  685                break;
  686          mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
  687       } else {
  688          for (mp = message + msgCount - 1; mp >= message; --mp)
  689             if (!(mp->m_flag & avoid))
  690                break;
  691          mdot = (mp >= message) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
  692       }
  693    } else if (!nmail &&
  694          (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
  695       mdot = (int)PTR2SIZE(mp - message + 1);
  696    else if (mb.mb_threaded) {
  697       for (mp = threadroot; mp; mp = next_in_thread(mp))
  698          if (!(mp->m_flag & avoid))
  699             break;
  700       mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : 1;
  701    } else {
  702       for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
  703          if (!(mp->m_flag & avoid))
  704             break;
  705       mdot = PTRCMP(mp, <, message + msgCount)
  706             ? (int)PTR2SIZE(mp - message + 1) : 1;
  707    }
  708 jleave:
  709    NYD_LEAVE;
  710    return mdot;
  711 }
  712 
  713 FL void
  714 initbox(char const *name)
  715 {
  716    bool_t err;
  717    char *tempMesg;
  718    NYD_ENTER;
  719 
  720    if (mb.mb_type != MB_VOID)
  721       n_strscpy(prevfile, mailname, PATH_MAX);
  722 
  723    /* TODO name always NE mailname (but goes away for objects anyway)
  724     * TODO Well, not true no more except that in parens */
  725    _update_mailname((name != mailname) ? name : NULL);
  726 
  727    err = FAL0;
  728    if((mb.mb_otf = Ftmp(&tempMesg, "tmpmbox", OF_WRONLY | OF_HOLDSIGS)) ==
  729          NULL){
  730       n_perr(_("initbox: temporary mail message file, writer"), 0);
  731       err = TRU1;
  732    }else if((mb.mb_itf = safe_fopen(tempMesg, "r", NULL)) == NULL) {
  733       n_perr(_("initbox: temporary mail message file, reader"), 0);
  734       err = TRU1;
  735    }
  736    Ftmp_release(&tempMesg);
  737    if(err)
  738       exit(n_EXIT_ERR);
  739 
  740    message_reset();
  741    mb.mb_active = MB_NONE;
  742    mb.mb_threaded = 0;
  743 #ifdef HAVE_IMAP
  744    mb.mb_flags = MB_NOFLAGS;
  745 #endif
  746    if (mb.mb_sorted != NULL) {
  747       free(mb.mb_sorted);
  748       mb.mb_sorted = NULL;
  749    }
  750    dot = prevdot = threadroot = NULL;
  751    n_pstate &= ~n_PS_DID_PRINT_DOT;
  752    NYD_LEAVE;
  753 }
  754 
  755 FL char const *
  756 n_folder_query(void){
  757    struct n_string s, *sp = &s;
  758    enum protocol proto;
  759    char *cp;
  760    char const *rv, *adjcp;
  761    bool_t err;
  762    NYD_ENTER;
  763 
  764    sp = n_string_creat_auto(sp);
  765 
  766    /* *folder* is linked with *folder_resolved*: we only use the latter */
  767    for(err = FAL0;;){
  768       if((rv = ok_vlook(folder_resolved)) != NULL)
  769          break;
  770 
  771       /* POSIX says:
  772        *    If directory does not start with a <slash> ('/'), the contents
  773        *    of HOME shall be prefixed to it.
  774        * And:
  775        *    If folder is unset or set to null, [.] filenames beginning with
  776        *    '+' shall refer to files in the current directory.
  777        * We may have the result already */
  778       rv = n_empty;
  779       err = FAL0;
  780 
  781       if((cp = ok_vlook(folder)) == NULL)
  782          goto jset;
  783 
  784       /* Expand the *folder*; skip %: prefix for simplicity of use */
  785       if(cp[0] == '%' && cp[1] == ':')
  786          cp += 2;
  787       if((err = (cp = fexpand(cp, FEXP_NSPECIAL | FEXP_NFOLDER | FEXP_NSHELL)
  788             ) == NULL) || *cp == '\0')
  789          goto jset;
  790       else{
  791          size_t i;
  792 
  793          for(i = strlen(cp);;){
  794             if(--i == 0)
  795                goto jset;
  796             if(cp[i] != '/'){
  797                cp[++i] = '\0';
  798                break;
  799             }
  800          }
  801       }
  802 
  803       switch((proto = which_protocol(cp, FAL0, FAL0, &adjcp))){
  804       case PROTO_POP3:
  805          n_err(_("*folder* can't be set to a flat, read-only POP3 account\n"));
  806          err = TRU1;
  807          goto jset;
  808       case PROTO_IMAP:
  809 #ifdef HAVE_IMAP
  810          rv = cp;
  811          if(!strcmp(rv, protbase(rv)))
  812             rv = savecatsep(rv, '/', n_empty);
  813 #else
  814          n_err(_("*folder*: IMAP support not compiled in\n"));
  815          err = TRU1;
  816 #endif
  817          goto jset;
  818       default:
  819          /* Further expansion desired */
  820          break;
  821       }
  822 
  823       /* Prefix HOME as necessary */
  824       if(*adjcp != '/'){ /* XXX path_is_absolute() */
  825          size_t l1, l2;
  826          char const *home;
  827 
  828          home = ok_vlook(HOME);
  829          l1 = strlen(home);
  830          l2 = strlen(cp);
  831 
  832          sp = n_string_reserve(sp, l1 + 1 + l2 +1);
  833          if(cp != adjcp){
  834             size_t i;
  835 
  836             sp = n_string_push_buf(sp, cp, i = PTR2SIZE(adjcp - cp));
  837             cp += i;
  838             l2 -= i;
  839          }
  840          sp = n_string_push_buf(sp, home, l1);
  841          sp = n_string_push_c(sp, '/');
  842          sp = n_string_push_buf(sp, cp, l2);
  843          cp = n_string_cp(sp);
  844          sp = n_string_drop_ownership(sp);
  845       }
  846 
  847       /* TODO Since our visual mailname is resolved via realpath(3) if available
  848        * TODO to avoid that we loose track of our currently open folder in case
  849        * TODO we chdir away, but still checks the leading path portion against
  850        * TODO n_folder_query() to be able to abbreviate to the +FOLDER syntax if
  851        * TODO possible, we need to realpath(3) the folder, too */
  852 #ifndef HAVE_REALPATH
  853       rv = cp;
  854 #else
  855       assert(sp->s_len == 0 && sp->s_dat == NULL);
  856 # ifndef HAVE_REALPATH_NULL
  857       sp = n_string_reserve(sp, PATH_MAX +1);
  858 # endif
  859 
  860       if((sp->s_dat = realpath(cp, sp->s_dat)) != NULL){
  861 # ifdef HAVE_REALPATH_NULL
  862          n_string_cp(sp = n_string_assign_cp(sp, cp = sp->s_dat));
  863          (free)(cp);
  864 # endif
  865          rv = sp->s_dat;
  866       }else if(n_err_no == n_ERR_NOENT)
  867          rv = cp;
  868       else{
  869          n_err(_("Can't canonicalize *folder*: %s\n"),
  870             n_shexp_quote_cp(cp, FAL0));
  871          err = TRU1;
  872          rv = n_empty;
  873       }
  874       sp = n_string_drop_ownership(sp);
  875 #endif /* HAVE_REALPATH */
  876 
  877       /* Always append a solidus to our result path upon success */
  878       if(!err){
  879          size_t i;
  880 
  881          if(rv[(i = strlen(rv)) - 1] != '/'){
  882             sp = n_string_reserve(sp, i + 1 +1);
  883             sp = n_string_push_buf(sp, rv, i);
  884             sp = n_string_push_c(sp, '/');
  885             rv = n_string_cp(sp);
  886             sp = n_string_drop_ownership(sp);
  887          }
  888       }
  889 
  890 jset:
  891       n_PS_ROOT_BLOCK(ok_vset(folder_resolved, rv));
  892    }
  893 
  894    if(err){
  895       n_err(_("*folder* is not resolvable, using CWD\n"));
  896       assert(rv != NULL && *rv == '\0');
  897    }
  898    NYD_LEAVE;
  899    return rv;
  900 }
  901 
  902 FL int
  903 n_folder_mbox_prepare_append(FILE *fout, struct stat *st_or_null){
  904    /* TODO n_folder_mbox_prepare_append -> Mailbox->append() */
  905    struct stat stb;
  906    char buf[2];
  907    int rv;
  908    bool_t needsep;
  909    NYD2_ENTER;
  910 
  911    if(!fseek(fout, -2L, SEEK_END)){
  912       if(fread(buf, sizeof *buf, 2, fout) != 2)
  913          goto jerrno;
  914       needsep = (buf[0] != '\n' || buf[1] != '\n');
  915    }else{
  916       rv = n_err_no;
  917 
  918       if(st_or_null == NULL){
  919          st_or_null = &stb;
  920          if(fstat(fileno(fout), st_or_null))
  921             goto jerrno;
  922       }
  923 
  924       if(st_or_null->st_size >= 2)
  925          goto jleave;
  926       if(st_or_null->st_size == 0){
  927          rv = n_ERR_NONE;
  928          goto jleave;
  929       }
  930 
  931       if(fseek(fout, -1L, SEEK_END))
  932          goto jerrno;
  933       if(fread(buf, sizeof *buf, 1, fout) != 1)
  934          goto jerrno;
  935       needsep = (buf[0] != '\n');
  936    }
  937 
  938    rv = n_ERR_NONE;
  939    if(fflush(fout) || (needsep && putc('\n', fout) == EOF))
  940 jerrno:
  941       rv = n_err_no;
  942 jleave:
  943    NYD2_LEAVE;
  944    return rv;
  945 }
  946 
  947 /* s-it-mode */