"Fossies" - the Fresh Open Source Software Archive

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