"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/cmd-headers.c" (25 Mar 2018, 32760 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 "cmd-headers.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  *@ Header display, search, etc., related user commands.
    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 cmd_headers
   37 
   38 #ifndef HAVE_AMALGAMATION
   39 # include "nail.h"
   40 #endif
   41 
   42 static int        _screen;
   43 
   44 /* ... And place the extracted date in `date' */
   45 static void    _parse_from_(struct message *mp, char date[n_FROM_DATEBUF]);
   46 
   47 /* Print out the header of a specific message
   48  * a_cmd__hprf: handle *headline*
   49  * a_cmd__subject: -1 if Subject: yet seen, otherwise smalloc()d Subject:
   50  * a_cmd__putindent: print out the indenting in threaded display
   51  * a_cmd__putuc: print out a Unicode character or a substitute for it, return
   52  *    0 on error or wcwidth() (or 1) on success */
   53 static void a_cmd_print_head(size_t yetprinted, size_t msgno, FILE *f,
   54                   bool_t threaded);
   55 
   56 static void a_cmd__hprf(size_t yetprinted, char const *fmt, size_t msgno,
   57                FILE *f, bool_t threaded, char const *attrlist);
   58 static char *a_cmd__subject(struct message *mp, bool_t threaded,
   59                size_t yetprinted);
   60 static int a_cmd__putindent(FILE *fp, struct message *mp, int maxwidth);
   61 static size_t a_cmd__putuc(int u, int c, FILE *fp);
   62 static int a_cmd__dispc(struct message *mp, char const *a);
   63 
   64 /* Shared `z' implementation */
   65 static int a_cmd_scroll(char const *arg, bool_t onlynew);
   66 
   67 /* Shared `headers' implementation */
   68 static int     _headers(int msgspec);
   69 
   70 static void
   71 _parse_from_(struct message *mp, char date[n_FROM_DATEBUF]) /* TODO line pool */
   72 {
   73    FILE *ibuf;
   74    int hlen;
   75    char *hline = NULL;
   76    size_t hsize = 0;
   77    NYD_ENTER;
   78 
   79    if ((ibuf = setinput(&mb, mp, NEED_HEADER)) != NULL &&
   80          (hlen = readline_restart(ibuf, &hline, &hsize, 0)) > 0)
   81       extract_date_from_from_(hline, hlen, date);
   82    if (hline != NULL)
   83       free(hline);
   84    NYD_LEAVE;
   85 }
   86 
   87 static void
   88 a_cmd_print_head(size_t yetprinted, size_t msgno, FILE *f, bool_t threaded){
   89    enum {attrlen = 14};
   90    char attrlist[attrlen +1], *cp;
   91    char const *fmt;
   92    NYD2_ENTER;
   93 
   94    if((cp = ok_vlook(attrlist)) != NULL){
   95       if(strlen(cp) == attrlen){
   96          memcpy(attrlist, cp, attrlen +1);
   97          goto jattrok;
   98       }
   99       n_err(_("*attrlist* is not of the correct length, using built-in\n"));
  100    }
  101 
  102    if(ok_blook(bsdcompat) || ok_blook(bsdflags)){
  103       char const bsdattr[attrlen +1] = "NU  *HMFAT+-$~";
  104 
  105       memcpy(attrlist, bsdattr, sizeof bsdattr);
  106    }else if(ok_blook(SYSV3)){
  107       char const bsdattr[attrlen +1] = "NU  *HMFAT+-$~";
  108 
  109       memcpy(attrlist, bsdattr, sizeof bsdattr);
  110       n_OBSOLETE(_("*SYSV3*: please use *bsdcompat* or *bsdflags*, "
  111          "or set *attrlist*"));
  112    }else{
  113       char const pattr[attrlen +1]   = "NUROSPMFAT+-$~";
  114 
  115       memcpy(attrlist, pattr, sizeof pattr);
  116    }
  117 
  118 jattrok:
  119    if((fmt = ok_vlook(headline)) == NULL){
  120       fmt = ((ok_blook(bsdcompat) || ok_blook(bsdheadline))
  121             ? "%>%a%m %-20f  %16d %4l/%-5o %i%-S"
  122             : "%>%a%m %-18f %-16d %4l/%-5o %i%-s");
  123    }
  124 
  125    a_cmd__hprf(yetprinted, fmt, msgno, f, threaded, attrlist);
  126    NYD2_LEAVE;
  127 }
  128 
  129 static void
  130 a_cmd__hprf(size_t yetprinted, char const *fmt, size_t msgno, FILE *f,
  131    bool_t threaded, char const *attrlist)
  132 {
  133    char buf[16], datebuf[n_FROM_DATEBUF], cbuf[8], *cp, *subjline;
  134    char const *datefmt, *date, *name, *fp n_COLOUR( COMMA *colo_tag );
  135    int i, n, s, wleft, subjlen;
  136    struct message *mp;
  137    time_t datet;
  138    n_COLOUR( struct n_colour_pen *cpen_new COMMA *cpen_cur COMMA *cpen_bas; )
  139    enum {
  140       _NONE       = 0,
  141       _ISDOT      = 1<<0,
  142       _ISADDR     = 1<<1,
  143       _ISTO       = 1<<2,
  144       _IFMT       = 1<<3,
  145       _LOOP_MASK  = (1<<4) - 1,
  146       _SFMT       = 1<<4,        /* It is 'S' */
  147       /* For the simple byte-based counts in wleft and n we sometimes need
  148        * adjustments to compensate for additional bytes of UTF-8 sequences */
  149       _PUTCB_UTF8_SHIFT = 5,
  150       _PUTCB_UTF8_MASK = 3<<5
  151    } flags = _NONE;
  152    NYD2_ENTER;
  153    n_UNUSED(buf);
  154 
  155    if ((mp = message + msgno - 1) == dot)
  156       flags = _ISDOT;
  157    datet = mp->m_time;
  158    date = NULL;
  159    n_COLOUR( colo_tag = NULL; )
  160 
  161    datefmt = ok_vlook(datefield);
  162 jredo:
  163    if (datefmt != NULL) {
  164       fp = hfield1("date", mp);/* TODO use m_date field! */
  165       if (fp == NULL) {
  166          datefmt = NULL;
  167          goto jredo;
  168       }
  169       datet = rfctime(fp);
  170       date = n_time_ctime(datet, NULL);
  171       fp = ok_vlook(datefield_markout_older);
  172       i = (*datefmt != '\0');
  173       if (fp != NULL)
  174          i |= (*fp != '\0') ? 2 | 4 : 2; /* XXX no magics */
  175 
  176       /* May we strftime(3)? */
  177       if (i & (1 | 4)){
  178          /* This localtime(3) should not fail since rfctime(3).. but .. */
  179          struct tm *tmp;
  180          time_t datet2;
  181 
  182          /* TODO the datetime stuff is horror: mails should be parsed into
  183           * TODO an object tree, and date: etc. have a datetime object, which
  184           * TODO verifies upon parse time; then ALL occurrences of datetime are
  185           * TODO valid all through the program; and: to_wire, to_user! */
  186          datet2 = datet;
  187 jredo_localtime:
  188          if((tmp = localtime(&datet2)) == NULL){
  189             datet2 = 0;
  190             goto jredo_localtime;
  191          }
  192          memcpy(&time_current.tc_local, tmp, sizeof(*tmp));
  193       }
  194 
  195       if ((i & 2) &&
  196             (UICMP(64,datet, >, time_current.tc_time + n_DATE_SECSDAY) ||
  197 #define _6M ((n_DATE_DAYSYEAR / 2) * n_DATE_SECSDAY)
  198             UICMP(64,datet + _6M, <, time_current.tc_time))) {
  199 #undef _6M
  200          if ((datefmt = (i & 4) ? fp : NULL) == NULL) {
  201             memset(datebuf, ' ', n_FROM_DATEBUF); /* xxx ur */
  202             memcpy(datebuf + 4, date + 4, 7);
  203             datebuf[4 + 7] = ' ';
  204             memcpy(datebuf + 4 + 7 + 1, date + 20, 4);
  205             datebuf[4 + 7 + 1 + 4] = '\0';
  206             date = datebuf;
  207          }
  208          n_COLOUR( colo_tag = n_COLOUR_TAG_SUM_OLDER; )
  209       } else if ((i & 1) == 0)
  210          datefmt = NULL;
  211    } else if (datet == (time_t)0 && !(mp->m_flag & MNOFROM)) {
  212       /* TODO eliminate this path, query the FROM_ date in setptr(),
  213        * TODO all other codepaths do so by themselves ALREADY ?????
  214        * TODO assert(mp->m_time != 0);, then
  215        * TODO ALSO changes behaviour of datefield_markout_older */
  216       _parse_from_(mp, datebuf);
  217       date = datebuf;
  218    } else
  219       date = n_time_ctime(datet, NULL);
  220 
  221    flags |= _ISADDR;
  222    name = name1(mp, 0);
  223    if (name != NULL && ok_blook(showto) && n_is_myname(skin(name))) {
  224       if ((cp = hfield1("to", mp)) != NULL) {
  225          name = cp;
  226          flags |= _ISTO;
  227       }
  228    }
  229    if (name == NULL) {
  230       name = n_empty;
  231       flags &= ~_ISADDR;
  232    }
  233    if (flags & _ISADDR)
  234       name = ok_blook(showname) ? realname(name) : prstr(skin(name));
  235 
  236    subjline = NULL;
  237 
  238    /* Detect the width of the non-format characters in *headline*;
  239     * like that we can simply use putc() in the next loop, since we have
  240     * already calculated their column widths (TODO it's sick) */
  241    wleft = subjlen = n_scrnwidth;
  242 
  243    for (fp = fmt; *fp != '\0'; ++fp) {
  244       if (*fp == '%') {
  245          if (*++fp == '-')
  246             ++fp;
  247          else if (*fp == '+')
  248             ++fp;
  249          if (digitchar(*fp)) {
  250             n = 0;
  251             do
  252                n = 10*n + *fp - '0';
  253             while (++fp, digitchar(*fp));
  254             subjlen -= n;
  255          }
  256          if (*fp == 'i')
  257             flags |= _IFMT;
  258 
  259          if (*fp == '\0')
  260             break;
  261       } else {
  262 #ifdef HAVE_WCWIDTH
  263          if (n_mb_cur_max > 1) {
  264             wchar_t  wc;
  265             if ((s = mbtowc(&wc, fp, n_mb_cur_max)) == -1)
  266                n = s = 1;
  267             else if ((n = wcwidth(wc)) == -1)
  268                n = 1;
  269          } else
  270 #endif
  271             n = s = 1;
  272          subjlen -= n;
  273          wleft -= n;
  274          while (--s > 0)
  275             ++fp;
  276       }
  277    }
  278 
  279    /* Walk *headline*, producing output TODO not (really) MB safe */
  280 #ifdef HAVE_COLOUR
  281    if(n_COLOUR_IS_ACTIVE()){
  282       if(flags & _ISDOT)
  283          colo_tag = n_COLOUR_TAG_SUM_DOT;
  284       cpen_bas = n_colour_pen_create(n_COLOUR_ID_SUM_HEADER, colo_tag);
  285       n_colour_pen_put(cpen_new = cpen_cur = cpen_bas);
  286    }else
  287       cpen_new = cpen_bas = cpen_cur = NULL;
  288 #endif
  289 
  290    for (fp = fmt; *fp != '\0'; ++fp) {
  291       char c;
  292 
  293       if ((c = *fp & 0xFF) != '%') {
  294          n_COLOUR(
  295             if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
  296                n_colour_pen_put(cpen_cur = cpen_new);
  297          );
  298          putc(c, f);
  299          continue;
  300       }
  301 
  302       flags &= _LOOP_MASK;
  303       n = 0;
  304       s = 1;
  305       if ((c = *++fp) == '-') {
  306          s = -1;
  307          ++fp;
  308       } else if (c == '+')
  309          ++fp;
  310       if (digitchar(*fp)) {
  311          do
  312             n = 10*n + *fp - '0';
  313          while (++fp, digitchar(*fp));
  314       }
  315 
  316       if ((c = *fp & 0xFF) == '\0')
  317          break;
  318       n *= s;
  319 
  320       cbuf[1] = '\0';
  321       switch (c) {
  322       case '%':
  323          goto jputcb;
  324       case '>':
  325       case '<':
  326          if (flags & _ISDOT) {
  327             n_COLOUR(
  328                if(n_COLOUR_IS_ACTIVE())
  329                   cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_DOTMARK,
  330                         colo_tag);
  331             );
  332             if((n_psonce & n_PSO_UNICODE) && !ok_blook(headline_plain)){
  333                if (c == '>')
  334                   /* 25B8;BLACK RIGHT-POINTING SMALL TRIANGLE */
  335                   cbuf[1] = (char)0x96, cbuf[2] = (char)0xB8;
  336                else
  337                   /* 25C2;BLACK LEFT-POINTING SMALL TRIANGLE */
  338                   cbuf[1] = (char)0x97, cbuf[2] = (char)0x82;
  339                c = (char)0xE2;
  340                cbuf[3] = '\0';
  341                flags |= 2 << _PUTCB_UTF8_SHIFT;
  342             }
  343          } else
  344             c = ' ';
  345          goto jputcb;
  346       case '$':
  347 #ifdef HAVE_SPAM
  348          if (n == 0)
  349             n = 5;
  350          if (UICMP(32, n_ABS(n), >, wleft))
  351             n = (n < 0) ? -wleft : wleft;
  352          snprintf(buf, sizeof buf, "%u.%02u",
  353             (mp->m_spamscore >> 8), (mp->m_spamscore & 0xFF));
  354          n = fprintf(f, "%*s", n, buf);
  355          wleft = (n >= 0) ? wleft - n : 0;
  356          break;
  357 #else
  358          c = '?';
  359          goto jputcb;
  360 #endif
  361       case 'a':
  362          c = a_cmd__dispc(mp, attrlist);
  363 jputcb:
  364 #ifdef HAVE_COLOUR
  365          if(n_COLOUR_IS_ACTIVE()){
  366             if(cpen_new == cpen_cur)
  367                cpen_new = cpen_bas;
  368             if(cpen_new != cpen_cur)
  369                n_colour_pen_put(cpen_cur = cpen_new);
  370          }
  371 #endif
  372          if (UICMP(32, n_ABS(n), >, wleft))
  373             n = (n < 0) ? -wleft : wleft;
  374          cbuf[0] = c;
  375          n = fprintf(f, "%*s", n, cbuf);
  376          if (n >= 0) {
  377             wleft -= n;
  378             if ((n = (flags & _PUTCB_UTF8_MASK)) != 0) {
  379                n >>= _PUTCB_UTF8_SHIFT;
  380                wleft += n;
  381             }
  382          } else {
  383             wleft = 0; /* TODO I/O error.. ? break? */
  384          }
  385 #ifdef HAVE_COLOUR
  386          if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
  387             n_colour_pen_put(cpen_cur = cpen_new);
  388 #endif
  389          break;
  390       case 'd':
  391          if (datefmt != NULL) {
  392             i = strftime(datebuf, sizeof datebuf, datefmt,
  393                   &time_current.tc_local);
  394             if (i != 0)
  395                date = datebuf;
  396             else
  397                n_err(_("Ignoring date format, it is either empty or "
  398                   "excesses buffer size (%lu bytes)\n"),
  399                   (ul_i)sizeof(datebuf));
  400             datefmt = NULL;
  401          }
  402          if (n == 0)
  403             n = 16;
  404          if (UICMP(32, n_ABS(n), >, wleft))
  405             n = (n < 0) ? -wleft : wleft;
  406          n = fprintf(f, "%*.*s", n, n_ABS(n), date);
  407          wleft = (n >= 0) ? wleft - n : 0;
  408          break;
  409       case 'e':
  410          if (n == 0)
  411             n = 2;
  412          if (UICMP(32, n_ABS(n), >, wleft))
  413             n = (n < 0) ? -wleft : wleft;
  414          n = fprintf(f, "%*u", n, (threaded == 1 ? mp->m_level : 0));
  415          wleft = (n >= 0) ? wleft - n : 0;
  416          break;
  417       case 'f':
  418          if (n == 0) {
  419             n = 18;
  420             if (s < 0)
  421                n = -n;
  422          }
  423          i = n_ABS(n);
  424          if (i > wleft) {
  425             i = wleft;
  426             n = (n < 0) ? -wleft : wleft;
  427          }
  428          if (flags & _ISTO) /* XXX tr()! */
  429             i -= 3;
  430          n = fprintf(f, "%s%s", ((flags & _ISTO) ? "To " : n_empty),
  431                colalign(name, i, n, &wleft));
  432          if (n < 0)
  433             wleft = 0;
  434          else if (flags & _ISTO)
  435             wleft -= 3;
  436          break;
  437       case 'i':
  438          if (threaded) {
  439 #ifdef HAVE_COLOUR
  440             if(n_COLOUR_IS_ACTIVE()){
  441                cpen_new = n_colour_pen_create(n_COLOUR_ID_SUM_THREAD, colo_tag);
  442                if(cpen_new != cpen_cur)
  443                   n_colour_pen_put(cpen_cur = cpen_new);
  444             }
  445 #endif
  446             n = a_cmd__putindent(f, mp, n_MIN(wleft, (int)n_scrnwidth - 60));
  447             wleft = (n >= 0) ? wleft - n : 0;
  448 #ifdef HAVE_COLOUR
  449             if(n_COLOUR_IS_ACTIVE() && (cpen_new = cpen_bas) != cpen_cur)
  450                n_colour_pen_put(cpen_cur = cpen_new);
  451 #endif
  452          }
  453          break;
  454       case 'l':
  455          if (n == 0)
  456             n = 4;
  457          if (UICMP(32, n_ABS(n), >, wleft))
  458             n = (n < 0) ? -wleft : wleft;
  459          if (mp->m_xlines) {
  460             n = fprintf(f, "%*ld", n, mp->m_xlines);
  461             wleft = (n >= 0) ? wleft - n : 0;
  462          } else {
  463             n = n_ABS(n);
  464             wleft -= n;
  465             while (n-- != 0)
  466                putc(' ', f);
  467          }
  468          break;
  469       case 'm':
  470          if (n == 0) {
  471             n = 3;
  472             if (threaded)
  473                for (i = msgCount; i > 999; i /= 10)
  474                   ++n;
  475          }
  476          if (UICMP(32, n_ABS(n), >, wleft))
  477             n = (n < 0) ? -wleft : wleft;
  478          n = fprintf(f, "%*lu", n, (ul_i)msgno);
  479          wleft = (n >= 0) ? wleft - n : 0;
  480          break;
  481       case 'o':
  482          if (n == 0)
  483             n = -5;
  484          if (UICMP(32, n_ABS(n), >, wleft))
  485             n = (n < 0) ? -wleft : wleft;
  486          n = fprintf(f, "%*lu", n, (ul_i)mp->m_xsize);
  487          wleft = (n >= 0) ? wleft - n : 0;
  488          break;
  489       case 'S':
  490          flags |= _SFMT;
  491          /*FALLTHRU*/
  492       case 's':
  493          if (n == 0)
  494             n = subjlen - 2;
  495          if (n > 0 && s < 0)
  496             n = -n;
  497          if (subjlen > wleft)
  498             subjlen = wleft;
  499          if (UICMP(32, n_ABS(n), >, subjlen))
  500             n = (n < 0) ? -subjlen : subjlen;
  501          if (flags & _SFMT)
  502             n -= (n < 0) ? -2 : 2;
  503          if (n == 0)
  504             break;
  505          if (subjline == NULL)
  506             subjline = a_cmd__subject(mp, (threaded && (flags & _IFMT)),
  507                   yetprinted);
  508          if (subjline == (char*)-1) {
  509             n = fprintf(f, "%*s", n, n_empty);
  510             wleft = (n >= 0) ? wleft - n : 0;
  511          } else {
  512             n = fprintf(f, ((flags & _SFMT) ? "\"%s\"" : "%s"),
  513                   colalign(subjline, n_ABS(n), n, &wleft));
  514             if (n < 0)
  515                wleft = 0;
  516          }
  517          break;
  518       case 'T': /* Message recipient flags */
  519          switch(is_mlist_mp(mp, MLIST_OTHER)){
  520          case MLIST_OTHER: c = ' '; break;
  521          case MLIST_KNOWN: c = 'l'; break;
  522          case MLIST_SUBSCRIBED: c = 'L'; break;
  523          }
  524          goto jputcb;
  525       case 't':
  526          if (n == 0) {
  527             n = 3;
  528             if (threaded)
  529                for (i = msgCount; i > 999; i /= 10)
  530                   ++n;
  531          }
  532          if (UICMP(32, n_ABS(n), >, wleft))
  533             n = (n < 0) ? -wleft : wleft;
  534          n = fprintf(f, "%*lu",
  535                n, (threaded ? (ul_i)mp->m_threadpos : (ul_i)msgno));
  536          wleft = (n >= 0) ? wleft - n : 0;
  537          break;
  538       case 'U':
  539 #ifdef HAVE_IMAP
  540             if (n == 0)
  541                n = 9;
  542             if (UICMP(32, n_ABS(n), >, wleft))
  543                n = (n < 0) ? -wleft : wleft;
  544             n = fprintf(f, "%*" PRIu64 , n, mp->m_uid);
  545             wleft = (n >= 0) ? wleft - n : 0;
  546             break;
  547 #else
  548             c = '0';
  549             goto jputcb;
  550 #endif
  551       default:
  552          if (n_poption & n_PO_D_V)
  553             n_err(_("Unknown *headline* format: %%%c\n"), c);
  554          c = '?';
  555          goto jputcb;
  556       }
  557 
  558       if (wleft <= 0)
  559          break;
  560    }
  561 
  562    n_COLOUR( n_colour_reset(); )
  563    putc('\n', f);
  564 
  565    if (subjline != NULL && subjline != (char*)-1)
  566       free(subjline);
  567    NYD2_LEAVE;
  568 }
  569 
  570 static char *
  571 a_cmd__subject(struct message *mp, bool_t threaded, size_t yetprinted)
  572 {
  573    struct str in, out;
  574    char *rv, *ms;
  575    NYD2_ENTER;
  576 
  577    rv = (char*)-1;
  578 
  579    if ((ms = hfield1("subject", mp)) == NULL)
  580       goto jleave;
  581 
  582    in.l = strlen(in.s = ms);
  583    mime_fromhdr(&in, &out, TD_ICONV | TD_ISPR);
  584    rv = ms = out.s;
  585 
  586    if (!threaded || mp->m_level == 0)
  587       goto jleave;
  588 
  589    /* In a display thread - check whether this message uses the same
  590     * Subject: as it's parent or elder neighbour, suppress printing it if
  591     * this is the case.  To extend this a bit, ignore any leading Re: or
  592     * Fwd: plus follow-up WS.  Ignore invisible messages along the way */
  593    ms = n_UNCONST(subject_re_trim(n_UNCONST(ms)));
  594 
  595    for (; (mp = prev_in_thread(mp)) != NULL && yetprinted-- > 0;) {
  596       char *os;
  597 
  598       if (visible(mp) && (os = hfield1("subject", mp)) != NULL) {
  599          struct str oout;
  600          int x;
  601 
  602          in.l = strlen(in.s = os);
  603          mime_fromhdr(&in, &oout, TD_ICONV | TD_ISPR);
  604          x = asccasecmp(ms, subject_re_trim(oout.s));
  605          free(oout.s);
  606 
  607          if (!x) {
  608             free(out.s);
  609             rv = (char*)-1;
  610          }
  611          break;
  612       }
  613    }
  614 jleave:
  615    NYD2_LEAVE;
  616    return rv;
  617 }
  618 
  619 static int
  620 a_cmd__putindent(FILE *fp, struct message *mp, int maxwidth)/* XXX magics */
  621 {
  622    struct message *mq;
  623    int *us, indlvl, indw, i, important = MNEW | MFLAGGED;
  624    char *cs;
  625    NYD2_ENTER;
  626 
  627    if (mp->m_level == 0 || maxwidth == 0) {
  628       indw = 0;
  629       goto jleave;
  630    }
  631 
  632    cs = ac_alloc(mp->m_level);
  633    us = ac_alloc(mp->m_level * sizeof *us);
  634 
  635    i = mp->m_level - 1;
  636    if (mp->m_younger && UICMP(32, i + 1, ==, mp->m_younger->m_level)) {
  637       if (mp->m_parent && mp->m_parent->m_flag & important)
  638          us[i] = mp->m_flag & important ? 0x2523 : 0x2520;
  639       else
  640          us[i] = mp->m_flag & important ? 0x251D : 0x251C;
  641       cs[i] = '+';
  642    } else {
  643       if (mp->m_parent && mp->m_parent->m_flag & important)
  644          us[i] = mp->m_flag & important ? 0x2517 : 0x2516;
  645       else
  646          us[i] = mp->m_flag & important ? 0x2515 : 0x2514;
  647       cs[i] = '\\';
  648    }
  649 
  650    mq = mp->m_parent;
  651    for (i = mp->m_level - 2; i >= 0; --i) {
  652       if (mq) {
  653          if (UICMP(32, i, >, mq->m_level - 1)) {
  654             us[i] = cs[i] = ' ';
  655             continue;
  656          }
  657          if (mq->m_younger) {
  658             if (mq->m_parent && (mq->m_parent->m_flag & important))
  659                us[i] = 0x2503;
  660             else
  661                us[i] = 0x2502;
  662             cs[i] = '|';
  663          } else
  664             us[i] = cs[i] = ' ';
  665          mq = mq->m_parent;
  666       } else
  667          us[i] = cs[i] = ' ';
  668    }
  669 
  670    --maxwidth;
  671    for (indlvl = indw = 0; (ui8_t)indlvl < mp->m_level && indw < maxwidth;
  672          ++indlvl) {
  673       if (indw < maxwidth - 1)
  674          indw += (int)a_cmd__putuc(us[indlvl], cs[indlvl] & 0xFF, fp);
  675       else
  676          indw += (int)a_cmd__putuc(0x21B8, '^', fp);
  677    }
  678    indw += a_cmd__putuc(0x25B8, '>', fp);
  679 
  680    ac_free(us);
  681    ac_free(cs);
  682 jleave:
  683    NYD2_LEAVE;
  684    return indw;
  685 }
  686 
  687 static size_t
  688 a_cmd__putuc(int u, int c, FILE *fp){
  689    size_t rv;
  690    NYD2_ENTER;
  691    n_UNUSED(u);
  692 
  693 #ifdef HAVE_NATCH_CHAR
  694    if((n_psonce & n_PSO_UNICODE) && (u & ~(wchar_t)0177) &&
  695          !ok_blook(headline_plain)){
  696       char mbb[MB_LEN_MAX];
  697       int i, n;
  698 
  699       if((n = wctomb(mbb, u)) > 0){
  700          rv = wcwidth(u);
  701          for(i = 0; i < n; ++i)
  702             if(putc(mbb[i] & 0377, fp) == EOF){
  703                rv = 0;
  704                break;
  705             }
  706       }else if(n == 0)
  707          rv = (putc('\0', fp) != EOF);
  708       else
  709          rv = 0;
  710    }else
  711 #endif
  712       rv = (putc(c, fp) != EOF);
  713    NYD2_LEAVE;
  714    return rv;
  715 }
  716 
  717 static int
  718 a_cmd__dispc(struct message *mp, char const *a)
  719 {
  720    int i = ' ';
  721    NYD2_ENTER;
  722 
  723    if ((mp->m_flag & (MREAD | MNEW)) == MREAD)
  724       i = a[3];
  725    if ((mp->m_flag & (MREAD | MNEW)) == (MREAD | MNEW))
  726       i = a[2];
  727    if (mp->m_flag & MANSWERED)
  728       i = a[8];
  729    if (mp->m_flag & MDRAFTED)
  730       i = a[9];
  731    if ((mp->m_flag & (MREAD | MNEW)) == MNEW)
  732       i = a[0];
  733    if (!(mp->m_flag & (MREAD | MNEW)))
  734       i = a[1];
  735    if (mp->m_flag & MSPAM)
  736       i = a[12];
  737    if (mp->m_flag & MSPAMUNSURE)
  738       i = a[13];
  739    if (mp->m_flag & MSAVED)
  740       i = a[4];
  741    if (mp->m_flag & MPRESERVE)
  742       i = a[5];
  743    if (mp->m_flag & (MBOX | MBOXED))
  744       i = a[6];
  745    if (mp->m_flag & MFLAGGED)
  746       i = a[7];
  747    if (mb.mb_threaded == 1 && mp->m_collapsed > 0)
  748       i = a[11];
  749    if (mb.mb_threaded == 1 && mp->m_collapsed < 0)
  750       i = a[10];
  751    NYD2_LEAVE;
  752    return i;
  753 }
  754 
  755 static int
  756 a_cmd_scroll(char const *arg, bool_t onlynew){
  757    siz_t l;
  758    bool_t isabs;
  759    int msgspec, size, maxs;
  760    NYD2_ENTER;
  761 
  762    /* TODO scroll problem: we do not know whether + and $ have already reached
  763     * TODO the last screen in threaded mode */
  764    msgspec = onlynew ? -1 : 0;
  765    size = (int)/*TODO*/n_screensize();
  766    if((maxs = msgCount / size) > 0 && msgCount % size == 0)
  767       --maxs;
  768 
  769    if(arg == NULL)
  770       arg = n_empty;
  771    switch(*arg){
  772    case '\0':
  773       ++_screen;
  774       goto jfwd;
  775    case '^':
  776       if(arg[1] != '\0')
  777          goto jerr;
  778       if(_screen == 0)
  779          goto jerrbwd;
  780       _screen = 0;
  781       break;
  782    case '$':
  783       if(arg[1] != '\0')
  784          goto jerr;
  785       if(_screen == maxs)
  786          goto jerrfwd;
  787       _screen = maxs;
  788       break;
  789    case '+':
  790       if(arg[1] == '\0')
  791          ++_screen;
  792       else{
  793          isabs = FAL0;
  794 
  795          ++arg;
  796          if(0){
  797    case '1': case '2': case '3': case '4': case '5':
  798    case '6': case '7': case '8': case '9': case '0':
  799             isabs = TRU1;
  800          }
  801          if((n_idec_siz_cp(&l, arg, 0, NULL
  802                   ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
  803                ) != n_IDEC_STATE_CONSUMED)
  804             goto jerr;
  805          if(l > maxs - (isabs ? 0 : _screen))
  806             goto jerrfwd;
  807          _screen = isabs ? (int)l : _screen + l;
  808       }
  809 jfwd:
  810       if(_screen > maxs){
  811 jerrfwd:
  812          _screen = maxs;
  813          fprintf(n_stdout, _("On last screenful of messages\n"));
  814       }
  815       break;
  816 
  817    case '-':
  818       if(arg[1] == '\0')
  819          --_screen;
  820       else{
  821          if((n_idec_siz_cp(&l, ++arg, 0, NULL
  822                   ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
  823                ) != n_IDEC_STATE_CONSUMED)
  824             goto jerr;
  825          if(l > _screen)
  826             goto jerrbwd;
  827          _screen -= l;
  828       }
  829       if(_screen < 0){
  830 jerrbwd:
  831          _screen = 0;
  832          fprintf(n_stdout, _("On first screenful of messages\n"));
  833       }
  834       if(msgspec == -1)
  835          msgspec = -2;
  836       break;
  837    default:
  838 jerr:
  839       n_err(_("Unrecognized scrolling command: %s\n"), arg);
  840       size = 1;
  841       goto jleave;
  842    }
  843 
  844    size = _headers(msgspec);
  845 jleave:
  846    NYD2_LEAVE;
  847    return size;
  848 }
  849 
  850 static int
  851 _headers(int msgspec) /* TODO rework v15 */
  852 {
  853    bool_t needdot, showlast;
  854    int g, k, mesg, size;
  855    struct message *lastmq, *mp, *mq;
  856    int volatile lastg;
  857    ui32_t volatile flag;
  858    enum mflag fl;
  859    NYD_ENTER;
  860 
  861    time_current_update(&time_current, FAL0);
  862 
  863    fl = MNEW | MFLAGGED;
  864    flag = 0;
  865    lastg = 1;
  866    lastmq = NULL;
  867 
  868    size = (int)/*TODO*/n_screensize();
  869    if (_screen < 0)
  870       _screen = 0;
  871 #if 0 /* FIXME original code path */
  872       k = _screen * size;
  873 #else
  874    if (msgspec <= 0)
  875       k = _screen * size;
  876    else
  877       k = msgspec;
  878 #endif
  879    if (k >= msgCount)
  880       k = msgCount - size;
  881    if (k < 0)
  882       k = 0;
  883 
  884    needdot = (msgspec <= 0) ? TRU1 : (dot != &message[msgspec - 1]);
  885    showlast = ok_blook(showlast);
  886 
  887    if (mb.mb_threaded == 0) {
  888       g = 0;
  889       mq = message;
  890       for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
  891          if (visible(mp)) {
  892             if (g % size == 0)
  893                mq = mp;
  894             if (mp->m_flag & fl) {
  895                lastg = g;
  896                lastmq = mq;
  897             }
  898             if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
  899                   (msgspec == 0 && g == k) ||
  900                   (msgspec == -2 && g == k + size && lastmq) ||
  901                   (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
  902                break;
  903             g++;
  904          }
  905       if (lastmq && (msgspec == -2 ||
  906             (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
  907          g = lastg;
  908          mq = lastmq;
  909       }
  910       _screen = g / size;
  911       mp = mq;
  912 
  913       mesg = (int)PTR2SIZE(mp - message);
  914 #ifdef HAVE_IMAP
  915       if (mb.mb_type == MB_IMAP)
  916          imap_getheaders(mesg + 1, mesg + size);
  917 #endif
  918       n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
  919       srelax_hold();
  920       for(lastmq = NULL, mq = &message[msgCount]; mp < mq; lastmq = mp, ++mp){
  921          ++mesg;
  922          if (!visible(mp))
  923             continue;
  924          if (UICMP(32, flag, >=, size))
  925             break;
  926          if(needdot){
  927             if(showlast){
  928                if(UICMP(32, flag, ==, size - 1) || &mp[1] == mq)
  929                   goto jdot_unsort;
  930             }else if(flag == 0){
  931 jdot_unsort:
  932                needdot = FAL0;
  933                setdot(mp);
  934             }
  935          }
  936          ++flag;
  937          a_cmd_print_head(0, mesg, n_stdout, 0);
  938          srelax();
  939       }
  940       if(needdot && ok_blook(showlast)) /* xxx will not show */
  941          setdot(lastmq);
  942       srelax_rele();
  943       n_COLOUR( n_colour_env_gut(); )
  944 
  945    } else { /* threaded */
  946       g = 0;
  947       mq = threadroot;
  948       for (mp = threadroot; mp; mp = next_in_thread(mp))
  949          if (visible(mp) &&
  950                (mp->m_collapsed <= 0 ||
  951                 PTRCMP(mp, ==, message + msgspec - 1))) {
  952             if (g % size == 0)
  953                mq = mp;
  954             if (mp->m_flag & fl) {
  955                lastg = g;
  956                lastmq = mq;
  957             }
  958             if ((msgspec > 0 && PTRCMP(mp, ==, message + msgspec - 1)) ||
  959                   (msgspec == 0 && g == k) ||
  960                   (msgspec == -2 && g == k + size && lastmq) ||
  961                   (msgspec < 0 && g >= k && (mp->m_flag & fl) != 0))
  962                break;
  963             g++;
  964          }
  965       if (lastmq && (msgspec == -2 ||
  966             (msgspec == -1 && PTRCMP(mp, ==, message + msgCount)))) {
  967          g = lastg;
  968          mq = lastmq;
  969       }
  970       _screen = g / size;
  971       mp = mq;
  972 
  973       n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
  974       srelax_hold();
  975       for(lastmq = NULL; mp != NULL; lastmq = mp, mp = mq){
  976          mq = next_in_thread(mp);
  977          if (visible(mp) &&
  978                (mp->m_collapsed <= 0 ||
  979                 PTRCMP(mp, ==, message + msgspec - 1))) {
  980             if (UICMP(32, flag, >=, size))
  981                break;
  982             if(needdot){
  983                if(showlast){
  984                   if(UICMP(32, flag, ==, size - 1) || mq == NULL)
  985                      goto jdot_sort;
  986                }else if(flag == 0){
  987 jdot_sort:
  988                   needdot = FAL0;
  989                   setdot(mp);
  990                }
  991             }
  992             a_cmd_print_head(flag, PTR2SIZE(mp - message + 1), n_stdout,
  993                mb.mb_threaded);
  994             ++flag;
  995             srelax();
  996          }
  997       }
  998       if(needdot && ok_blook(showlast)) /* xxx will not show */
  999          setdot(lastmq);
 1000       srelax_rele();
 1001       n_COLOUR( n_colour_env_gut(); )
 1002    }
 1003 
 1004    if (flag == 0) {
 1005       fprintf(n_stdout, _("No more mail.\n"));
 1006       if (n_pstate & (n_PS_ROBOT | n_PS_HOOK_MASK))
 1007          flag = !flag;
 1008    }
 1009    NYD_LEAVE;
 1010    return !flag;
 1011 }
 1012 
 1013 FL int
 1014 c_headers(void *v)
 1015 {
 1016    int rv;
 1017    NYD_ENTER;
 1018 
 1019    rv = print_header_group((int*)v);
 1020    NYD_LEAVE;
 1021    return rv;
 1022 }
 1023 
 1024 FL int
 1025 print_header_group(int *vector)
 1026 {
 1027    int rv;
 1028    NYD_ENTER;
 1029 
 1030    assert(vector != NULL && vector != (void*)-1);
 1031    rv = _headers(vector[0]);
 1032    NYD_LEAVE;
 1033    return rv;
 1034 }
 1035 
 1036 FL int
 1037 c_scroll(void *v)
 1038 {
 1039    int rv;
 1040    NYD_ENTER;
 1041 
 1042    rv = a_cmd_scroll(*(char const**)v, FAL0);
 1043    NYD_LEAVE;
 1044    return rv;
 1045 }
 1046 
 1047 FL int
 1048 c_Scroll(void *v)
 1049 {
 1050    int rv;
 1051    NYD_ENTER;
 1052 
 1053    rv = a_cmd_scroll(*(char const**)v, TRU1);
 1054    NYD_LEAVE;
 1055    return rv;
 1056 }
 1057 
 1058 FL int
 1059 c_dotmove(void *v)
 1060 {
 1061    char const *args;
 1062    int msgvec[2], rv;
 1063    NYD_ENTER;
 1064 
 1065    if (*(args = v) == '\0' || args[1] != '\0') {
 1066 jerr:
 1067       n_err(_("Synopsis: dotmove: up <-> or down <+> by one message\n"));
 1068       rv = 1;
 1069    } else switch (args[0]) {
 1070    case '-':
 1071    case '+':
 1072       if (msgCount == 0) {
 1073          fprintf(n_stdout, _("At EOF\n"));
 1074          rv = 0;
 1075       } else if (getmsglist(n_UNCONST(/*TODO*/ args), msgvec, 0) > 0) {
 1076          setdot(message + msgvec[0] - 1);
 1077          msgvec[1] = 0;
 1078          rv = c_headers(msgvec);
 1079       } else
 1080          rv = 1;
 1081       break;
 1082    default:
 1083       goto jerr;
 1084    }
 1085    NYD_LEAVE;
 1086    return rv;
 1087 }
 1088 
 1089 FL int
 1090 c_from(void *vp)
 1091 {
 1092    int *msgvec, *ip, n;
 1093    char *cp;
 1094    FILE * volatile obuf;
 1095    NYD_ENTER;
 1096 
 1097    if(*(msgvec = vp) == 0)
 1098       goto jleave;
 1099 
 1100    time_current_update(&time_current, FAL0);
 1101 
 1102    obuf = n_stdout;
 1103 
 1104    if (n_psonce & n_PSO_INTERACTIVE) {
 1105       if ((cp = ok_vlook(crt)) != NULL) {
 1106          uiz_t ib;
 1107 
 1108          for (n = 0, ip = msgvec; *ip != 0; ++ip)
 1109             ++n;
 1110 
 1111          if(*cp == '\0')
 1112             ib = n_screensize();
 1113          else
 1114             n_idec_uiz_cp(&ib, cp, 0, NULL);
 1115          if (UICMP(z, n, >, ib) && (obuf = n_pager_open()) == NULL)
 1116             obuf = n_stdout;
 1117       }
 1118    }
 1119 
 1120    /* Update dot before display so that the dotmark etc. are correct */
 1121    for (ip = msgvec; ip[1] != 0; ++ip)
 1122       ;
 1123    setdot(&message[(ok_blook(showlast) ? *ip : *msgvec) - 1]);
 1124 
 1125    n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, obuf, obuf != n_stdout); )
 1126    srelax_hold();
 1127    for (n = 0, ip = msgvec; *ip != 0; ++ip) { /* TODO join into _print_head() */
 1128       a_cmd_print_head((size_t)n++, (size_t)*ip, obuf, mb.mb_threaded);
 1129       srelax();
 1130    }
 1131    srelax_rele();
 1132    n_COLOUR( n_colour_env_gut(); )
 1133 
 1134    if (obuf != n_stdout)
 1135       n_pager_close(obuf);
 1136 jleave:
 1137    NYD_LEAVE;
 1138    return 0;
 1139 }
 1140 
 1141 FL void
 1142 print_headers(size_t bottom, size_t topx, bool_t only_marked)
 1143 {
 1144    size_t printed;
 1145    NYD_ENTER;
 1146 
 1147 #ifdef HAVE_IMAP
 1148    if (mb.mb_type == MB_IMAP)
 1149       imap_getheaders(bottom, topx);
 1150 #endif
 1151    time_current_update(&time_current, FAL0);
 1152 
 1153    n_COLOUR( n_colour_env_create(n_COLOUR_CTX_SUM, n_stdout, FAL0); )
 1154    srelax_hold();
 1155    for (printed = 0; bottom <= topx; ++bottom) {
 1156       struct message *mp = message + bottom - 1;
 1157       if (only_marked) {
 1158          if (!(mp->m_flag & MMARK))
 1159             continue;
 1160       } else if (!visible(mp))
 1161          continue;
 1162       a_cmd_print_head(printed++, bottom, n_stdout, FAL0);
 1163       srelax();
 1164    }
 1165    srelax_rele();
 1166    n_COLOUR( n_colour_env_gut(); )
 1167    NYD_LEAVE;
 1168 }
 1169 
 1170 /* s-it-mode */