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 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 /*
9  * Copyright (c) 1980, 1993
10  *      The Regents of the University of California.  All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 #undef su_FILE
37 #define su_FILE cmd_msg
38 #define mx_SOURCE
39 
40 #ifndef mx_HAVE_AMALGAMATION
41 # include "mx/nail.h"
42 #endif
43 
44 #include <su/cs.h>
45 #include <su/icodec.h>
46 
47 #include "mx/cmd.h"
48 #include "mx/colour.h"
49 #include "mx/file-streams.h"
50 #include "mx/termios.h"
51 
52 /* TODO fake */
53 #include "su/code-in.h"
54 
55 /* Prepare and print "[Message: xy]:" intro */
56 static boole a_cmsg_show_overview(FILE *obuf, struct message *mp, int msg_no);
57 
58 /* Show the requested messages */
59 static int     _type1(int *msgvec, boole doign, boole dopage, boole dopipe,
60                   boole donotdecode, char *cmd, u64 *tstats);
61 
62 /* Pipe the requested messages */
63 static int a_cmsg_pipe1(void *vp, boole doign);
64 
65 /* `top' / `Top' */
66 static int a_cmsg_top(void *vp, struct n_ignore const *itp);
67 
68 /* Delete the indicated messages.  Set dot to some nice place afterwards */
69 static int     delm(int *msgvec);
70 
71 static boole
a_cmsg_show_overview(FILE * obuf,struct message * mp,int msg_no)72 a_cmsg_show_overview(FILE *obuf, struct message *mp, int msg_no){
73    boole rv;
74    char const *cpre, *csuf;
75    NYD2_IN;
76 
77    cpre = csuf = n_empty;
78 #ifdef mx_HAVE_COLOUR
79    if(mx_COLOUR_IS_ACTIVE()){
80       struct mx_colour_pen *cpen;
81 
82       if((cpen = mx_colour_pen_create(mx_COLOUR_ID_VIEW_MSGINFO, NULL)
83             ) != NIL){
84          struct str const *s;
85 
86          if((s = mx_colour_pen_to_str(cpen)) != NIL)
87             cpre = s->s;
88          if((s = mx_colour_reset_to_str()) != NIL)
89             csuf = s->s;
90       }
91    }
92 #endif
93    /* XXX Message info uses wire format for line count */
94    rv = (fprintf(obuf,
95          A_("%s[-- Message %2d -- %lu lines, %lu bytes --]:%s\n"),
96          cpre, msg_no, (ul)mp->m_lines, (ul)mp->m_size, csuf) > 0);
97    NYD2_OU;
98    return rv;
99 }
100 
101 static int
_type1(int * msgvec,boole doign,boole dopage,boole dopipe,boole donotdecode,char * cmd,u64 * tstats)102 _type1(int *msgvec, boole doign, boole dopage, boole dopipe,
103    boole donotdecode, char *cmd, u64 *tstats)
104 {
105    u64 mstats[1];
106    int *ip;
107    struct message *mp;
108    char const *cp;
109    enum sendaction action;
110    boole volatile formfeed;
111    FILE * volatile obuf;
112    int volatile rv;
113    NYD_IN;
114 
115    rv = 1;
116    obuf = n_stdout;
117    formfeed = (dopipe && ok_blook(page));
118    action = ((dopipe && ok_blook(piperaw))
119          ? SEND_MBOX : donotdecode
120          ? SEND_SHOW : doign
121          ? SEND_TODISP : SEND_TODISP_ALL);
122    UNINIT(cp, NIL);
123 
124    if(dopipe){
125       if((obuf = mx_fs_pipe_open(cmd, "w", ok_vlook(SHELL), NIL, -1)) == NIL){
126          n_perr(cmd, 0);
127          obuf = n_stdout;
128       }
129    } else if ((n_psonce & n_PSO_TTYOUT) && (dopage ||
130          ((n_psonce & n_PSO_INTERACTIVE) && (cp = ok_vlook(crt)) != NULL))) {
131       uz nlines, lib;
132 
133       nlines = 0;
134 
135       if (!dopage) {
136          for (ip = msgvec; *ip && PCMP(ip - msgvec, <, msgCount); ++ip) {
137             mp = message + *ip - 1;
138             if (!(mp->m_content_info & CI_HAVE_BODY))
139                if (get_body(mp) != OKAY)
140                   goto jleave;
141             nlines += mp->m_lines + 1; /* TODO BUT wire format, not display! */
142          }
143       }
144 
145       /* >= not <: we return to the prompt */
146       if(dopage || nlines >= (*cp != '\0'
147                ? (su_idec_uz_cp(&lib, cp, 0, NULL), lib)
148                : S(uz,mx_termios_dimen.tiosd_real_height))){
149          if((obuf = mx_pager_open()) == NULL)
150             obuf = n_stdout;
151       }
152       mx_COLOUR(
153          if(action == SEND_TODISP || action == SEND_TODISP_ALL)
154             mx_colour_env_create(mx_COLOUR_CTX_VIEW, obuf, obuf != n_stdout);
155       )
156    }
157    mx_COLOUR(
158       else if(action == SEND_TODISP || action == SEND_TODISP_ALL)
159          mx_colour_env_create(mx_COLOUR_CTX_VIEW, n_stdout, FAL0);
160    )
161 
162    rv = 0;
163    n_autorec_relax_create();
164    for (ip = msgvec; *ip && PCMP(ip - msgvec, <, msgCount); ++ip) {
165       mp = message + *ip - 1;
166       touch(mp);
167       setdot(mp);
168       n_pstate |= n_PS_DID_PRINT_DOT;
169       uncollapse1(mp, 1);
170       if(!dopipe && ip != msgvec && fprintf(obuf, "\n") < 0){
171          rv = 1;
172          break;
173       }
174       if(action != SEND_MBOX && !a_cmsg_show_overview(obuf, mp, *ip)){
175          rv = 1;
176          break;
177       }
178       if(sendmp(mp, obuf, (doign ? n_IGNORE_TYPE : NULL), NULL, action, mstats
179             ) < 0){
180          rv = 1;
181          break;
182       }
183       n_autorec_relax_unroll();
184       if(formfeed){ /* TODO a nicer way to separate piped messages! */
185          if(putc('\f', obuf) == EOF){
186             rv = 1;
187             break;
188          }
189       }
190       if (tstats != NULL)
191          tstats[0] += mstats[0];
192    }
193    n_autorec_relax_gut();
194    mx_COLOUR(
195       if(!dopipe && (action == SEND_TODISP || action == SEND_TODISP_ALL))
196          mx_colour_env_gut();
197    )
198 
199 jleave:
200    if(obuf != n_stdout)
201       mx_pager_close(obuf);
202    else
203       clearerr(obuf);
204 
205    NYD_OU;
206    return rv;
207 }
208 
209 static int
a_cmsg_pipe1(void * vp,boole doign)210 a_cmsg_pipe1(void *vp, boole doign){
211    u64 stats[1];
212    char const *cmd, *cmdq;
213    int *msgvec, rv;
214    struct mx_cmd_arg *cap;
215    struct mx_cmd_arg_ctx *cacp;
216    NYD2_IN;
217 
218    cacp = vp;
219    cap = cacp->cac_arg;
220    msgvec = cap->ca_arg.ca_msglist;
221    cap = cap->ca_next;
222    rv = 1;
223 
224    if((cmd = cap->ca_arg.ca_str.s)[0] == '\0' &&
225          ((cmd = ok_vlook(cmd)) == NULL || *cmd == '\0')){
226       n_err(_("%s: variable *cmd* not set\n"), cacp->cac_desc->cad_name);
227       goto jleave;
228    }
229 
230    cmdq = n_shexp_quote_cp(cmd, FAL0);
231    fprintf(n_stdout, _("Pipe to: %s\n"), cmdq);
232    stats[0] = 0;
233    if((rv = _type1(msgvec, doign, FAL0, TRU1, FAL0, n_UNCONST(cmd), stats)
234          ) == 0)
235       fprintf(n_stdout, "%s %" PRIu64 " bytes\n", cmdq, stats[0]);
236 jleave:
237    NYD2_OU;
238    return rv;
239 }
240 
241 static int
a_cmsg_top(void * vp,struct n_ignore const * itp)242 a_cmsg_top(void *vp, struct n_ignore const *itp){
243    struct n_string s;
244    int *msgvec, *ip;
245    enum{a_NONE, a_SQUEEZE = 1u<<0,
246       a_EMPTY = 1u<<8, a_STOP = 1u<<9,  a_WORKMASK = 0xFF00u} f;
247    uz tmax, plines;
248    FILE *iobuf, *pbuf;
249    NYD2_IN;
250 
251    if((iobuf = mx_fs_tmp_open("topio", (mx_FS_O_RDWR | mx_FS_O_UNLINK |
252             mx_FS_O_REGISTER), NIL)) == NIL){
253       n_perr(_("top: I/O temporary file"), 0);
254       vp = NIL;
255       goto jleave;
256    }
257    if((pbuf = mx_fs_tmp_open("toppag", (mx_FS_O_RDWR | mx_FS_O_UNLINK |
258             mx_FS_O_REGISTER), NIL)) == NIL)
259       pbuf = n_stdout;
260 
261    /* TODO In v15 we should query the m_message object, and directly send only
262     * TODO those parts, optionally over empty-line-squeeze and quote-strip
263     * TODO filters, in which we are interested in: only text content!
264     * TODO And: with *topsqueeze*, header/content separating empty line.. */
265    n_pstate &= ~n_PS_MSGLIST_DIRECT; /* TODO NO ATTACHMENTS */
266    plines = 0;
267 
268    mx_COLOUR( mx_colour_env_create(mx_COLOUR_CTX_VIEW, iobuf, FAL0); )
269    n_string_creat_auto(&s);
270    /* C99 */{
271       sz l;
272 
273       if((su_idec_sz_cp(&l, ok_vlook(toplines), 0, NULL
274                ) & (su_IDEC_STATE_EMASK | su_IDEC_STATE_CONSUMED)
275             ) != su_IDEC_STATE_CONSUMED)
276          l = 0;
277       if(l <= 0){
278          tmax = n_screensize();
279          if(l < 0){
280             l = ABS(l);
281             tmax >>= l;
282          }
283       }else
284          tmax = (uz)l;
285    }
286    f = ok_blook(topsqueeze) ? a_SQUEEZE : a_NONE;
287 
288    for(ip = msgvec = vp; *ip != 0; ++ip){
289       struct message *mp;
290 
291       mp = &message[*ip - 1];
292       touch(mp);
293       setdot(mp);
294       n_pstate |= n_PS_DID_PRINT_DOT;
295       uncollapse1(mp, 1);
296 
297       rewind(iobuf);
298       if(ftruncate(fileno(iobuf), 0)){
299          n_perr(_("top: ftruncate(2)"), 0);
300          vp = NULL;
301          break;
302       }
303 
304       if(!a_cmsg_show_overview(iobuf, mp, *ip) ||
305             sendmp(mp, iobuf, itp, NULL, SEND_TODISP_ALL, NULL) < 0){
306          n_err(_("top: failed to prepare message %d\n"), *ip);
307          vp = NULL;
308          break;
309       }
310       fflush_rewind(iobuf);
311 
312       /* TODO Skip over the _msg_overview line -- this is a hack to make
313        * TODO colours work: colour contexts should be objects */
314       for(;;){
315          int c;
316 
317          if((c = getc(iobuf)) == EOF || putc(c, pbuf) == EOF){
318             vp = NULL;
319             break;
320          }else if(c == '\n')
321             break;
322       }
323       if(vp == NULL)
324          break;
325       ++plines;
326 
327       /* C99 */{
328          uz l;
329 
330          n_string_trunc(&s, 0);
331          for(l = 0, f &= ~a_WORKMASK; !(f & a_STOP);){
332             int c;
333 
334             if((c = getc(iobuf)) == EOF){
335                f |= a_STOP;
336                c = '\n';
337             }
338 
339             if(c != '\n')
340                n_string_push_c(&s, c);
341             else if((f & a_SQUEEZE) && s.s_len == 0){
342                if(!(f & a_STOP) && ((f & a_EMPTY) || tmax - 1 <= l))
343                   continue;
344                if(putc('\n', pbuf) == EOF){
345                   vp = NULL;
346                   break;
347                }
348                f |= a_EMPTY;
349                ++l;
350             }else{
351                char const *cp, *xcp;
352 
353                cp = n_string_cp_const(&s);
354                /* TODO Brute simple skip part overviews; see above.. */
355                if(!(f & a_SQUEEZE))
356                   c = '\1';
357                else if(s.s_len > 8 &&
358                      (xcp = su_cs_find(cp, "[-- ")) != NULL &&
359                       su_cs_find(&xcp[1], " --]") != NULL)
360                   c = '\0';
361                else{
362                   char const *qcp;
363 
364                   for(qcp = ok_vlook(quote_chars); (c = *cp) != '\0'; ++cp){
365                      if(!su_cs_is_ascii(c))
366                         break;
367                      if(!su_cs_is_space(c)){
368                         if(su_cs_find_c(qcp, c) == NULL)
369                            break;
370                         c = '\0';
371                         break;
372                      }
373                   }
374                }
375 
376                if(c != '\0'){
377                   if(fputs(n_string_cp_const(&s), pbuf) == EOF ||
378                         putc('\n', pbuf) == EOF){
379                      vp = NULL;
380                      break;
381                   }
382                   if(++l >= tmax)
383                      break;
384                   f &= ~a_EMPTY;
385                }else
386                   f |= a_EMPTY;
387                n_string_trunc(&s, 0);
388             }
389          }
390          if(vp == NULL)
391             break;
392          if(l > 0)
393             plines += l;
394          else{
395             if(!(f & a_EMPTY) && putc('\n', pbuf) == EOF){
396                vp = NULL;
397                break;
398             }
399             ++plines;
400          }
401       }
402    }
403 
404    n_string_gut(&s);
405    mx_COLOUR( mx_colour_env_gut(); )
406 
407    if(pbuf != n_stdout){
408       page_or_print(pbuf, plines);
409 
410       mx_fs_close(pbuf);
411    }else
412       clearerr(pbuf);
413 
414    mx_fs_close(iobuf);
415 
416 jleave:
417    NYD2_OU;
418    return (vp != NIL);
419 }
420 
421 static int
delm(int * msgvec)422 delm(int *msgvec)
423 {
424    struct message *mp;
425    int rv = -1, *ip, last;
426    NYD_IN;
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_OU;
447    return rv;
448 }
449 
450 FL int
c_more(void * v)451 c_more(void *v)
452 {
453    int *msgvec = v, rv;
454    NYD_IN;
455 
456    rv = _type1(msgvec, TRU1, TRU1, FAL0, FAL0, NULL, NULL);
457    NYD_OU;
458    return rv;
459 }
460 
461 FL int
c_More(void * v)462 c_More(void *v)
463 {
464    int *msgvec = v, rv;
465    NYD_IN;
466 
467    rv = _type1(msgvec, FAL0, TRU1, FAL0, FAL0, NULL, NULL);
468    NYD_OU;
469    return rv;
470 }
471 
472 FL int
c_type(void * v)473 c_type(void *v)
474 {
475    int *msgvec = v, rv;
476    NYD_IN;
477 
478    rv = _type1(msgvec, TRU1, FAL0, FAL0, FAL0, NULL, NULL);
479    NYD_OU;
480    return rv;
481 }
482 
483 FL int
c_Type(void * v)484 c_Type(void *v)
485 {
486    int *msgvec = v, rv;
487    NYD_IN;
488 
489    rv = _type1(msgvec, FAL0, FAL0, FAL0, FAL0, NULL, NULL);
490    NYD_OU;
491    return rv;
492 }
493 
494 FL int
c_show(void * v)495 c_show(void *v)
496 {
497    int *msgvec = v, rv;
498    NYD_IN;
499 
500    rv = _type1(msgvec, FAL0, FAL0, FAL0, TRU1, NULL, NULL);
501    NYD_OU;
502    return rv;
503 }
504 
505 FL int
c_mimeview(void * vp)506 c_mimeview(void *vp){ /* TODO direct addressable parts, multiple such */
507    struct message *mp;
508    int rv, *msgvec;
509    NYD_IN;
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 = su_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    mx_COLOUR( mx_colour_env_create(mx_COLOUR_CTX_VIEW, n_stdout, FAL0); )
525 
526    if(!a_cmsg_show_overview(n_stdout, mp, *msgvec))
527       n_pstate_err_no = su_ERR_IO;
528    else if(sendmp(mp, n_stdout, n_IGNORE_TYPE, NULL, SEND_TODISP_PARTS,
529          NULL) < 0)
530       n_pstate_err_no = su_ERR_IO;
531    else
532       n_pstate_err_no = su_ERR_NONE;
533 
534    mx_COLOUR( mx_colour_env_gut(); )
535 
536    rv = (n_pstate_err_no != su_ERR_NONE);
537 jleave:
538    NYD_OU;
539    return rv;
540 }
541 
542 FL int
c_pipe(void * vp)543 c_pipe(void *vp){
544    int rv;
545    NYD_IN;
546 
547    rv = a_cmsg_pipe1(vp, TRU1);
548    NYD_OU;
549    return rv;
550 }
551 
552 FL int
c_Pipe(void * vp)553 c_Pipe(void *vp){
554    int rv;
555    NYD_IN;
556 
557    rv = a_cmsg_pipe1(vp, FAL0);
558    NYD_OU;
559    return rv;
560 }
561 
562 FL int
c_top(void * v)563 c_top(void *v){
564    struct n_ignore *itp;
565    int rv;
566    NYD_IN;
567 
568    if(n_ignore_is_any(n_IGNORE_TOP))
569       itp = n_IGNORE_TOP;
570    else{
571       itp = n_ignore_new(TRU1);
572       n_ignore_insert(itp, TRU1, "from", sizeof("from") -1);
573       n_ignore_insert(itp, TRU1, "to", sizeof("to") -1);
574       n_ignore_insert(itp, TRU1, "cc", sizeof("cc") -1);
575       n_ignore_insert(itp, TRU1, "subject", sizeof("subject") -1);
576    }
577 
578    rv = !a_cmsg_top(v, itp);
579    NYD_OU;
580    return rv;
581 }
582 
583 FL int
c_Top(void * v)584 c_Top(void *v){
585    int rv;
586    NYD_IN;
587 
588    rv = !a_cmsg_top(v, n_IGNORE_TYPE);
589    NYD_OU;
590    return rv;
591 }
592 
593 FL int
c_next(void * v)594 c_next(void *v)
595 {
596    int list[2], *ip, *ip2, mdot, *msgvec = v, rv = 1;
597    struct message *mp;
598    NYD_IN;
599 
600    if (*msgvec != 0) {
601       /* If some messages were supplied, find the first applicable one
602        * following dot using wrap around */
603       mdot = (int)P2UZ(dot - message + 1);
604 
605       /* Find first message in supplied message list which follows dot */
606       for (ip = msgvec; *ip != 0; ++ip) {
607          if ((mb.mb_threaded ? message[*ip - 1].m_threadpos > dot->m_threadpos
608                : *ip > mdot))
609             break;
610       }
611       if (*ip == 0)
612          ip = msgvec;
613       ip2 = ip;
614       do {
615          mp = message + *ip2 - 1;
616          if (!(mp->m_flag & MMNDEL)) {
617             setdot(mp);
618             goto jhitit;
619          }
620          if (*ip2 != 0)
621             ++ip2;
622          if (*ip2 == 0)
623             ip2 = msgvec;
624       } while (ip2 != ip);
625       fprintf(n_stdout, _("No messages applicable\n"));
626       goto jleave;
627    }
628 
629    /* If this is the first command, select message 1.  Note that this must
630     * exist for us to get here at all */
631    if (!(n_pstate & n_PS_SAW_COMMAND)) {
632       if (msgCount == 0)
633          goto jateof;
634       goto jhitit;
635    }
636 
637    /* Just find the next good message after dot, no wraparound */
638    if (mb.mb_threaded == 0) {
639       for (mp = dot + !!(n_pstate & n_PS_DID_PRINT_DOT);
640             PCMP(mp, <, message + msgCount); ++mp)
641          if (!(mp->m_flag & MMNORM))
642             break;
643    } else {
644       /* TODO The threading code had some bugs that caused crashes.
645        * TODO The last thing (before the deep look) happens here,
646        * TODO let's not trust n_PS_DID_PRINT_DOT but check & hope it fixes */
647       if ((mp = dot) != NULL && (n_pstate & n_PS_DID_PRINT_DOT))
648          mp = next_in_thread(mp);
649       while (mp != NULL && (mp->m_flag & MMNORM))
650          mp = next_in_thread(mp);
651    }
652    if (mp == NULL || PCMP(mp, >=, message + msgCount)) {
653 jateof:
654       fprintf(n_stdout, _("At EOF\n"));
655       rv = 0;
656       goto jleave;
657    }
658    setdot(mp);
659 
660    /* Print dot */
661 jhitit:
662    list[0] = (int)P2UZ(dot - message + 1);
663    list[1] = 0;
664    rv = c_type(list);
665 jleave:
666    NYD_OU;
667    return rv;
668 }
669 
670 FL int
c_pdot(void * vp)671 c_pdot(void *vp){
672    char cbuf[su_IENC_BUFFER_SIZE], sep1, sep2;
673    struct n_string s_b, *s;
674    int *mlp;
675    struct mx_cmd_arg_ctx *cacp;
676    NYD_IN;
677    UNUSED(vp);
678 
679    n_pstate_err_no = su_ERR_NONE;
680    s = n_string_creat_auto(&s_b);
681    sep1 = *ok_vlook(ifs);
682    sep2 = *ok_vlook(ifs_ws);
683    if(sep1 == sep2)
684       sep2 = '\0';
685    if(sep1 == '\0')
686       sep1 = ' ';
687 
688    cacp = vp;
689 
690    for(mlp = cacp->cac_arg->ca_arg.ca_msglist; *mlp != 0; ++mlp){
691       if(!n_string_can_book(s, su_IENC_BUFFER_SIZE + 2u)){
692          n_err(_("=: overflow: string too long!\n"));
693          n_pstate_err_no = su_ERR_OVERFLOW;
694          vp = NULL;
695          goto jleave;
696       }
697       if(s->s_len > 0){
698          s = n_string_push_c(s, sep1);
699          if(sep2 != '\0')
700             s = n_string_push_c(s, sep2);
701       }
702       s = n_string_push_cp(s,
703             su_ienc(cbuf, (u32)*mlp, 10, su_IENC_MODE_NONE));
704    }
705 
706    (void)n_string_cp(s);
707    if(cacp->cac_vput == NULL){
708       if(fprintf(n_stdout, "%s\n", s->s_dat) < 0){
709          n_pstate_err_no = su_err_no();
710          vp = NULL;
711       }
712    }else if(!n_var_vset(cacp->cac_vput, (up)s->s_dat)){
713       n_pstate_err_no = su_ERR_NOTSUP;
714       vp = NULL;
715    }
716 jleave:
717    /* n_string_gut(s); */
718    NYD_OU;
719    return (vp == NULL);
720 }
721 
722 FL int
c_messize(void * v)723 c_messize(void *v)
724 {
725    int *msgvec = v, *ip, mesg;
726    struct message *mp;
727    NYD_IN;
728 
729    for (ip = msgvec; *ip != 0; ++ip) {
730       mesg = *ip;
731       mp = message + mesg - 1;
732       fprintf(n_stdout, "%d: ", mesg);
733       if (mp->m_xlines > 0)
734          fprintf(n_stdout, "%ld", mp->m_xlines);
735       else
736          putc(' ', n_stdout);
737       fprintf(n_stdout, "/%lu\n", (ul)mp->m_xsize);
738    }
739    NYD_OU;
740    return 0;
741 }
742 
743 FL int
c_delete(void * v)744 c_delete(void *v)
745 {
746    int *msgvec = v;
747    NYD_IN;
748 
749    delm(msgvec);
750    NYD_OU;
751    return 0;
752 }
753 
754 FL int
c_deltype(void * v)755 c_deltype(void *v)
756 {
757    int list[2], rv = 0, *msgvec = v, lastdot;
758    NYD_IN;
759 
760    lastdot = (int)P2UZ(dot - message + 1);
761    if (delm(msgvec) >= 0) {
762       list[0] = (int)P2UZ(dot - message + 1);
763       if (list[0] > lastdot) {
764          touch(dot);
765          list[1] = 0;
766          rv = c_type(list);
767          goto jleave;
768       }
769       fprintf(n_stdout, _("At EOF\n"));
770    } else
771       fprintf(n_stdout, _("No more messages\n"));
772 jleave:
773    NYD_OU;
774    return rv;
775 }
776 
777 FL int
c_undelete(void * v)778 c_undelete(void *v)
779 {
780    int *msgvec = v, *ip;
781    struct message *mp;
782    NYD_IN;
783 
784    for (ip = msgvec; *ip != 0; ++ip) {
785       mp = &message[*ip - 1];
786       touch(mp);
787       setdot(mp);
788       if (mp->m_flag & (MDELETED | MSAVED))
789          mp->m_flag &= ~(MDELETED | MSAVED);
790       else
791          mp->m_flag &= ~MDELETED;
792 #ifdef mx_HAVE_IMAP
793       if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
794          imap_undelete(mp, *ip);
795 #endif
796    }
797    NYD_OU;
798    return 0;
799 }
800 
801 FL int
c_stouch(void * v)802 c_stouch(void *v)
803 {
804    int *msgvec = v, *ip;
805    NYD_IN;
806 
807    for (ip = msgvec; *ip != 0; ++ip) {
808       setdot(message + *ip - 1);
809       dot->m_flag |= MTOUCH;
810       dot->m_flag &= ~MPRESERVE;
811       n_pstate |= n_PS_DID_PRINT_DOT;
812    }
813    NYD_OU;
814    return 0;
815 }
816 
817 FL int
c_mboxit(void * v)818 c_mboxit(void *v)
819 {
820    int *msgvec = v, *ip;
821    NYD_IN;
822 
823    if (n_pstate & n_PS_EDIT) {
824       n_err(_("mbox: can only be used in a system mailbox\n")); /* TODO */
825       goto jleave;
826    }
827 
828    for (ip = msgvec; *ip != 0; ++ip) {
829       setdot(message + *ip - 1);
830       dot->m_flag |= MTOUCH | MBOX;
831       dot->m_flag &= ~MPRESERVE;
832       n_pstate |= n_PS_DID_PRINT_DOT;
833    }
834 jleave:
835    NYD_OU;
836    return 0;
837 }
838 
839 FL int
c_preserve(void * v)840 c_preserve(void *v)
841 {
842    int *msgvec = v, *ip, mesg, rv = 1;
843    struct message *mp;
844    NYD_IN;
845 
846    if (n_pstate & n_PS_EDIT) {
847       fprintf(n_stdout, _("preserve: cannot be used in a system mailbox\n"));
848       goto jleave;
849    }
850 
851    for (ip = msgvec; *ip != 0; ++ip) {
852       mesg = *ip;
853       mp = message + mesg - 1;
854       mp->m_flag |= MPRESERVE;
855       mp->m_flag &= ~MBOX;
856       setdot(mp);
857       n_pstate |= n_PS_DID_PRINT_DOT;
858    }
859    rv = 0;
860 jleave:
861    NYD_OU;
862    return rv;
863 }
864 
865 FL int
c_unread(void * v)866 c_unread(void *v)
867 {
868    struct message *mp;
869    int *msgvec = v, *ip;
870    NYD_IN;
871 
872    for (ip = msgvec; *ip != 0; ++ip) {
873       mp = &message[*ip - 1];
874       setdot(mp);
875       dot->m_flag &= ~(MREAD | MTOUCH);
876       dot->m_flag |= MSTATUS;
877 #ifdef mx_HAVE_IMAP
878       if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
879          imap_unread(mp, *ip); /* TODO return? */
880 #endif
881       n_pstate |= n_PS_DID_PRINT_DOT;
882    }
883    NYD_OU;
884    return 0;
885 }
886 
887 FL int
c_seen(void * v)888 c_seen(void *v)
889 {
890    int *msgvec = v, *ip;
891    NYD_IN;
892 
893    for (ip = msgvec; *ip != 0; ++ip) {
894       struct message *mp = message + *ip - 1;
895       setdot(mp);
896       touch(mp);
897    }
898    NYD_OU;
899    return 0;
900 }
901 
902 FL int
c_flag(void * v)903 c_flag(void *v)
904 {
905    struct message *m;
906    int *msgvec = v, *ip;
907    NYD_IN;
908 
909    for (ip = msgvec; *ip != 0; ++ip) {
910       m = message + *ip - 1;
911       setdot(m);
912       if (!(m->m_flag & (MFLAG | MFLAGGED)))
913          m->m_flag |= MFLAG | MFLAGGED;
914    }
915    NYD_OU;
916    return 0;
917 }
918 
919 FL int
c_unflag(void * v)920 c_unflag(void *v)
921 {
922    struct message *m;
923    int *msgvec = v, *ip;
924    NYD_IN;
925 
926    for (ip = msgvec; *ip != 0; ++ip) {
927       m = message + *ip - 1;
928       setdot(m);
929       if (m->m_flag & (MFLAG | MFLAGGED)) {
930          m->m_flag &= ~(MFLAG | MFLAGGED);
931          m->m_flag |= MUNFLAG;
932       }
933    }
934    NYD_OU;
935    return 0;
936 }
937 
938 FL int
c_answered(void * v)939 c_answered(void *v)
940 {
941    struct message *m;
942    int *msgvec = v, *ip;
943    NYD_IN;
944 
945    for (ip = msgvec; *ip != 0; ++ip) {
946       m = message + *ip - 1;
947       setdot(m);
948       if (!(m->m_flag & (MANSWER | MANSWERED)))
949          m->m_flag |= MANSWER | MANSWERED;
950    }
951    NYD_OU;
952    return 0;
953 }
954 
955 FL int
c_unanswered(void * v)956 c_unanswered(void *v)
957 {
958    struct message *m;
959    int *msgvec = v, *ip;
960    NYD_IN;
961 
962    for (ip = msgvec; *ip != 0; ++ip) {
963       m = message + *ip - 1;
964       setdot(m);
965       if (m->m_flag & (MANSWER | MANSWERED)) {
966          m->m_flag &= ~(MANSWER | MANSWERED);
967          m->m_flag |= MUNANSWER;
968       }
969    }
970    NYD_OU;
971    return 0;
972 }
973 
974 FL int
c_draft(void * v)975 c_draft(void *v)
976 {
977    struct message *m;
978    int *msgvec = v, *ip;
979    NYD_IN;
980 
981    for (ip = msgvec; *ip != 0; ++ip) {
982       m = message + *ip - 1;
983       setdot(m);
984       if (!(m->m_flag & (MDRAFT | MDRAFTED)))
985          m->m_flag |= MDRAFT | MDRAFTED;
986    }
987    NYD_OU;
988    return 0;
989 }
990 
991 FL int
c_undraft(void * v)992 c_undraft(void *v)
993 {
994    struct message *m;
995    int *msgvec = v, *ip;
996    NYD_IN;
997 
998    for (ip = msgvec; *ip != 0; ++ip) {
999       m = message + *ip - 1;
1000       setdot(m);
1001       if (m->m_flag & (MDRAFT | MDRAFTED)) {
1002          m->m_flag &= ~(MDRAFT | MDRAFTED);
1003          m->m_flag |= MUNDRAFT;
1004       }
1005    }
1006    NYD_OU;
1007    return 0;
1008 }
1009 
1010 #include "su/code-ou.h"
1011 /* s-it-mode */
1012