"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/cmd-message.c" (25 Mar 2018, 23264 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-message.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  *@ Iterating over, and over such housekeeping message 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_message
   37 
   38 #ifndef HAVE_AMALGAMATION
   39 # include "nail.h"
   40 #endif
   41 
   42 /* Prepare and print "[Message: xy]:" intro */
   43 static bool_t a_cmsg_show_overview(FILE *obuf, struct message *mp, int msg_no);
   44 
   45 /* Show the requested messages */
   46 static int     _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
   47                   bool_t donotdecode, char *cmd, ui64_t *tstats);
   48 
   49 /* Pipe the requested messages */
   50 static int     _pipe1(char *str, int doign);
   51 
   52 /* `top' / `Top' */
   53 static int a_cmsg_top(void *vp, struct n_ignore const *itp);
   54 
   55 /* Delete the indicated messages.  Set dot to some nice place afterwards */
   56 static int     delm(int *msgvec);
   57 
   58 static bool_t
   59 a_cmsg_show_overview(FILE *obuf, struct message *mp, int msg_no){
   60    bool_t rv;
   61    char const *cpre, *csuf;
   62    NYD2_ENTER;
   63 
   64    cpre = csuf = n_empty;
   65 #ifdef HAVE_COLOUR
   66    if(n_COLOUR_IS_ACTIVE()){
   67       struct n_colour_pen *cpen;
   68 
   69       if((cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_MSGINFO, NULL)) != NULL){
   70          struct str const *sp;
   71 
   72          if((sp = n_colour_pen_to_str(cpen)) != NULL)
   73             cpre = sp->s;
   74          if((sp = n_colour_reset_to_str()) != NULL)
   75             csuf = sp->s;
   76       }
   77    }
   78 #endif
   79    /* XXX Message info uses wire format for line count */
   80    rv = (fprintf(obuf,
   81          A_("%s[-- Message %2d -- %lu lines, %lu bytes --]:%s\n"),
   82          cpre, msg_no, (ul_i)mp->m_lines, (ul_i)mp->m_size, csuf) > 0);
   83    NYD2_LEAVE;
   84    return rv;
   85 }
   86 
   87 static int
   88 _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
   89    bool_t donotdecode, char *cmd, ui64_t *tstats)
   90 {
   91    ui64_t mstats[1];
   92    int *ip;
   93    struct message *mp;
   94    char const *cp;
   95    enum sendaction action;
   96    bool_t volatile formfeed;
   97    FILE * volatile obuf;
   98    int volatile rv;
   99    NYD_ENTER;
  100 
  101    rv = 1;
  102    obuf = n_stdout;
  103    formfeed = (dopipe && ok_blook(page));
  104    action = ((dopipe && ok_blook(piperaw))
  105          ? SEND_MBOX : donotdecode
  106          ? SEND_SHOW : doign
  107          ? SEND_TODISP : SEND_TODISP_ALL);
  108 
  109    if (dopipe) {
  110       if ((obuf = Popen(cmd, "w", ok_vlook(SHELL), NULL, 1)) == NULL) {
  111          n_perr(cmd, 0);
  112          obuf = n_stdout;
  113       }
  114    } else if ((n_psonce & n_PSO_TTYOUT) && (dopage ||
  115          ((n_psonce & n_PSO_INTERACTIVE) && (cp = ok_vlook(crt)) != NULL))) {
  116       uiz_t nlines, lib;
  117 
  118       nlines = 0;
  119 
  120       if (!dopage) {
  121          for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
  122             mp = message + *ip - 1;
  123             if (!(mp->m_content_info & CI_HAVE_BODY))
  124                if (get_body(mp) != OKAY)
  125                   goto jleave;
  126             nlines += mp->m_lines + 1; /* TODO BUT wire format, not display! */
  127          }
  128       }
  129 
  130       /* >= not <: we return to the prompt */
  131       if(dopage || nlines >= (*cp != '\0'
  132                ? (n_idec_uiz_cp(&lib, cp, 0, NULL), lib)
  133                : (uiz_t)n_realscreenheight)){
  134          if((obuf = n_pager_open()) == NULL)
  135             obuf = n_stdout;
  136       }
  137       n_COLOUR(
  138          if(action == SEND_TODISP || action == SEND_TODISP_ALL)
  139             n_colour_env_create(n_COLOUR_CTX_VIEW, obuf, obuf != n_stdout);
  140       )
  141    }
  142    n_COLOUR(
  143       else if(action == SEND_TODISP || action == SEND_TODISP_ALL)
  144          n_colour_env_create(n_COLOUR_CTX_VIEW, n_stdout, FAL0);
  145    )
  146 
  147    rv = 0;
  148    srelax_hold();
  149    for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
  150       mp = message + *ip - 1;
  151       touch(mp);
  152       setdot(mp);
  153       n_pstate |= n_PS_DID_PRINT_DOT;
  154       uncollapse1(mp, 1);
  155       if(!dopipe && ip != msgvec && fprintf(obuf, "\n") < 0){
  156          rv = 1;
  157          break;
  158       }
  159       if(action != SEND_MBOX && !a_cmsg_show_overview(obuf, mp, *ip)){
  160          rv = 1;
  161          break;
  162       }
  163       if(sendmp(mp, obuf, (doign ? n_IGNORE_TYPE : NULL), NULL, action, mstats
  164             ) < 0){
  165          rv = 1;
  166          break;
  167       }
  168       srelax();
  169       if(formfeed){ /* TODO a nicer way to separate piped messages! */
  170          if(putc('\f', obuf) == EOF){
  171             rv = 1;
  172             break;
  173          }
  174       }
  175       if (tstats != NULL)
  176          tstats[0] += mstats[0];
  177    }
  178    srelax_rele();
  179    n_COLOUR(
  180       if(!dopipe && (action == SEND_TODISP || action == SEND_TODISP_ALL))
  181          n_colour_env_gut();
  182    )
  183 jleave:
  184    if (obuf != n_stdout)
  185       n_pager_close(obuf);
  186    NYD_LEAVE;
  187    return rv;
  188 }
  189 
  190 static int
  191 _pipe1(char *str, int doign)
  192 {
  193    ui64_t stats[1];
  194    char const *cmd, *cmdq;
  195    int *msgvec, rv = 1;
  196    bool_t needs_list;
  197    NYD_ENTER;
  198 
  199    if ((cmd = laststring(str, &needs_list, TRU1)) == NULL) {
  200       cmd = ok_vlook(cmd);
  201       if (cmd == NULL || *cmd == '\0') {
  202          n_err(_("Variable *cmd* not set\n"));
  203          goto jleave;
  204       }
  205    }
  206 
  207    msgvec = salloc((msgCount + 2) * sizeof *msgvec);
  208 
  209    if (!needs_list) {
  210       *msgvec = first(0, MMNORM);
  211       if (*msgvec == 0) {
  212          if (n_pstate & (n_PS_ROBOT | n_PS_HOOK_MASK)) {
  213             rv = 0;
  214             goto jleave;
  215          }
  216          fputs(_("No messages to pipe.\n"), n_stdout);
  217          goto jleave;
  218       }
  219       msgvec[1] = 0;
  220    } else if (getmsglist(str, msgvec, 0) < 0)
  221       goto jleave;
  222    if (*msgvec == 0) {
  223       if (n_pstate & (n_PS_ROBOT | n_PS_HOOK_MASK)) {
  224          rv = 0;
  225          goto jleave;
  226       }
  227       fprintf(n_stdout, "No applicable messages.\n");
  228       goto jleave;
  229    }
  230 
  231    cmdq = n_shexp_quote_cp(cmd, FAL0);
  232    fprintf(n_stdout, _("Pipe to: %s\n"), cmdq);
  233    stats[0] = 0;
  234    if ((rv = _type1(msgvec, doign, FAL0, TRU1, FAL0, n_UNCONST(cmd), stats)
  235          ) == 0)
  236       fprintf(n_stdout, "%s %" PRIu64 " bytes\n", cmdq, stats[0]);
  237 jleave:
  238    NYD_LEAVE;
  239    return rv;
  240 }
  241 
  242 static int
  243 a_cmsg_top(void *vp, struct n_ignore const *itp){
  244    struct n_string s;
  245    int *msgvec, *ip;
  246    enum{a_NONE, a_SQUEEZE = 1u<<0,
  247       a_EMPTY = 1u<<8, a_STOP = 1u<<9,  a_WORKMASK = 0xFF00u} f;
  248    size_t tmax, plines;
  249    FILE *iobuf, *pbuf;
  250    NYD2_ENTER;
  251 
  252    if((iobuf = Ftmp(NULL, "topio", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL){
  253       n_perr(_("`top': I/O temporary file"), 0);
  254       vp = NULL;
  255       goto jleave;
  256    }
  257    if((pbuf = Ftmp(NULL, "toppag", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL){
  258       n_perr(_("`top': temporary pager file"), 0);
  259       vp = NULL;
  260       goto jleave1;
  261    }
  262 
  263    /* TODO In v15 we should query the m_message object, and directly send only
  264     * TODO those parts, optionally over empty-line-squeeze and quote-strip
  265     * TODO filters, in which we are interested in: only text content!
  266     * TODO And: with *topsqueeze*, header/content separating empty line.. */
  267    n_pstate &= ~n_PS_MSGLIST_DIRECT; /* TODO NO ATTACHMENTS */
  268    plines = 0;
  269 
  270    n_COLOUR( n_colour_env_create(n_COLOUR_CTX_VIEW, iobuf, FAL0); )
  271    n_string_creat_auto(&s);
  272    /* C99 */{
  273       siz_t l;
  274 
  275       if((n_idec_siz_cp(&l, ok_vlook(toplines), 0, NULL
  276                ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
  277             ) != n_IDEC_STATE_CONSUMED)
  278          l = 0;
  279       if(l <= 0){
  280          tmax = n_screensize();
  281          if(l < 0){
  282             l = n_ABS(l);
  283             tmax >>= l;
  284          }
  285       }else
  286          tmax = (size_t)l;
  287    }
  288    f = ok_blook(topsqueeze) ? a_SQUEEZE : a_NONE;
  289 
  290    for(ip = msgvec = vp;
  291          *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount); ++ip){
  292       struct message *mp;
  293 
  294       mp = &message[*ip - 1];
  295       touch(mp);
  296       setdot(mp);
  297       n_pstate |= n_PS_DID_PRINT_DOT;
  298       uncollapse1(mp, 1);
  299 
  300       rewind(iobuf);
  301       if(ftruncate(fileno(iobuf), 0)){
  302          n_perr(_("`top': ftruncate(2)"), 0);
  303          vp = NULL;
  304          break;
  305       }
  306 
  307       if(!a_cmsg_show_overview(iobuf, mp, *ip) ||
  308             sendmp(mp, iobuf, itp, NULL, SEND_TODISP_ALL, NULL) < 0){
  309          n_err(_("`top': failed to prepare message %d\n"), *ip);
  310          vp = NULL;
  311          break;
  312       }
  313       fflush_rewind(iobuf);
  314 
  315       /* TODO Skip over the _msg_overview line -- this is a hack to make
  316        * TODO colours work: colour contexts should be objects */
  317       for(;;){
  318          int c;
  319 
  320          if((c = getc(iobuf)) == EOF || putc(c, pbuf) == EOF){
  321             vp = NULL;
  322             break;
  323          }else if(c == '\n')
  324             break;
  325       }
  326       if(vp == NULL)
  327          break;
  328       ++plines;
  329 
  330       /* C99 */{
  331          size_t l;
  332 
  333          n_string_trunc(&s, 0);
  334          for(l = 0, f &= ~a_WORKMASK; !(f & a_STOP);){
  335             int c;
  336 
  337             if((c = getc(iobuf)) == EOF){
  338                f |= a_STOP;
  339                c = '\n';
  340             }
  341 
  342             if(c != '\n')
  343                n_string_push_c(&s, c);
  344             else if((f & a_SQUEEZE) && s.s_len == 0){
  345                if(!(f & a_STOP) && ((f & a_EMPTY) || tmax - 1 <= l))
  346                   continue;
  347                if(putc('\n', pbuf) == EOF){
  348                   vp = NULL;
  349                   break;
  350                }
  351                f |= a_EMPTY;
  352                ++l;
  353             }else{
  354                char const *cp, *xcp;
  355 
  356                cp = n_string_cp_const(&s);
  357                /* TODO Brute simple skip part overviews; see above.. */
  358                if(!(f & a_SQUEEZE))
  359                   c = '\1';
  360                else if(s.s_len > 8 &&
  361                      (xcp = strstr(cp, "[-- ")) != NULL &&
  362                       strstr(&xcp[1], " --]") != NULL)
  363                   c = '\0';
  364                else{
  365                   char const *qcp;
  366 
  367                   for(qcp = ok_vlook(quote_chars); (c = *cp) != '\0'; ++cp){
  368                      if(!asciichar(c))
  369                         break;
  370                      if(!blankspacechar(c)){
  371                         if(strchr(qcp, c) == NULL)
  372                            break;
  373                         c = '\0';
  374                         break;
  375                      }
  376                   }
  377                }
  378 
  379                if(c != '\0'){
  380                   if(fputs(n_string_cp_const(&s), pbuf) == EOF ||
  381                         putc('\n', pbuf) == EOF){
  382                      vp = NULL;
  383                      break;
  384                   }
  385                   if(++l >= tmax)
  386                      break;
  387                   f &= ~a_EMPTY;
  388                }else
  389                   f |= a_EMPTY;
  390                n_string_trunc(&s, 0);
  391             }
  392          }
  393          if(vp == NULL)
  394             break;
  395          if(l > 0)
  396             plines += l;
  397          else{
  398             if(!(f & a_EMPTY) && putc('\n', pbuf) == EOF){
  399                vp = NULL;
  400                break;
  401             }
  402             ++plines;
  403          }
  404       }
  405    }
  406 
  407    n_string_gut(&s);
  408    n_COLOUR( n_colour_env_gut(); )
  409 
  410    fflush(pbuf);
  411    page_or_print(pbuf, plines);
  412 
  413    Fclose(pbuf);
  414 jleave1:
  415    Fclose(iobuf);
  416 jleave:
  417    NYD2_LEAVE;
  418    return (vp != NULL);
  419 }
  420 
  421 static int
  422 delm(int *msgvec)
  423 {
  424    struct message *mp;
  425    int rv = -1, *ip, last;
  426    NYD_ENTER;
  427 
  428    last = 0;
  429    for (ip = msgvec; *ip != 0; ++ip) {
  430       mp = message + *ip - 1;
  431       touch(mp);
  432       mp->m_flag |= MDELETED | MTOUCH;
  433       mp->m_flag &= ~(MPRESERVE | MSAVED | MBOX);
  434       last = *ip;
  435    }
  436    if (last != 0) {
  437       setdot(message + last - 1);
  438       last = first(0, MDELETED);
  439       if (last != 0) {
  440          setdot(message + last - 1);
  441          rv = 0;
  442       } else {
  443          setdot(message);
  444       }
  445    }
  446    NYD_LEAVE;
  447    return rv;
  448 }
  449 
  450 FL int
  451 c_more(void *v)
  452 {
  453    int *msgvec = v, rv;
  454    NYD_ENTER;
  455 
  456    rv = _type1(msgvec, TRU1, TRU1, FAL0, FAL0, NULL, NULL);
  457    NYD_LEAVE;
  458    return rv;
  459 }
  460 
  461 FL int
  462 c_More(void *v)
  463 {
  464    int *msgvec = v, rv;
  465    NYD_ENTER;
  466 
  467    rv = _type1(msgvec, FAL0, TRU1, FAL0, FAL0, NULL, NULL);
  468    NYD_LEAVE;
  469    return rv;
  470 }
  471 
  472 FL int
  473 c_type(void *v)
  474 {
  475    int *msgvec = v, rv;
  476    NYD_ENTER;
  477 
  478    rv = _type1(msgvec, TRU1, FAL0, FAL0, FAL0, NULL, NULL);
  479    NYD_LEAVE;
  480    return rv;
  481 }
  482 
  483 FL int
  484 c_Type(void *v)
  485 {
  486    int *msgvec = v, rv;
  487    NYD_ENTER;
  488 
  489    rv = _type1(msgvec, FAL0, FAL0, FAL0, FAL0, NULL, NULL);
  490    NYD_LEAVE;
  491    return rv;
  492 }
  493 
  494 FL int
  495 c_show(void *v)
  496 {
  497    int *msgvec = v, rv;
  498    NYD_ENTER;
  499 
  500    rv = _type1(msgvec, FAL0, FAL0, FAL0, TRU1, NULL, NULL);
  501    NYD_LEAVE;
  502    return rv;
  503 }
  504 
  505 FL int
  506 c_mimeview(void *vp){ /* TODO direct addressable parts, multiple such */
  507    struct message *mp;
  508    int rv, *msgvec;
  509    NYD_ENTER;
  510 
  511    if((msgvec = vp)[1] != 0){
  512       n_err(_("`mimeview': can yet only take one message, sorry!\n"));/* TODO */
  513       n_pstate_err_no = n_ERR_NOTSUP;
  514       rv = 1;
  515       goto jleave;
  516    }
  517 
  518    mp = &message[*msgvec - 1];
  519    touch(mp);
  520    setdot(mp);
  521    n_pstate |= n_PS_DID_PRINT_DOT;
  522    uncollapse1(mp, 1);
  523 
  524    n_COLOUR(
  525       n_colour_env_create(n_COLOUR_CTX_VIEW, n_stdout, FAL0);
  526    )
  527 
  528    if(!a_cmsg_show_overview(n_stdout, mp, *msgvec))
  529       n_pstate_err_no = n_ERR_IO;
  530    else if(sendmp(mp, n_stdout, n_IGNORE_TYPE, NULL, SEND_TODISP_PARTS,
  531          NULL) < 0)
  532       n_pstate_err_no = n_ERR_IO;
  533    else
  534       n_pstate_err_no = n_ERR_NONE;
  535 
  536    n_COLOUR(
  537       n_colour_env_gut();
  538    )
  539 
  540    rv = (n_pstate_err_no != n_ERR_NONE);
  541 jleave:
  542    NYD_LEAVE;
  543    return rv;
  544 }
  545 
  546 FL int
  547 c_pipe(void *v)
  548 {
  549    char *str = v;
  550    int rv;
  551    NYD_ENTER;
  552 
  553    rv = _pipe1(str, 1);
  554    NYD_LEAVE;
  555    return rv;
  556 }
  557 
  558 FL int
  559 c_Pipe(void *v)
  560 {
  561    char *str = v;
  562    int rv;
  563    NYD_ENTER;
  564 
  565    rv = _pipe1(str, 0);
  566    NYD_LEAVE;
  567    return rv;
  568 }
  569 
  570 FL int
  571 c_top(void *v){
  572    struct n_ignore *itp;
  573    int rv;
  574    NYD_ENTER;
  575 
  576    if(n_ignore_is_any(n_IGNORE_TOP))
  577       itp = n_IGNORE_TOP;
  578    else{
  579       itp = n_ignore_new(TRU1);
  580       n_ignore_insert(itp, TRU1, "from", sizeof("from") -1);
  581       n_ignore_insert(itp, TRU1, "to", sizeof("to") -1);
  582       n_ignore_insert(itp, TRU1, "cc", sizeof("cc") -1);
  583       n_ignore_insert(itp, TRU1, "subject", sizeof("subject") -1);
  584    }
  585 
  586    rv = !a_cmsg_top(v, itp);
  587    NYD_LEAVE;
  588    return rv;
  589 }
  590 
  591 FL int
  592 c_Top(void *v){
  593    int rv;
  594    NYD_ENTER;
  595 
  596    rv = !a_cmsg_top(v, n_IGNORE_TYPE);
  597    NYD_LEAVE;
  598    return rv;
  599 }
  600 
  601 FL int
  602 c_next(void *v)
  603 {
  604    int list[2], *ip, *ip2, mdot, *msgvec = v, rv = 1;
  605    struct message *mp;
  606    NYD_ENTER;
  607 
  608    if (*msgvec != 0) {
  609       /* If some messages were supplied, find the first applicable one
  610        * following dot using wrap around */
  611       mdot = (int)PTR2SIZE(dot - message + 1);
  612 
  613       /* Find first message in supplied message list which follows dot */
  614       for (ip = msgvec; *ip != 0; ++ip) {
  615          if ((mb.mb_threaded ? message[*ip - 1].m_threadpos > dot->m_threadpos
  616                : *ip > mdot))
  617             break;
  618       }
  619       if (*ip == 0)
  620          ip = msgvec;
  621       ip2 = ip;
  622       do {
  623          mp = message + *ip2 - 1;
  624          if (!(mp->m_flag & MMNDEL)) {
  625             setdot(mp);
  626             goto jhitit;
  627          }
  628          if (*ip2 != 0)
  629             ++ip2;
  630          if (*ip2 == 0)
  631             ip2 = msgvec;
  632       } while (ip2 != ip);
  633       fprintf(n_stdout, _("No messages applicable\n"));
  634       goto jleave;
  635    }
  636 
  637    /* If this is the first command, select message 1.  Note that this must
  638     * exist for us to get here at all */
  639    if (!(n_pstate & n_PS_SAW_COMMAND)) {
  640       if (msgCount == 0)
  641          goto jateof;
  642       goto jhitit;
  643    }
  644 
  645    /* Just find the next good message after dot, no wraparound */
  646    if (mb.mb_threaded == 0) {
  647       for (mp = dot + !!(n_pstate & n_PS_DID_PRINT_DOT);
  648             PTRCMP(mp, <, message + msgCount); ++mp)
  649          if (!(mp->m_flag & MMNORM))
  650             break;
  651    } else {
  652       /* TODO The threading code had some bugs that caused crashes.
  653        * TODO The last thing (before the deep look) happens here,
  654        * TODO so let's not trust n_PS_DID_PRINT_DOT but check & hope it fixes */
  655       if ((mp = dot) != NULL && (n_pstate & n_PS_DID_PRINT_DOT))
  656          mp = next_in_thread(mp);
  657       while (mp != NULL && (mp->m_flag & MMNORM))
  658          mp = next_in_thread(mp);
  659    }
  660    if (mp == NULL || PTRCMP(mp, >=, message + msgCount)) {
  661 jateof:
  662       fprintf(n_stdout, _("At EOF\n"));
  663       rv = 0;
  664       goto jleave;
  665    }
  666    setdot(mp);
  667 
  668    /* Print dot */
  669 jhitit:
  670    list[0] = (int)PTR2SIZE(dot - message + 1);
  671    list[1] = 0;
  672    rv = c_type(list);
  673 jleave:
  674    NYD_LEAVE;
  675    return rv;
  676 }
  677 
  678 FL int
  679 c_pdot(void *vp)
  680 {
  681    NYD_ENTER;
  682    n_UNUSED(vp);
  683    fprintf(n_stdout, "%d\n", (int)PTR2SIZE(dot - message + 1));
  684    NYD_LEAVE;
  685    return 0;
  686 }
  687 
  688 FL int
  689 c_messize(void *v)
  690 {
  691    int *msgvec = v, *ip, mesg;
  692    struct message *mp;
  693    NYD_ENTER;
  694 
  695    for (ip = msgvec; *ip != 0; ++ip) {
  696       mesg = *ip;
  697       mp = message + mesg - 1;
  698       fprintf(n_stdout, "%d: ", mesg);
  699       if (mp->m_xlines > 0)
  700          fprintf(n_stdout, "%ld", mp->m_xlines);
  701       else
  702          putc(' ', n_stdout);
  703       fprintf(n_stdout, "/%lu\n", (ul_i)mp->m_xsize);
  704    }
  705    NYD_LEAVE;
  706    return 0;
  707 }
  708 
  709 FL int
  710 c_delete(void *v)
  711 {
  712    int *msgvec = v;
  713    NYD_ENTER;
  714 
  715    delm(msgvec);
  716    NYD_LEAVE;
  717    return 0;
  718 }
  719 
  720 FL int
  721 c_deltype(void *v)
  722 {
  723    int list[2], rv = 0, *msgvec = v, lastdot;
  724    NYD_ENTER;
  725 
  726    lastdot = (int)PTR2SIZE(dot - message + 1);
  727    if (delm(msgvec) >= 0) {
  728       list[0] = (int)PTR2SIZE(dot - message + 1);
  729       if (list[0] > lastdot) {
  730          touch(dot);
  731          list[1] = 0;
  732          rv = c_type(list);
  733          goto jleave;
  734       }
  735       fprintf(n_stdout, _("At EOF\n"));
  736    } else
  737       fprintf(n_stdout, _("No more messages\n"));
  738 jleave:
  739    NYD_LEAVE;
  740    return rv;
  741 }
  742 
  743 FL int
  744 c_undelete(void *v)
  745 {
  746    int *msgvec = v, *ip;
  747    struct message *mp;
  748    NYD_ENTER;
  749 
  750    for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
  751          ++ip) {
  752       mp = &message[*ip - 1];
  753       touch(mp);
  754       setdot(mp);
  755       if (mp->m_flag & (MDELETED | MSAVED))
  756          mp->m_flag &= ~(MDELETED | MSAVED);
  757       else
  758          mp->m_flag &= ~MDELETED;
  759 #ifdef HAVE_IMAP
  760       if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
  761          imap_undelete(mp, *ip);
  762 #endif
  763    }
  764    NYD_LEAVE;
  765    return 0;
  766 }
  767 
  768 FL int
  769 c_stouch(void *v)
  770 {
  771    int *msgvec = v, *ip;
  772    NYD_ENTER;
  773 
  774    for (ip = msgvec; *ip != 0; ++ip) {
  775       setdot(message + *ip - 1);
  776       dot->m_flag |= MTOUCH;
  777       dot->m_flag &= ~MPRESERVE;
  778       n_pstate |= n_PS_DID_PRINT_DOT;
  779    }
  780    NYD_LEAVE;
  781    return 0;
  782 }
  783 
  784 FL int
  785 c_mboxit(void *v)
  786 {
  787    int *msgvec = v, *ip;
  788    NYD_ENTER;
  789 
  790    if (n_pstate & n_PS_EDIT) {
  791       n_err(_("`mbox' can only be used in a system mailbox\n")); /* TODO */
  792       goto jleave;
  793    }
  794 
  795    for (ip = msgvec; *ip != 0; ++ip) {
  796       setdot(message + *ip - 1);
  797       dot->m_flag |= MTOUCH | MBOX;
  798       dot->m_flag &= ~MPRESERVE;
  799       n_pstate |= n_PS_DID_PRINT_DOT;
  800    }
  801 jleave:
  802    NYD_LEAVE;
  803    return 0;
  804 }
  805 
  806 FL int
  807 c_preserve(void *v)
  808 {
  809    int *msgvec = v, *ip, mesg, rv = 1;
  810    struct message *mp;
  811    NYD_ENTER;
  812 
  813    if (n_pstate & n_PS_EDIT) {
  814       fprintf(n_stdout, _("Cannot `preserve' in a system mailbox\n"));
  815       goto jleave;
  816    }
  817 
  818    for (ip = msgvec; *ip != 0; ++ip) {
  819       mesg = *ip;
  820       mp = message + mesg - 1;
  821       mp->m_flag |= MPRESERVE;
  822       mp->m_flag &= ~MBOX;
  823       setdot(mp);
  824       n_pstate |= n_PS_DID_PRINT_DOT;
  825    }
  826    rv = 0;
  827 jleave:
  828    NYD_LEAVE;
  829    return rv;
  830 }
  831 
  832 FL int
  833 c_unread(void *v)
  834 {
  835    struct message *mp;
  836    int *msgvec = v, *ip;
  837    NYD_ENTER;
  838 
  839    for (ip = msgvec; *ip != 0; ++ip) {
  840       mp = &message[*ip - 1];
  841       setdot(mp);
  842       dot->m_flag &= ~(MREAD | MTOUCH);
  843       dot->m_flag |= MSTATUS;
  844 #ifdef HAVE_IMAP
  845       if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
  846          imap_unread(mp, *ip); /* TODO return? */
  847 #endif
  848       n_pstate |= n_PS_DID_PRINT_DOT;
  849    }
  850    NYD_LEAVE;
  851    return 0;
  852 }
  853 
  854 FL int
  855 c_seen(void *v)
  856 {
  857    int *msgvec = v, *ip;
  858    NYD_ENTER;
  859 
  860    for (ip = msgvec; *ip != 0; ++ip) {
  861       struct message *mp = message + *ip - 1;
  862       setdot(mp);
  863       touch(mp);
  864    }
  865    NYD_LEAVE;
  866    return 0;
  867 }
  868 
  869 FL int
  870 c_flag(void *v)
  871 {
  872    struct message *m;
  873    int *msgvec = v, *ip;
  874    NYD_ENTER;
  875 
  876    for (ip = msgvec; *ip != 0; ++ip) {
  877       m = message + *ip - 1;
  878       setdot(m);
  879       if (!(m->m_flag & (MFLAG | MFLAGGED)))
  880          m->m_flag |= MFLAG | MFLAGGED;
  881    }
  882    NYD_LEAVE;
  883    return 0;
  884 }
  885 
  886 FL int
  887 c_unflag(void *v)
  888 {
  889    struct message *m;
  890    int *msgvec = v, *ip;
  891    NYD_ENTER;
  892 
  893    for (ip = msgvec; *ip != 0; ++ip) {
  894       m = message + *ip - 1;
  895       setdot(m);
  896       if (m->m_flag & (MFLAG | MFLAGGED)) {
  897          m->m_flag &= ~(MFLAG | MFLAGGED);
  898          m->m_flag |= MUNFLAG;
  899       }
  900    }
  901    NYD_LEAVE;
  902    return 0;
  903 }
  904 
  905 FL int
  906 c_answered(void *v)
  907 {
  908    struct message *m;
  909    int *msgvec = v, *ip;
  910    NYD_ENTER;
  911 
  912    for (ip = msgvec; *ip != 0; ++ip) {
  913       m = message + *ip - 1;
  914       setdot(m);
  915       if (!(m->m_flag & (MANSWER | MANSWERED)))
  916          m->m_flag |= MANSWER | MANSWERED;
  917    }
  918    NYD_LEAVE;
  919    return 0;
  920 }
  921 
  922 FL int
  923 c_unanswered(void *v)
  924 {
  925    struct message *m;
  926    int *msgvec = v, *ip;
  927    NYD_ENTER;
  928 
  929    for (ip = msgvec; *ip != 0; ++ip) {
  930       m = message + *ip - 1;
  931       setdot(m);
  932       if (m->m_flag & (MANSWER | MANSWERED)) {
  933          m->m_flag &= ~(MANSWER | MANSWERED);
  934          m->m_flag |= MUNANSWER;
  935       }
  936    }
  937    NYD_LEAVE;
  938    return 0;
  939 }
  940 
  941 FL int
  942 c_draft(void *v)
  943 {
  944    struct message *m;
  945    int *msgvec = v, *ip;
  946    NYD_ENTER;
  947 
  948    for (ip = msgvec; *ip != 0; ++ip) {
  949       m = message + *ip - 1;
  950       setdot(m);
  951       if (!(m->m_flag & (MDRAFT | MDRAFTED)))
  952          m->m_flag |= MDRAFT | MDRAFTED;
  953    }
  954    NYD_LEAVE;
  955    return 0;
  956 }
  957 
  958 FL int
  959 c_undraft(void *v)
  960 {
  961    struct message *m;
  962    int *msgvec = v, *ip;
  963    NYD_ENTER;
  964 
  965    for (ip = msgvec; *ip != 0; ++ip) {
  966       m = message + *ip - 1;
  967       setdot(m);
  968       if (m->m_flag & (MDRAFT | MDRAFTED)) {
  969          m->m_flag &= ~(MDRAFT | MDRAFTED);
  970          m->m_flag |= MUNDRAFT;
  971       }
  972    }
  973    NYD_LEAVE;
  974    return 0;
  975 }
  976 
  977 /* s-it-mode */