"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/folder.c" (16 Feb 2018, 26566 Bytes) of package /linux/misc/s-nail-14.9.7.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.6_vs_14.9.7.

    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    char *tempMesg;
  717    NYD_ENTER;
  718 
  719    if (mb.mb_type != MB_VOID)
  720       n_strscpy(prevfile, mailname, PATH_MAX);
  721 
  722    /* TODO name always NE mailname (but goes away for objects anyway)
  723     * TODO Well, not true no more except that in parens */
  724    _update_mailname((name != mailname) ? name : NULL);
  725 
  726    if ((mb.mb_otf = Ftmp(&tempMesg, "tmpmbox", OF_WRONLY | OF_HOLDSIGS)) ==
  727          NULL) {
  728       n_perr(_("temporary mail message file"), 0);
  729       exit(n_EXIT_ERR);
  730    }
  731    if ((mb.mb_itf = safe_fopen(tempMesg, "r", NULL)) == NULL) {
  732       n_perr(_("temporary mail message file"), 0);
  733       exit(n_EXIT_ERR);
  734    }
  735    Ftmp_release(&tempMesg);
  736 
  737    message_reset();
  738    mb.mb_active = MB_NONE;
  739    mb.mb_threaded = 0;
  740 #ifdef HAVE_IMAP
  741    mb.mb_flags = MB_NOFLAGS;
  742 #endif
  743    if (mb.mb_sorted != NULL) {
  744       free(mb.mb_sorted);
  745       mb.mb_sorted = NULL;
  746    }
  747    dot = prevdot = threadroot = NULL;
  748    n_pstate &= ~n_PS_DID_PRINT_DOT;
  749    NYD_LEAVE;
  750 }
  751 
  752 FL char const *
  753 n_folder_query(void){
  754    struct n_string s, *sp = &s;
  755    enum protocol proto;
  756    char *cp;
  757    char const *rv, *adjcp;
  758    bool_t err;
  759    NYD_ENTER;
  760 
  761    sp = n_string_creat_auto(sp);
  762 
  763    /* *folder* is linked with *folder_resolved*: we only use the latter */
  764    for(err = FAL0;;){
  765       if((rv = ok_vlook(folder_resolved)) != NULL)
  766          break;
  767 
  768       /* POSIX says:
  769        *    If directory does not start with a <slash> ('/'), the contents
  770        *    of HOME shall be prefixed to it.
  771        * And:
  772        *    If folder is unset or set to null, [.] filenames beginning with
  773        *    '+' shall refer to files in the current directory.
  774        * We may have the result already */
  775       rv = n_empty;
  776       err = FAL0;
  777 
  778       if((cp = ok_vlook(folder)) == NULL)
  779          goto jset;
  780 
  781       /* Expand the *folder*; skip %: prefix for simplicity of use */
  782       if(cp[0] == '%' && cp[1] == ':')
  783          cp += 2;
  784       if((err = (cp = fexpand(cp, FEXP_NSPECIAL | FEXP_NFOLDER | FEXP_NSHELL)
  785             ) == NULL) || *cp == '\0')
  786          goto jset;
  787       else{
  788          size_t i;
  789 
  790          for(i = strlen(cp);;){
  791             if(--i == 0)
  792                goto jset;
  793             if(cp[i] != '/'){
  794                cp[++i] = '\0';
  795                break;
  796             }
  797          }
  798       }
  799 
  800       switch((proto = which_protocol(cp, FAL0, FAL0, &adjcp))){
  801       case PROTO_POP3:
  802          n_err(_("*folder* can't be set to a flat, read-only POP3 account\n"));
  803          err = TRU1;
  804          goto jset;
  805       case PROTO_IMAP:
  806 #ifdef HAVE_IMAP
  807          rv = cp;
  808          if(!strcmp(rv, protbase(rv)))
  809             rv = savecatsep(rv, '/', n_empty);
  810 #else
  811          n_err(_("*folder*: IMAP support not compiled in\n"));
  812          err = TRU1;
  813 #endif
  814          goto jset;
  815       default:
  816          /* Further expansion desired */
  817          break;
  818       }
  819 
  820       /* Prefix HOME as necessary */
  821       if(*adjcp != '/'){ /* XXX path_is_absolute() */
  822          size_t l1, l2;
  823          char const *home;
  824 
  825          home = ok_vlook(HOME);
  826          l1 = strlen(home);
  827          l2 = strlen(cp);
  828 
  829          sp = n_string_reserve(sp, l1 + 1 + l2 +1);
  830          if(cp != adjcp){
  831             size_t i;
  832 
  833             sp = n_string_push_buf(sp, cp, i = PTR2SIZE(adjcp - cp));
  834             cp += i;
  835             l2 -= i;
  836          }
  837          sp = n_string_push_buf(sp, home, l1);
  838          sp = n_string_push_c(sp, '/');
  839          sp = n_string_push_buf(sp, cp, l2);
  840          cp = n_string_cp(sp);
  841          sp = n_string_drop_ownership(sp);
  842       }
  843 
  844       /* TODO Since our visual mailname is resolved via realpath(3) if available
  845        * TODO to avoid that we loose track of our currently open folder in case
  846        * TODO we chdir away, but still checks the leading path portion against
  847        * TODO n_folder_query() to be able to abbreviate to the +FOLDER syntax if
  848        * TODO possible, we need to realpath(3) the folder, too */
  849 #ifndef HAVE_REALPATH
  850       rv = cp;
  851 #else
  852       assert(sp->s_len == 0 && sp->s_dat == NULL);
  853 # ifndef HAVE_REALPATH_NULL
  854       sp = n_string_reserve(sp, PATH_MAX +1);
  855 # endif
  856 
  857       if((sp->s_dat = realpath(cp, sp->s_dat)) != NULL){
  858 # ifdef HAVE_REALPATH_NULL
  859          n_string_cp(sp = n_string_assign_cp(sp, cp = sp->s_dat));
  860          (free)(cp);
  861 # endif
  862          rv = sp->s_dat;
  863       }else if(n_err_no == n_ERR_NOENT)
  864          rv = cp;
  865       else{
  866          n_err(_("Can't canonicalize *folder*: %s\n"),
  867             n_shexp_quote_cp(cp, FAL0));
  868          err = TRU1;
  869          rv = n_empty;
  870       }
  871       sp = n_string_drop_ownership(sp);
  872 #endif /* HAVE_REALPATH */
  873 
  874       /* Always append a solidus to our result path upon success */
  875       if(!err){
  876          size_t i;
  877 
  878          if(rv[(i = strlen(rv)) - 1] != '/'){
  879             sp = n_string_reserve(sp, i + 1 +1);
  880             sp = n_string_push_buf(sp, rv, i);
  881             sp = n_string_push_c(sp, '/');
  882             rv = n_string_cp(sp);
  883             sp = n_string_drop_ownership(sp);
  884          }
  885       }
  886 
  887 jset:
  888       n_PS_ROOT_BLOCK(ok_vset(folder_resolved, rv));
  889    }
  890 
  891    if(err){
  892       n_err(_("*folder* is not resolvable, using CWD\n"));
  893       assert(rv != NULL && *rv == '\0');
  894    }
  895    NYD_LEAVE;
  896    return rv;
  897 }
  898 
  899 FL int
  900 n_folder_mbox_prepare_append(FILE *fout, struct stat *st_or_null){
  901    /* TODO n_folder_mbox_prepare_append -> Mailbox->append() */
  902    struct stat stb;
  903    char buf[2];
  904    bool_t needsep;
  905    int rv;
  906    NYD2_ENTER;
  907 
  908    if(fseek(fout, -2L, SEEK_END)){
  909       rv = n_err_no;
  910 
  911       if(st_or_null == NULL){
  912          st_or_null = &stb;
  913          if(fstat(fileno(fout), st_or_null))
  914             goto jerrno;
  915       }
  916 
  917       if(st_or_null->st_size >= 2)
  918          goto jleave;
  919       if(st_or_null->st_size == 0){
  920          rv = n_ERR_NONE;
  921          goto jleave;
  922       }
  923       if(fseek(fout, -1L, SEEK_END))
  924          goto jerrno;
  925    }
  926 
  927    rv = n_ERR_NONE;
  928 
  929    needsep = FAL0;
  930    switch(fread(buf, sizeof *buf, 2, fout)){
  931    case 2:
  932       if(buf[1] != '\n')
  933          needsep = TRU1;
  934       break;
  935    case 1:
  936       if(buf[0] != '\n')
  937          needsep = TRU1;
  938       break;
  939    default:
  940       if(ferror(fout))
  941          goto jerrno;
  942       break;
  943    }
  944 
  945    if(fflush(fout) || (needsep && putc('\n', fout) == EOF))
  946 jerrno:
  947       rv = n_err_no;
  948 jleave:
  949    NYD2_LEAVE;
  950    return rv;
  951 }
  952 
  953 /* s-it-mode */