"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/folder.c" (8 Aug 2018, 32406 Bytes) of package /linux/misc/s-nail-14.9.11.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.10_vs_14.9.11.

    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  * SPDX-License-Identifier: BSD-3-Clause
    7  */
    8 /*
    9  * Copyright (c) 1980, 1993
   10  *      The Regents of the University of California.  All rights reserved.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  * 3. Neither the name of the University nor the names of its contributors
   21  *    may be used to endorse or promote products derived from this software
   22  *    without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   34  * SUCH DAMAGE.
   35  */
   36 #undef n_FILE
   37 #define n_FILE folder
   38 
   39 #ifndef HAVE_AMALGAMATION
   40 # include "nail.h"
   41 #endif
   42 
   43 #include <pwd.h>
   44 
   45 /* Update mailname (if name != NULL) and displayname, return whether displayname
   46  * was large enough to swallow mailname */
   47 static bool_t  _update_mailname(char const *name);
   48 #ifdef HAVE_C90AMEND1 /* TODO unite __narrow_suffix() into one fun! */
   49 n_INLINE size_t __narrow_suffix(char const *cp, size_t cpl, size_t maxl);
   50 #endif
   51 
   52 /**/
   53 static void a_folder_info(void);
   54 
   55 /* Set up the input pointers while copying the mail file into /tmp */
   56 static void a_folder_mbox_setptr(FILE *ibuf, off_t offset);
   57 
   58 static bool_t
   59 _update_mailname(char const *name) /* TODO 2MUCH work, cache, prop of Object! */
   60 {
   61    char const *foldp;
   62    char *mailp, *dispp;
   63    size_t i, j, foldlen;
   64    bool_t rv;
   65    NYD_ENTER;
   66 
   67    /* Don't realpath(3) if it's only an update request */
   68    if(name != NULL){
   69 #ifdef HAVE_REALPATH
   70       char const *adjname;
   71       enum protocol p;
   72 
   73       p = which_protocol(name, TRU1, TRU1, &adjname);
   74 
   75       if(p == PROTO_FILE || p == PROTO_MAILDIR){
   76          name = adjname;
   77          if (realpath(name, mailname) == NULL && n_err_no != n_ERR_NOENT) {
   78             n_err(_("Can't canonicalize %s\n"), n_shexp_quote_cp(name, FAL0));
   79             goto jdocopy;
   80          }
   81       }else
   82 jdocopy:
   83 #endif
   84          n_strscpy(mailname, name, sizeof(mailname));
   85    }
   86 
   87    mailp = mailname;
   88    dispp = displayname;
   89 
   90    /* Don't display an absolute path but "+FOLDER" if under *folder* */
   91    if(*(foldp = n_folder_query()) != '\0'){
   92       foldlen = strlen(foldp);
   93       if(strncmp(foldp, mailp, foldlen))
   94          foldlen = 0;
   95    }else
   96       foldlen = 0;
   97 
   98    /* We want to see the name of the folder .. on the screen */
   99    i = strlen(mailp);
  100    if(i < sizeof(displayname) - 3 -1){
  101       if(foldlen > 0){
  102          *dispp++ = '+';
  103          *dispp++ = '[';
  104          memcpy(dispp, mailp, foldlen);
  105          dispp += foldlen;
  106          mailp += foldlen;
  107          *dispp++ = ']';
  108          memcpy(dispp, mailp, i -= foldlen);
  109          dispp[i] = '\0';
  110       }else
  111          memcpy(dispp, mailp, i +1);
  112       rv = TRU1;
  113    }else{
  114       /* Avoid disrupting multibyte sequences (if possible) */
  115 #ifndef HAVE_C90AMEND1
  116       j = sizeof(displayname) / 3 - 3;
  117       i -= sizeof(displayname) - (1/* + */ + 3) - j;
  118 #else
  119       j = field_detect_clip(sizeof(displayname) / 3, mailp, i);
  120       i = j + __narrow_suffix(mailp + j, i - j,
  121          sizeof(displayname) - (1/* + */ + 3 + 1) - j);
  122 #endif
  123       snprintf(dispp, sizeof(displayname), "%s%.*s...%s",
  124          (foldlen > 0 ? "[+]" : ""), (int)j, mailp, mailp + i);
  125       rv = FAL0;
  126    }
  127 
  128    n_PS_ROOT_BLOCK((ok_vset(mailbox_resolved, mailname),
  129       ok_vset(mailbox_display, displayname)));
  130    NYD_LEAVE;
  131    return rv;
  132 }
  133 
  134 #ifdef HAVE_C90AMEND1
  135 n_INLINE size_t
  136 __narrow_suffix(char const *cp, size_t cpl, size_t maxl)
  137 {
  138    int err;
  139    size_t i, ok;
  140    NYD_ENTER;
  141 
  142    for (err = ok = i = 0; cpl > maxl || err;) {
  143       int ml = mblen(cp, cpl);
  144       if (ml < 0) { /* XXX _narrow_suffix(): mblen() error; action? */
  145          (void)mblen(NULL, 0);
  146          err = 1;
  147          ml = 1;
  148       } else {
  149          if (!err)
  150             ok = i;
  151          err = 0;
  152          if (ml == 0)
  153             break;
  154       }
  155       cp += ml;
  156       i += ml;
  157       cpl -= ml;
  158    }
  159    NYD_LEAVE;
  160    return ok;
  161 }
  162 #endif /* HAVE_C90AMEND1 */
  163 
  164 static void
  165 a_folder_info(void){
  166    struct message *mp;
  167    int u, n, d, s, hidden, moved;
  168    NYD2_ENTER;
  169 
  170    if(mb.mb_type == MB_VOID){
  171       fprintf(n_stdout, _("(Currently no active mailbox)"));
  172       goto jleave;
  173    }
  174 
  175    s = d = hidden = moved = 0;
  176    for (mp = message, n = 0, u = 0; PTRCMP(mp, <, message + msgCount); ++mp) {
  177       if (mp->m_flag & MNEW)
  178          ++n;
  179       if ((mp->m_flag & MREAD) == 0)
  180          ++u;
  181       if ((mp->m_flag & (MDELETED | MSAVED)) == (MDELETED | MSAVED))
  182          ++moved;
  183       if ((mp->m_flag & (MDELETED | MSAVED)) == MDELETED)
  184          ++d;
  185       if ((mp->m_flag & (MDELETED | MSAVED)) == MSAVED)
  186          ++s;
  187       if (mp->m_flag & MHIDDEN)
  188          ++hidden;
  189    }
  190 
  191    /* If displayname gets truncated the user effectively has no option to see
  192     * the full pathname of the mailbox, so print it at least for '? fi' */
  193    fprintf(n_stdout, "%s: ", n_shexp_quote_cp(
  194       (_update_mailname(NULL) ? displayname : mailname), FAL0));
  195    if (msgCount == 1)
  196       fprintf(n_stdout, _("1 message"));
  197    else
  198       fprintf(n_stdout, _("%d messages"), msgCount);
  199    if (n > 0)
  200       fprintf(n_stdout, _(" %d new"), n);
  201    if (u-n > 0)
  202       fprintf(n_stdout, _(" %d unread"), u);
  203    if (d > 0)
  204       fprintf(n_stdout, _(" %d deleted"), d);
  205    if (s > 0)
  206       fprintf(n_stdout, _(" %d saved"), s);
  207    if (moved > 0)
  208       fprintf(n_stdout, _(" %d moved"), moved);
  209    if (hidden > 0)
  210       fprintf(n_stdout, _(" %d hidden"), hidden);
  211    if (mb.mb_perm == 0)
  212       fprintf(n_stdout, _(" [Read-only]"));
  213 #ifdef HAVE_IMAP
  214    if (mb.mb_type == MB_CACHE)
  215       fprintf(n_stdout, _(" [Disconnected]"));
  216 #endif
  217 
  218 jleave:
  219    putc('\n', n_stdout);
  220    NYD2_LEAVE;
  221 }
  222 
  223 static void
  224 a_folder_mbox_setptr(FILE *ibuf, off_t offset) /* TODO Mailbox->setptr() */
  225 {
  226    struct message self;
  227    char const *cp2;
  228    char *linebuf, *cp;
  229    int selfcnt, c;
  230    bool_t need_rfc4155, maybe, inhead, from_;
  231    size_t filesize, linesize, cnt;
  232    NYD_ENTER;
  233 
  234    memset(&self, 0, sizeof self);
  235    self.m_flag = MUSED | MNEW | MNEWEST;
  236    filesize = mailsize - offset;
  237    offset = ftell(mb.mb_otf);
  238    need_rfc4155 = ok_blook(mbox_rfc4155);
  239    maybe = TRU1;
  240    inhead = FAL0;
  241    selfcnt = 0;
  242    linebuf = NULL, linesize = 0; /* TODO string pool */
  243 
  244    for (;;) {
  245       if (fgetline(&linebuf, &linesize, &filesize, &cnt, ibuf, 0) == NULL) {
  246          self.m_xsize = self.m_size;
  247          self.m_xlines = self.m_lines;
  248          self.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
  249          if (selfcnt > 0)
  250             message_append(&self);
  251          message_append_null();
  252          if (linebuf != NULL)
  253             n_free(linebuf);
  254          break;
  255       }
  256 
  257 #ifdef notdef
  258       if (linebuf[0] == '\0')
  259          linebuf[0] = '.';
  260 #endif
  261       /* XXX Convert CRLF to LF; this should be rethought in that
  262        * XXX CRLF input should possibly end as CRLF output? */
  263       if (cnt >= 2 && linebuf[cnt - 1] == '\n' && linebuf[cnt - 2] == '\r')
  264          linebuf[--cnt - 1] = '\n';
  265       fwrite(linebuf, sizeof *linebuf, cnt, mb.mb_otf);
  266       if (ferror(mb.mb_otf)) {
  267          n_perr(_("/tmp"), 0);
  268          exit(n_EXIT_ERR);
  269       }
  270       if (linebuf[cnt - 1] == '\n')
  271          linebuf[cnt - 1] = '\0';
  272       /* TODO In v15 this should use a/the flat MIME parser in order to ignore
  273        * TODO "From " when MIME boundaries are active -- whereas this opens
  274        * TODO another can of worms, it very likely is better than messing up
  275        * TODO MIME because of a "From " line! */
  276       if (maybe && linebuf[0] == 'F' &&
  277             (from_ = is_head(linebuf, cnt, TRU1)) &&
  278             (from_ == TRU1 || !need_rfc4155)) {
  279          /* TODO char date[n_FROM_DATEBUF];
  280           * TODO extract_date_from_from_(linebuf, cnt, date);
  281           * TODO self.m_time = 10000; */
  282          if (from_ == TRUM1) {
  283             if (n_poption & n_PO_D_V)
  284                n_err(_("Invalid MBOX \"From_ line\": %.*s\n"),
  285                   (int)cnt, linebuf);
  286             else if (!(mb.mb_active & MB_FROM__WARNED))
  287                n_err(_("MBOX mailbox contains non-conforming From_ line(s)!\n"
  288                   "  Message boundaries may have been falsely detected!\n"
  289                   "  Setting variable *mbox-rfc4155* and reopen should improve "
  290                      "the result.\n"
  291                   "  If so, make changes permanent: \"copy * SOME-FILE\".  "
  292                      "Then unset *mbox-rfc4155*\n"));
  293             mb.mb_active |= MB_FROM__WARNED;
  294          }
  295          self.m_xsize = self.m_size;
  296          self.m_xlines = self.m_lines;
  297          self.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
  298          if (selfcnt++ > 0)
  299             message_append(&self);
  300          msgCount++;
  301          self.m_flag = MUSED | MNEW | MNEWEST;
  302          self.m_size = 0;
  303          self.m_lines = 0;
  304          self.m_block = mailx_blockof(offset);
  305          self.m_offset = mailx_offsetof(offset);
  306          inhead = TRU1;
  307       } else if (linebuf[0] == 0) {
  308          inhead = FAL0;
  309       } else if (inhead) {
  310          for (cp = linebuf, cp2 = "status";; ++cp) {
  311             if ((c = *cp2++) == 0) {
  312                while (c = *cp++, whitechar(c))
  313                   ;
  314                if (cp[-1] != ':')
  315                   break;
  316                while ((c = *cp++) != '\0')
  317                   if (c == 'R')
  318                      self.m_flag |= MREAD;
  319                   else if (c == 'O')
  320                      self.m_flag &= ~MNEW;
  321                break;
  322             }
  323             if (*cp != c && *cp != upperconv(c))
  324                break;
  325          }
  326          for (cp = linebuf, cp2 = "x-status";; ++cp) {
  327             if ((c = *cp2++) == 0) {
  328                while ((c = *cp++, whitechar(c)))
  329                   ;
  330                if (cp[-1] != ':')
  331                   break;
  332                while ((c = *cp++) != '\0')
  333                   if (c == 'F')
  334                      self.m_flag |= MFLAGGED;
  335                   else if (c == 'A')
  336                      self.m_flag |= MANSWERED;
  337                   else if (c == 'T')
  338                      self.m_flag |= MDRAFTED;
  339                break;
  340             }
  341             if (*cp != c && *cp != upperconv(c))
  342                break;
  343          }
  344       }
  345       offset += cnt;
  346       self.m_size += cnt;
  347       ++self.m_lines;
  348       maybe = (linebuf[0] == 0);
  349    }
  350    NYD_LEAVE;
  351 }
  352 
  353 FL int
  354 setfile(char const *name, enum fedit_mode fm) /* TODO oh my god */
  355 {
  356    /* TODO This all needs to be converted to URL:: and Mailbox:: */
  357    static int shudclob;
  358 
  359    struct stat stb;
  360    size_t offset;
  361    char const *who, *orig_name;
  362    int rv, omsgCount = 0;
  363    FILE *ibuf = NULL, *lckfp = NULL;
  364    bool_t isdevnull = FAL0;
  365    NYD_ENTER;
  366 
  367    n_pstate &= ~n_PS_SETFILE_OPENED;
  368 
  369    /* C99 */{
  370       enum fexp_mode fexpm;
  371 
  372       if((who = shortcut_expand(name)) != NULL){
  373          fexpm = FEXP_NSHORTCUT/* XXX | FEXP_NSHELL*/;
  374          name = who;
  375       }else
  376          fexpm = FEXP_NSHELL;
  377 
  378       if(name[0] == '%'){
  379          char const *cp;
  380 
  381          fm |= FEDIT_SYSBOX; /* TODO fexpand() needs to tell is-valid-user! */
  382          if(*(who = &name[1]) == ':')
  383             ++who;
  384          if((cp = strrchr(who, '/')) != NULL)
  385             who = &cp[1];
  386          if(*who == '\0')
  387             goto jlogname;
  388       }else{
  389 jlogname:
  390          if(fm & FEDIT_ACCOUNT){
  391             if((who = ok_vlook(account)) == NULL)
  392                who = ACCOUNT_NULL;
  393             who = savecatsep(_("account"), ' ', who);
  394          }else
  395             who = ok_vlook(LOGNAME);
  396       }
  397 
  398       if ((name = fexpand(name, fexpm)) == NULL)
  399          goto jem1;
  400    }
  401 
  402    /* For at least substdate() users TODO -> eventloop tick */
  403    time_current_update(&time_current, FAL0);
  404 
  405    switch (which_protocol(orig_name = name, TRU1, TRU1, &name)) {
  406    case PROTO_FILE:
  407       isdevnull = ((n_poption & n_PO_BATCH_FLAG) &&
  408             !strcmp(name, n_path_devnull));
  409 #ifdef HAVE_REALPATH
  410       do { /* TODO we need objects, goes away then */
  411 # ifdef HAVE_REALPATH_NULL
  412          char *cp;
  413 
  414          if ((cp = realpath(name, NULL)) != NULL) {
  415             name = savestr(cp);
  416             (free)(cp);
  417          }
  418 # else
  419          char cbuf[PATH_MAX];
  420 
  421          if (realpath(name, cbuf) != NULL)
  422             name = savestr(cbuf);
  423 # endif
  424       } while (0);
  425 #endif
  426       rv = 1;
  427       break;
  428 #ifdef HAVE_MAILDIR
  429    case PROTO_MAILDIR:
  430       shudclob = 1;
  431       rv = maildir_setfile(who, name, fm);
  432       goto jleave;
  433 #endif
  434 #ifdef HAVE_POP3
  435    case PROTO_POP3:
  436       shudclob = 1;
  437       rv = pop3_setfile(who, orig_name, fm);
  438       goto jleave;
  439 #endif
  440 #ifdef HAVE_IMAP
  441    case PROTO_IMAP:
  442       shudclob = 1;
  443       if((fm & FEDIT_NEWMAIL) && mb.mb_type == MB_CACHE)
  444          rv = 1;
  445       else
  446          rv = imap_setfile(who, orig_name, fm);
  447       goto jleave;
  448 #endif
  449    default:
  450       n_err(_("Cannot handle protocol: %s\n"), orig_name);
  451       goto jem1;
  452    }
  453 
  454    if ((ibuf = n_fopen_any(savecat("file://", name), "r", NULL)) == NULL) {
  455       int e = n_err_no;
  456 
  457       if ((fm & FEDIT_SYSBOX) && e == n_ERR_NOENT) {
  458          if (!(fm & FEDIT_ACCOUNT) && strcmp(who, ok_vlook(LOGNAME)) &&
  459                getpwnam(who) == NULL) {
  460             n_err(_("%s is not a user of this system\n"),
  461                n_shexp_quote_cp(who, FAL0));
  462             goto jem2;
  463          }
  464          if (!(n_poption & n_PO_QUICKRUN_MASK) && ok_blook(bsdcompat))
  465             n_err(_("No mail for %s at %s\n"),
  466                who, n_shexp_quote_cp(name, FAL0));
  467       }
  468       if (fm & FEDIT_NEWMAIL)
  469          goto jleave;
  470 
  471       if(mb.mb_digmsg != NULL)
  472          n_dig_msg_on_mailbox_close(&mb);
  473       mb.mb_type = MB_VOID;
  474 
  475       if (ok_blook(emptystart)) {
  476          if (!(n_poption & n_PO_QUICKRUN_MASK) && !ok_blook(bsdcompat))
  477             n_perr(name, e);
  478          /* We must avoid returning -1 and causing program exit */
  479          rv = 1;
  480          goto jleave;
  481       }
  482       n_perr(name, e);
  483       goto jem1;
  484    }
  485 
  486    if (fstat(fileno(ibuf), &stb) == -1) {
  487       if (fm & FEDIT_NEWMAIL)
  488          goto jleave;
  489       n_perr(_("fstat"), 0);
  490       goto jem1;
  491    }
  492 
  493    if (S_ISREG(stb.st_mode) || isdevnull) {
  494       /* EMPTY */
  495    } else {
  496       if (fm & FEDIT_NEWMAIL)
  497          goto jleave;
  498       n_err_no = S_ISDIR(stb.st_mode) ? n_ERR_ISDIR : n_ERR_INVAL;
  499       n_perr(name, 0);
  500       goto jem1;
  501    }
  502 
  503    if (shudclob && !(fm & FEDIT_NEWMAIL) && !quit(FAL0))
  504       goto jem2;
  505 
  506    hold_sigs();
  507 
  508 #ifdef HAVE_SOCKETS
  509    if (!(fm & FEDIT_NEWMAIL) && mb.mb_sock.s_fd >= 0)
  510       sclose(&mb.mb_sock); /* TODO sorry? VMAILFS->close(), thank you */
  511 #endif
  512 
  513    /* TODO There is no intermediate VOID box we've switched to: name may
  514     * TODO point to the same box that we just have written, so any updates
  515     * TODO we won't see!  Reopen again in this case.  RACY! Goes with VOID! */
  516    /* TODO In addition: in case of compressed/hook boxes we lock a temporary! */
  517    /* TODO We may uselessly open twice but quit() doesn't say whether we were
  518     * TODO modified so we can't tell: Mailbox::is_modified() :-(( */
  519    if (/*shudclob && !(fm & FEDIT_NEWMAIL) &&*/ !strcmp(name, mailname)) {
  520       name = mailname;
  521       Fclose(ibuf);
  522 
  523       if ((ibuf = n_fopen_any(name, "r", NULL)) == NULL ||
  524             fstat(fileno(ibuf), &stb) == -1 ||
  525             (!S_ISREG(stb.st_mode) && !isdevnull)) {
  526          n_perr(name, 0);
  527          rele_sigs();
  528          goto jem2;
  529       }
  530    }
  531 
  532    /* Copy the messages into /tmp and set pointers */
  533    if (!(fm & FEDIT_NEWMAIL)) {
  534       if (isdevnull) {
  535          mb.mb_type = MB_VOID;
  536          mb.mb_perm = 0;
  537       } else {
  538          mb.mb_type = MB_FILE;
  539          mb.mb_perm = (((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY) ||
  540                access(name, W_OK) < 0) ? 0 : MB_DELE | MB_EDIT);
  541          if (shudclob) {
  542             if (mb.mb_itf) {
  543                fclose(mb.mb_itf);
  544                mb.mb_itf = NULL;
  545             }
  546             if (mb.mb_otf) {
  547                fclose(mb.mb_otf);
  548                mb.mb_otf = NULL;
  549             }
  550          }
  551       }
  552       shudclob = 1;
  553       if (fm & FEDIT_SYSBOX)
  554          n_pstate &= ~n_PS_EDIT;
  555       else
  556          n_pstate |= n_PS_EDIT;
  557       initbox(name);
  558       offset = 0;
  559    } else {
  560       fseek(mb.mb_otf, 0L, SEEK_END);
  561       /* TODO Doing this without holding a lock is.. And not err checking.. */
  562       fseek(ibuf, mailsize, SEEK_SET);
  563       offset = mailsize;
  564       omsgCount = msgCount;
  565    }
  566 
  567    if (isdevnull)
  568       lckfp = (FILE*)-1;
  569    else if (!(n_pstate & n_PS_EDIT))
  570       lckfp = n_dotlock(name, fileno(ibuf), FLT_READ, offset,0,
  571             (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX));
  572    else if (n_file_lock(fileno(ibuf), FLT_READ, offset,0,
  573          (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX)))
  574       lckfp = (FILE*)-1;
  575 
  576    if (lckfp == NULL) {
  577       if (!(fm & FEDIT_NEWMAIL)) {
  578          char const *emsg = (n_pstate & n_PS_EDIT)
  579                ? N_("Unable to lock mailbox, aborting operation")
  580                : N_("Unable to (dot) lock mailbox, aborting operation");
  581          n_perr(V_(emsg), 0);
  582       }
  583       rele_sigs();
  584       if (!(fm & FEDIT_NEWMAIL))
  585          goto jem1;
  586       goto jleave;
  587    }
  588 
  589    mailsize = fsize(ibuf);
  590 
  591    /* TODO This is too simple minded?  We should regenerate an index file
  592     * TODO to be able to truly tell whether *anything* has changed! */
  593    if ((fm & FEDIT_NEWMAIL) && UICMP(z, mailsize, <=, offset)) {
  594       rele_sigs();
  595       goto jleave;
  596    }
  597    a_folder_mbox_setptr(ibuf, offset);
  598    setmsize(msgCount);
  599    if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted) {
  600       mb.mb_threaded = 0;
  601       c_sort((void*)-1);
  602    }
  603 
  604    Fclose(ibuf);
  605    ibuf = NULL;
  606    if (lckfp != NULL && lckfp != (FILE*)-1) {
  607       Pclose(lckfp, FAL0);
  608       /*lckfp = NULL;*/
  609    }
  610 
  611    if (!(fm & FEDIT_NEWMAIL)) {
  612       n_pstate &= ~n_PS_SAW_COMMAND;
  613       n_pstate |= n_PS_SETFILE_OPENED;
  614    }
  615 
  616    rele_sigs();
  617 
  618    rv = (msgCount == 0);
  619    if(rv)
  620       n_err_no = n_ERR_NODATA;
  621 
  622    if(n_poption & n_PO_EXISTONLY)
  623       goto jleave;
  624 
  625    if(rv){
  626       if(!(n_pstate & n_PS_EDIT) || (fm & FEDIT_NEWMAIL)){
  627          if(!(fm & FEDIT_NEWMAIL)){
  628             if (!ok_blook(emptystart))
  629                n_err(_("No mail for %s at %s\n"),
  630                   who, n_shexp_quote_cp(name, FAL0));
  631          }
  632          goto jleave;
  633       }
  634    }
  635 
  636    if(fm & FEDIT_NEWMAIL)
  637       newmailinfo(omsgCount);
  638 jleave:
  639    if (ibuf != NULL) {
  640       Fclose(ibuf);
  641       if (lckfp != NULL && lckfp != (FILE*)-1)
  642          Pclose(lckfp, FAL0);
  643    }
  644    NYD_LEAVE;
  645    return rv;
  646 jem2:
  647    if(mb.mb_digmsg != NULL)
  648       n_dig_msg_on_mailbox_close(&mb);
  649    mb.mb_type = MB_VOID;
  650 jem1:
  651    n_err_no = n_ERR_NOTOBACCO;
  652    rv = -1;
  653    goto jleave;
  654 }
  655 
  656 FL int
  657 newmailinfo(int omsgCount)
  658 {
  659    int mdot, i;
  660    NYD_ENTER;
  661 
  662    for (i = 0; i < omsgCount; ++i)
  663       message[i].m_flag &= ~MNEWEST;
  664 
  665    if (msgCount > omsgCount) {
  666       for (i = omsgCount; i < msgCount; ++i)
  667          message[i].m_flag |= MNEWEST;
  668       fprintf(n_stdout, _("New mail has arrived.\n"));
  669       if ((i = msgCount - omsgCount) == 1)
  670          fprintf(n_stdout, _("Loaded 1 new message.\n"));
  671       else
  672          fprintf(n_stdout, _("Loaded %d new messages.\n"), i);
  673    } else
  674       fprintf(n_stdout, _("Loaded %d messages.\n"), msgCount);
  675 
  676    temporary_folder_hook_check(TRU1);
  677 
  678    mdot = getmdot(1);
  679 
  680    if(ok_blook(header) && (i = omsgCount + 1) <= msgCount){
  681 #ifdef HAVE_IMAP
  682       if(mb.mb_type == MB_IMAP)
  683          imap_getheaders(i, msgCount); /* TODO not here */
  684 #endif
  685       for(omsgCount = 0; i <= msgCount; ++omsgCount, ++i)
  686          n_msgvec[omsgCount] = i;
  687       n_msgvec[omsgCount] = 0;
  688       print_headers(n_msgvec, FAL0, FAL0);
  689    }
  690    NYD_LEAVE;
  691    return mdot;
  692 }
  693 
  694 FL void
  695 setmsize(int sz)
  696 {
  697    NYD_ENTER;
  698    if (n_msgvec != NULL)
  699       n_free(n_msgvec);
  700    n_msgvec = n_calloc(sz +1, sizeof *n_msgvec);
  701    NYD_LEAVE;
  702 }
  703 
  704 FL void
  705 print_header_summary(char const *Larg)
  706 {
  707    size_t i;
  708    NYD_ENTER;
  709 
  710    getmdot(0);
  711 #ifdef HAVE_IMAP
  712       if(mb.mb_type == MB_IMAP)
  713          imap_getheaders(0, msgCount); /* TODO not here */
  714 #endif
  715    assert(n_msgvec != NULL);
  716 
  717    if (Larg != NULL) {
  718       /* Avoid any messages XXX add a make_mua_silent() and use it? */
  719       if ((n_poption & (n_PO_VERB | n_PO_EXISTONLY)) == n_PO_EXISTONLY) {
  720          n_stdout = freopen(n_path_devnull, "w", stdout);
  721          n_stderr = freopen(n_path_devnull, "w", stderr);
  722       }
  723       i = (n_getmsglist(n_shexp_quote_cp(Larg, FAL0), n_msgvec, 0, NULL) <= 0);
  724       if (n_poption & n_PO_EXISTONLY)
  725          n_exit_status = (int)i;
  726       else if(i == 0)
  727          print_headers(n_msgvec, TRU1, FAL0); /* TODO should be iterator! */
  728    } else {
  729       i = 0;
  730       if(!mb.mb_threaded){
  731          for(; UICMP(z, i, <, msgCount); ++i)
  732             n_msgvec[i] = i + 1;
  733       }else{
  734          struct message *mp;
  735 
  736          for(mp = threadroot; mp; ++i, mp = next_in_thread(mp))
  737             n_msgvec[i] = (int)PTR2SIZE(mp - message + 1);
  738       }
  739       print_headers(n_msgvec, FAL0, TRU1); /* TODO should be iterator! */
  740    }
  741    NYD_LEAVE;
  742 }
  743 
  744 FL void
  745 n_folder_announce(enum n_announce_flags af){
  746    int vec[2], mdot;
  747    NYD_ENTER;
  748 
  749    mdot = (mb.mb_type == MB_VOID) ? 1 : getmdot(0);
  750    dot = &message[mdot - 1];
  751 
  752    if(af != n_ANNOUNCE_NONE && ok_blook(header) &&
  753          ((af & n_ANNOUNCE_MAIN_CALL) ||
  754           ((af & n_ANNOUNCE_CHANGE) && !ok_blook(posix))))
  755       af |= n_ANNOUNCE_STATUS | n__ANNOUNCE_HEADER;
  756 
  757    if(af & n_ANNOUNCE_STATUS){
  758       a_folder_info();
  759       af |= n__ANNOUNCE_ANY;
  760    }
  761 
  762    if(af & n__ANNOUNCE_HEADER){
  763       if(!(af & n_ANNOUNCE_MAIN_CALL) && ok_blook(bsdannounce))
  764          n_OBSOLETE(_("*bsdannounce* is now default behaviour"));
  765       vec[0] = mdot;
  766       vec[1] = 0;
  767       print_header_group(vec); /* XXX errors? */
  768       af |= n__ANNOUNCE_ANY;
  769    }
  770 
  771    if(af & n__ANNOUNCE_ANY)
  772       fflush(n_stdout);
  773    NYD_LEAVE;
  774 }
  775 
  776 FL int
  777 getmdot(int nmail)
  778 {
  779    struct message *mp;
  780    char *cp;
  781    int mdot;
  782    enum mflag avoid = MHIDDEN | MDELETED;
  783    NYD_ENTER;
  784 
  785    if (!nmail) {
  786       if (ok_blook(autothread)) {
  787          n_OBSOLETE(_("please use *autosort=thread* instead of *autothread*"));
  788          c_thread(NULL);
  789       } else if ((cp = ok_vlook(autosort)) != NULL) {
  790          if (mb.mb_sorted != NULL)
  791             n_free(mb.mb_sorted);
  792          mb.mb_sorted = sstrdup(cp);
  793          c_sort(NULL);
  794       }
  795    }
  796    if (mb.mb_type == MB_VOID) {
  797       mdot = 1;
  798       goto jleave;
  799    }
  800 
  801    if (nmail)
  802       for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
  803          if ((mp->m_flag & (MNEWEST | avoid)) == MNEWEST)
  804             break;
  805 
  806    if (!nmail || PTRCMP(mp, >=, message + msgCount)) {
  807       if (mb.mb_threaded) {
  808          for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
  809             if ((mp->m_flag & (MNEW | avoid)) == MNEW)
  810                break;
  811       } else {
  812          for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
  813             if ((mp->m_flag & (MNEW | avoid)) == MNEW)
  814                break;
  815       }
  816    }
  817 
  818    if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
  819       if (mb.mb_threaded) {
  820          for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
  821             if (mp->m_flag & MFLAGGED)
  822                break;
  823       } else {
  824          for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
  825             if (mp->m_flag & MFLAGGED)
  826                break;
  827       }
  828    }
  829 
  830    if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
  831       if (mb.mb_threaded) {
  832          for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
  833             if (!(mp->m_flag & (MREAD | avoid)))
  834                break;
  835       } else {
  836          for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
  837             if (!(mp->m_flag & (MREAD | avoid)))
  838                break;
  839       }
  840    }
  841 
  842    if (nmail &&
  843          (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
  844       mdot = (int)PTR2SIZE(mp - message + 1);
  845    else if (ok_blook(showlast)) {
  846       if (mb.mb_threaded) {
  847          for (mp = this_in_thread(threadroot, -1); mp;
  848                mp = prev_in_thread(mp))
  849             if (!(mp->m_flag & avoid))
  850                break;
  851          mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
  852       } else {
  853          for (mp = message + msgCount - 1; mp >= message; --mp)
  854             if (!(mp->m_flag & avoid))
  855                break;
  856          mdot = (mp >= message) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
  857       }
  858    } else if (!nmail &&
  859          (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
  860       mdot = (int)PTR2SIZE(mp - message + 1);
  861    else if (mb.mb_threaded) {
  862       for (mp = threadroot; mp; mp = next_in_thread(mp))
  863          if (!(mp->m_flag & avoid))
  864             break;
  865       mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : 1;
  866    } else {
  867       for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
  868          if (!(mp->m_flag & avoid))
  869             break;
  870       mdot = PTRCMP(mp, <, message + msgCount)
  871             ? (int)PTR2SIZE(mp - message + 1) : 1;
  872    }
  873 jleave:
  874    NYD_LEAVE;
  875    return mdot;
  876 }
  877 
  878 FL void
  879 initbox(char const *name)
  880 {
  881    bool_t err;
  882    char *tempMesg;
  883    NYD_ENTER;
  884 
  885    if (mb.mb_type != MB_VOID)
  886       n_strscpy(prevfile, mailname, PATH_MAX);
  887 
  888    /* TODO name always NE mailname (but goes away for objects anyway)
  889     * TODO Well, not true no more except that in parens */
  890    _update_mailname((name != mailname) ? name : NULL);
  891 
  892    err = FAL0;
  893    if((mb.mb_otf = Ftmp(&tempMesg, "tmpmbox", OF_WRONLY | OF_HOLDSIGS)) ==
  894          NULL){
  895       n_perr(_("initbox: temporary mail message file, writer"), 0);
  896       err = TRU1;
  897    }else if((mb.mb_itf = safe_fopen(tempMesg, "r", NULL)) == NULL) {
  898       n_perr(_("initbox: temporary mail message file, reader"), 0);
  899       err = TRU1;
  900    }
  901    Ftmp_release(&tempMesg);
  902    if(err)
  903       exit(n_EXIT_ERR);
  904 
  905    message_reset();
  906    mb.mb_active = MB_NONE;
  907    mb.mb_threaded = 0;
  908 #ifdef HAVE_IMAP
  909    mb.mb_flags = MB_NOFLAGS;
  910 #endif
  911    if (mb.mb_sorted != NULL) {
  912       n_free(mb.mb_sorted);
  913       mb.mb_sorted = NULL;
  914    }
  915    dot = prevdot = threadroot = NULL;
  916    n_pstate &= ~n_PS_DID_PRINT_DOT;
  917    NYD_LEAVE;
  918 }
  919 
  920 FL char const *
  921 n_folder_query(void){
  922    struct n_string s, *sp = &s;
  923    enum protocol proto;
  924    char *cp;
  925    char const *rv, *adjcp;
  926    bool_t err;
  927    NYD_ENTER;
  928 
  929    sp = n_string_creat_auto(sp);
  930 
  931    /* *folder* is linked with *folder_resolved*: we only use the latter */
  932    for(err = FAL0;;){
  933       if((rv = ok_vlook(folder_resolved)) != NULL)
  934          break;
  935 
  936       /* POSIX says:
  937        *    If directory does not start with a <slash> ('/'), the contents
  938        *    of HOME shall be prefixed to it.
  939        * And:
  940        *    If folder is unset or set to null, [.] filenames beginning with
  941        *    '+' shall refer to files in the current directory.
  942        * We may have the result already */
  943       rv = n_empty;
  944       err = FAL0;
  945 
  946       if((cp = ok_vlook(folder)) == NULL)
  947          goto jset;
  948 
  949       /* Expand the *folder*; skip %: prefix for simplicity of use */
  950       if(cp[0] == '%' && cp[1] == ':')
  951          cp += 2;
  952       if((err = (cp = fexpand(cp, FEXP_NSPECIAL | FEXP_NFOLDER | FEXP_NSHELL)
  953             ) == NULL) || *cp == '\0')
  954          goto jset;
  955       else{
  956          size_t i;
  957 
  958          for(i = strlen(cp);;){
  959             if(--i == 0)
  960                goto jset;
  961             if(cp[i] != '/'){
  962                cp[++i] = '\0';
  963                break;
  964             }
  965          }
  966       }
  967 
  968       switch((proto = which_protocol(cp, FAL0, FAL0, &adjcp))){
  969       case PROTO_POP3:
  970          n_err(_("*folder* can't be set to a flat, read-only POP3 account\n"));
  971          err = TRU1;
  972          goto jset;
  973       case PROTO_IMAP:
  974 #ifdef HAVE_IMAP
  975          rv = cp;
  976          if(!strcmp(rv, protbase(rv)))
  977             rv = savecatsep(rv, '/', n_empty);
  978 #else
  979          n_err(_("*folder*: IMAP support not compiled in\n"));
  980          err = TRU1;
  981 #endif
  982          goto jset;
  983       default:
  984          /* Further expansion desired */
  985          break;
  986       }
  987 
  988       /* Prefix HOME as necessary */
  989       if(*adjcp != '/'){ /* XXX path_is_absolute() */
  990          size_t l1, l2;
  991          char const *home;
  992 
  993          home = ok_vlook(HOME);
  994          l1 = strlen(home);
  995          l2 = strlen(cp);
  996 
  997          sp = n_string_reserve(sp, l1 + 1 + l2 +1);
  998          if(cp != adjcp){
  999             size_t i;
 1000 
 1001             sp = n_string_push_buf(sp, cp, i = PTR2SIZE(adjcp - cp));
 1002             cp += i;
 1003             l2 -= i;
 1004          }
 1005          sp = n_string_push_buf(sp, home, l1);
 1006          sp = n_string_push_c(sp, '/');
 1007          sp = n_string_push_buf(sp, cp, l2);
 1008          cp = n_string_cp(sp);
 1009          sp = n_string_drop_ownership(sp);
 1010       }
 1011 
 1012       /* TODO Since our visual mailname is resolved via realpath(3) if available
 1013        * TODO to avoid that we loose track of our currently open folder in case
 1014        * TODO we chdir away, but still checks the leading path portion against
 1015        * TODO n_folder_query() to be able to abbreviate to the +FOLDER syntax if
 1016        * TODO possible, we need to realpath(3) the folder, too */
 1017 #ifndef HAVE_REALPATH
 1018       rv = cp;
 1019 #else
 1020       assert(sp->s_len == 0 && sp->s_dat == NULL);
 1021 # ifndef HAVE_REALPATH_NULL
 1022       sp = n_string_reserve(sp, PATH_MAX +1);
 1023 # endif
 1024 
 1025       if((sp->s_dat = realpath(cp, sp->s_dat)) != NULL){
 1026 # ifdef HAVE_REALPATH_NULL
 1027          n_string_cp(sp = n_string_assign_cp(sp, cp = sp->s_dat));
 1028          (free)(cp);
 1029 # endif
 1030          rv = sp->s_dat;
 1031       }else if(n_err_no == n_ERR_NOENT)
 1032          rv = cp;
 1033       else{
 1034          n_err(_("Can't canonicalize *folder*: %s\n"),
 1035             n_shexp_quote_cp(cp, FAL0));
 1036          err = TRU1;
 1037          rv = n_empty;
 1038       }
 1039       sp = n_string_drop_ownership(sp);
 1040 #endif /* HAVE_REALPATH */
 1041 
 1042       /* Always append a solidus to our result path upon success */
 1043       if(!err){
 1044          size_t i;
 1045 
 1046          if(rv[(i = strlen(rv)) - 1] != '/'){
 1047             sp = n_string_reserve(sp, i + 1 +1);
 1048             sp = n_string_push_buf(sp, rv, i);
 1049             sp = n_string_push_c(sp, '/');
 1050             rv = n_string_cp(sp);
 1051             sp = n_string_drop_ownership(sp);
 1052          }
 1053       }
 1054 
 1055 jset:
 1056       n_PS_ROOT_BLOCK(ok_vset(folder_resolved, rv));
 1057    }
 1058 
 1059    if(err){
 1060       n_err(_("*folder* is not resolvable, using CWD\n"));
 1061       assert(rv != NULL && *rv == '\0');
 1062    }
 1063    NYD_LEAVE;
 1064    return rv;
 1065 }
 1066 
 1067 FL int
 1068 n_folder_mbox_prepare_append(FILE *fout, struct stat *st_or_null){
 1069    /* TODO n_folder_mbox_prepare_append -> Mailbox->append() */
 1070    struct stat stb;
 1071    char buf[2];
 1072    int rv;
 1073    bool_t needsep;
 1074    NYD2_ENTER;
 1075 
 1076    if(!fseek(fout, -2L, SEEK_END)){
 1077       if(fread(buf, sizeof *buf, 2, fout) != 2)
 1078          goto jerrno;
 1079       needsep = (buf[0] != '\n' || buf[1] != '\n');
 1080    }else{
 1081       rv = n_err_no;
 1082 
 1083       if(st_or_null == NULL){
 1084          st_or_null = &stb;
 1085          if(fstat(fileno(fout), st_or_null))
 1086             goto jerrno;
 1087       }
 1088 
 1089       if(st_or_null->st_size >= 2)
 1090          goto jleave;
 1091       if(st_or_null->st_size == 0){
 1092          rv = n_ERR_NONE;
 1093          goto jleave;
 1094       }
 1095 
 1096       if(fseek(fout, -1L, SEEK_END))
 1097          goto jerrno;
 1098       if(fread(buf, sizeof *buf, 1, fout) != 1)
 1099          goto jerrno;
 1100       needsep = (buf[0] != '\n');
 1101    }
 1102 
 1103    rv = n_ERR_NONE;
 1104    if(fflush(fout) || (needsep && putc('\n', fout) == EOF))
 1105 jerrno:
 1106       rv = n_err_no;
 1107 jleave:
 1108    NYD2_LEAVE;
 1109    return rv;
 1110 }
 1111 
 1112 /* s-it-mode */