README: review
[s-mailx.git] / cmd-msg.c
blob479cc0f6f8b30204704f3a427f90cb51ed1af8bb
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Iterating over, and over such housekeeping message user commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8 /*
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
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.
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.
36 #undef n_FILE
37 #define n_FILE cmd_msg
39 #ifndef HAVE_AMALGAMATION
40 # include "nail.h"
41 #endif
43 /* Prepare and print "[Message: xy]:" intro */
44 static bool_t a_cmsg_show_overview(FILE *obuf, struct message *mp, int msg_no);
46 /* Show the requested messages */
47 static int _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
48 bool_t donotdecode, char *cmd, ui64_t *tstats);
50 /* Pipe the requested messages */
51 static int a_cmsg_pipe1(void *vp, bool_t doign);
53 /* `top' / `Top' */
54 static int a_cmsg_top(void *vp, struct n_ignore const *itp);
56 /* Delete the indicated messages. Set dot to some nice place afterwards */
57 static int delm(int *msgvec);
59 static bool_t
60 a_cmsg_show_overview(FILE *obuf, struct message *mp, int msg_no){
61 bool_t rv;
62 char const *cpre, *csuf;
63 NYD2_ENTER;
65 cpre = csuf = n_empty;
66 #ifdef HAVE_COLOUR
67 if(n_COLOUR_IS_ACTIVE()){
68 struct n_colour_pen *cpen;
70 if((cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_MSGINFO, NULL)) != NULL){
71 struct str const *sp;
73 if((sp = n_colour_pen_to_str(cpen)) != NULL)
74 cpre = sp->s;
75 if((sp = n_colour_reset_to_str()) != NULL)
76 csuf = sp->s;
79 #endif
80 /* XXX Message info uses wire format for line count */
81 rv = (fprintf(obuf,
82 A_("%s[-- Message %2d -- %lu lines, %lu bytes --]:%s\n"),
83 cpre, msg_no, (ul_i)mp->m_lines, (ul_i)mp->m_size, csuf) > 0);
84 NYD2_LEAVE;
85 return rv;
88 static int
89 _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe,
90 bool_t donotdecode, char *cmd, ui64_t *tstats)
92 ui64_t mstats[1];
93 int *ip;
94 struct message *mp;
95 char const *cp;
96 enum sendaction action;
97 bool_t volatile formfeed;
98 FILE * volatile obuf;
99 int volatile rv;
100 NYD_ENTER;
102 rv = 1;
103 obuf = n_stdout;
104 formfeed = (dopipe && ok_blook(page));
105 action = ((dopipe && ok_blook(piperaw))
106 ? SEND_MBOX : donotdecode
107 ? SEND_SHOW : doign
108 ? SEND_TODISP : SEND_TODISP_ALL);
110 if (dopipe) {
111 if ((obuf = Popen(cmd, "w", ok_vlook(SHELL), NULL, 1)) == NULL) {
112 n_perr(cmd, 0);
113 obuf = n_stdout;
115 } else if ((n_psonce & n_PSO_TTYOUT) && (dopage ||
116 ((n_psonce & n_PSO_INTERACTIVE) && (cp = ok_vlook(crt)) != NULL))) {
117 uiz_t nlines, lib;
119 nlines = 0;
121 if (!dopage) {
122 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
123 mp = message + *ip - 1;
124 if (!(mp->m_content_info & CI_HAVE_BODY))
125 if (get_body(mp) != OKAY)
126 goto jleave;
127 nlines += mp->m_lines + 1; /* TODO BUT wire format, not display! */
131 /* >= not <: we return to the prompt */
132 if(dopage || nlines >= (*cp != '\0'
133 ? (n_idec_uiz_cp(&lib, cp, 0, NULL), lib)
134 : (uiz_t)n_realscreenheight)){
135 if((obuf = n_pager_open()) == NULL)
136 obuf = n_stdout;
138 n_COLOUR(
139 if(action == SEND_TODISP || action == SEND_TODISP_ALL)
140 n_colour_env_create(n_COLOUR_CTX_VIEW, obuf, obuf != n_stdout);
143 n_COLOUR(
144 else if(action == SEND_TODISP || action == SEND_TODISP_ALL)
145 n_colour_env_create(n_COLOUR_CTX_VIEW, n_stdout, FAL0);
148 rv = 0;
149 srelax_hold();
150 for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) {
151 mp = message + *ip - 1;
152 touch(mp);
153 setdot(mp);
154 n_pstate |= n_PS_DID_PRINT_DOT;
155 uncollapse1(mp, 1);
156 if(!dopipe && ip != msgvec && fprintf(obuf, "\n") < 0){
157 rv = 1;
158 break;
160 if(action != SEND_MBOX && !a_cmsg_show_overview(obuf, mp, *ip)){
161 rv = 1;
162 break;
164 if(sendmp(mp, obuf, (doign ? n_IGNORE_TYPE : NULL), NULL, action, mstats
165 ) < 0){
166 rv = 1;
167 break;
169 srelax();
170 if(formfeed){ /* TODO a nicer way to separate piped messages! */
171 if(putc('\f', obuf) == EOF){
172 rv = 1;
173 break;
176 if (tstats != NULL)
177 tstats[0] += mstats[0];
179 srelax_rele();
180 n_COLOUR(
181 if(!dopipe && (action == SEND_TODISP || action == SEND_TODISP_ALL))
182 n_colour_env_gut();
184 jleave:
185 if (obuf != n_stdout)
186 n_pager_close(obuf);
187 NYD_LEAVE;
188 return rv;
191 static int
192 a_cmsg_pipe1(void *vp, bool_t doign){
193 ui64_t stats[1];
194 char const *cmd, *cmdq;
195 int *msgvec, rv;
196 struct n_cmd_arg *cap;
197 struct n_cmd_arg_ctx *cacp;
198 NYD2_ENTER;
200 cacp = vp;
201 cap = cacp->cac_arg;
202 msgvec = cap->ca_arg.ca_msglist;
203 cap = cap->ca_next;
204 rv = 1;
206 if((cmd = cap->ca_arg.ca_str.s)[0] == '\0' &&
207 ((cmd = ok_vlook(cmd)) == NULL || *cmd == '\0')){
208 n_err(_("%s: variable *cmd* not set\n"), cacp->cac_desc->cad_name);
209 goto jleave;
212 cmdq = n_shexp_quote_cp(cmd, FAL0);
213 fprintf(n_stdout, _("Pipe to: %s\n"), cmdq);
214 stats[0] = 0;
215 if((rv = _type1(msgvec, doign, FAL0, TRU1, FAL0, n_UNCONST(cmd), stats)
216 ) == 0)
217 fprintf(n_stdout, "%s %" PRIu64 " bytes\n", cmdq, stats[0]);
218 jleave:
219 NYD2_LEAVE;
220 return rv;
223 static int
224 a_cmsg_top(void *vp, struct n_ignore const *itp){
225 struct n_string s;
226 int *msgvec, *ip;
227 enum{a_NONE, a_SQUEEZE = 1u<<0,
228 a_EMPTY = 1u<<8, a_STOP = 1u<<9, a_WORKMASK = 0xFF00u} f;
229 size_t tmax, plines;
230 FILE *iobuf, *pbuf;
231 NYD2_ENTER;
233 if((iobuf = Ftmp(NULL, "topio", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL){
234 n_perr(_("`top': I/O temporary file"), 0);
235 vp = NULL;
236 goto jleave;
238 if((pbuf = Ftmp(NULL, "toppag", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL){
239 n_perr(_("`top': temporary pager file"), 0);
240 vp = NULL;
241 goto jleave1;
244 /* TODO In v15 we should query the m_message object, and directly send only
245 * TODO those parts, optionally over empty-line-squeeze and quote-strip
246 * TODO filters, in which we are interested in: only text content!
247 * TODO And: with *topsqueeze*, header/content separating empty line.. */
248 n_pstate &= ~n_PS_MSGLIST_DIRECT; /* TODO NO ATTACHMENTS */
249 plines = 0;
251 n_COLOUR( n_colour_env_create(n_COLOUR_CTX_VIEW, iobuf, FAL0); )
252 n_string_creat_auto(&s);
253 /* C99 */{
254 siz_t l;
256 if((n_idec_siz_cp(&l, ok_vlook(toplines), 0, NULL
257 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
258 ) != n_IDEC_STATE_CONSUMED)
259 l = 0;
260 if(l <= 0){
261 tmax = n_screensize();
262 if(l < 0){
263 l = n_ABS(l);
264 tmax >>= l;
266 }else
267 tmax = (size_t)l;
269 f = ok_blook(topsqueeze) ? a_SQUEEZE : a_NONE;
271 for(ip = msgvec = vp; *ip != 0; ++ip){
272 struct message *mp;
274 mp = &message[*ip - 1];
275 touch(mp);
276 setdot(mp);
277 n_pstate |= n_PS_DID_PRINT_DOT;
278 uncollapse1(mp, 1);
280 rewind(iobuf);
281 if(ftruncate(fileno(iobuf), 0)){
282 n_perr(_("`top': ftruncate(2)"), 0);
283 vp = NULL;
284 break;
287 if(!a_cmsg_show_overview(iobuf, mp, *ip) ||
288 sendmp(mp, iobuf, itp, NULL, SEND_TODISP_ALL, NULL) < 0){
289 n_err(_("`top': failed to prepare message %d\n"), *ip);
290 vp = NULL;
291 break;
293 fflush_rewind(iobuf);
295 /* TODO Skip over the _msg_overview line -- this is a hack to make
296 * TODO colours work: colour contexts should be objects */
297 for(;;){
298 int c;
300 if((c = getc(iobuf)) == EOF || putc(c, pbuf) == EOF){
301 vp = NULL;
302 break;
303 }else if(c == '\n')
304 break;
306 if(vp == NULL)
307 break;
308 ++plines;
310 /* C99 */{
311 size_t l;
313 n_string_trunc(&s, 0);
314 for(l = 0, f &= ~a_WORKMASK; !(f & a_STOP);){
315 int c;
317 if((c = getc(iobuf)) == EOF){
318 f |= a_STOP;
319 c = '\n';
322 if(c != '\n')
323 n_string_push_c(&s, c);
324 else if((f & a_SQUEEZE) && s.s_len == 0){
325 if(!(f & a_STOP) && ((f & a_EMPTY) || tmax - 1 <= l))
326 continue;
327 if(putc('\n', pbuf) == EOF){
328 vp = NULL;
329 break;
331 f |= a_EMPTY;
332 ++l;
333 }else{
334 char const *cp, *xcp;
336 cp = n_string_cp_const(&s);
337 /* TODO Brute simple skip part overviews; see above.. */
338 if(!(f & a_SQUEEZE))
339 c = '\1';
340 else if(s.s_len > 8 &&
341 (xcp = strstr(cp, "[-- ")) != NULL &&
342 strstr(&xcp[1], " --]") != NULL)
343 c = '\0';
344 else{
345 char const *qcp;
347 for(qcp = ok_vlook(quote_chars); (c = *cp) != '\0'; ++cp){
348 if(!asciichar(c))
349 break;
350 if(!blankspacechar(c)){
351 if(strchr(qcp, c) == NULL)
352 break;
353 c = '\0';
354 break;
359 if(c != '\0'){
360 if(fputs(n_string_cp_const(&s), pbuf) == EOF ||
361 putc('\n', pbuf) == EOF){
362 vp = NULL;
363 break;
365 if(++l >= tmax)
366 break;
367 f &= ~a_EMPTY;
368 }else
369 f |= a_EMPTY;
370 n_string_trunc(&s, 0);
373 if(vp == NULL)
374 break;
375 if(l > 0)
376 plines += l;
377 else{
378 if(!(f & a_EMPTY) && putc('\n', pbuf) == EOF){
379 vp = NULL;
380 break;
382 ++plines;
387 n_string_gut(&s);
388 n_COLOUR( n_colour_env_gut(); )
390 fflush(pbuf);
391 page_or_print(pbuf, plines);
393 Fclose(pbuf);
394 jleave1:
395 Fclose(iobuf);
396 jleave:
397 NYD2_LEAVE;
398 return (vp != NULL);
401 static int
402 delm(int *msgvec)
404 struct message *mp;
405 int rv = -1, *ip, last;
406 NYD_ENTER;
408 last = 0;
409 for (ip = msgvec; *ip != 0; ++ip) {
410 mp = message + *ip - 1;
411 touch(mp);
412 mp->m_flag |= MDELETED | MTOUCH;
413 mp->m_flag &= ~(MPRESERVE | MSAVED | MBOX);
414 last = *ip;
416 if (last != 0) {
417 setdot(message + last - 1);
418 last = first(0, MDELETED);
419 if (last != 0) {
420 setdot(message + last - 1);
421 rv = 0;
422 } else {
423 setdot(message);
426 NYD_LEAVE;
427 return rv;
430 FL int
431 c_more(void *v)
433 int *msgvec = v, rv;
434 NYD_ENTER;
436 rv = _type1(msgvec, TRU1, TRU1, FAL0, FAL0, NULL, NULL);
437 NYD_LEAVE;
438 return rv;
441 FL int
442 c_More(void *v)
444 int *msgvec = v, rv;
445 NYD_ENTER;
447 rv = _type1(msgvec, FAL0, TRU1, FAL0, FAL0, NULL, NULL);
448 NYD_LEAVE;
449 return rv;
452 FL int
453 c_type(void *v)
455 int *msgvec = v, rv;
456 NYD_ENTER;
458 rv = _type1(msgvec, TRU1, FAL0, FAL0, FAL0, NULL, NULL);
459 NYD_LEAVE;
460 return rv;
463 FL int
464 c_Type(void *v)
466 int *msgvec = v, rv;
467 NYD_ENTER;
469 rv = _type1(msgvec, FAL0, FAL0, FAL0, FAL0, NULL, NULL);
470 NYD_LEAVE;
471 return rv;
474 FL int
475 c_show(void *v)
477 int *msgvec = v, rv;
478 NYD_ENTER;
480 rv = _type1(msgvec, FAL0, FAL0, FAL0, TRU1, NULL, NULL);
481 NYD_LEAVE;
482 return rv;
485 FL int
486 c_mimeview(void *vp){ /* TODO direct addressable parts, multiple such */
487 struct message *mp;
488 int rv, *msgvec;
489 NYD_ENTER;
491 if((msgvec = vp)[1] != 0){
492 n_err(_("`mimeview': can yet only take one message, sorry!\n"));/* TODO */
493 n_pstate_err_no = n_ERR_NOTSUP;
494 rv = 1;
495 goto jleave;
498 mp = &message[*msgvec - 1];
499 touch(mp);
500 setdot(mp);
501 n_pstate |= n_PS_DID_PRINT_DOT;
502 uncollapse1(mp, 1);
504 n_COLOUR(
505 n_colour_env_create(n_COLOUR_CTX_VIEW, n_stdout, FAL0);
508 if(!a_cmsg_show_overview(n_stdout, mp, *msgvec))
509 n_pstate_err_no = n_ERR_IO;
510 else if(sendmp(mp, n_stdout, n_IGNORE_TYPE, NULL, SEND_TODISP_PARTS,
511 NULL) < 0)
512 n_pstate_err_no = n_ERR_IO;
513 else
514 n_pstate_err_no = n_ERR_NONE;
516 n_COLOUR(
517 n_colour_env_gut();
520 rv = (n_pstate_err_no != n_ERR_NONE);
521 jleave:
522 NYD_LEAVE;
523 return rv;
526 FL int
527 c_pipe(void *vp){
528 int rv;
529 NYD_ENTER;
531 rv = a_cmsg_pipe1(vp, TRU1);
532 NYD_LEAVE;
533 return rv;
536 FL int
537 c_Pipe(void *vp){
538 int rv;
539 NYD_ENTER;
541 rv = a_cmsg_pipe1(vp, FAL0);
542 NYD_LEAVE;
543 return rv;
546 FL int
547 c_top(void *v){
548 struct n_ignore *itp;
549 int rv;
550 NYD_ENTER;
552 if(n_ignore_is_any(n_IGNORE_TOP))
553 itp = n_IGNORE_TOP;
554 else{
555 itp = n_ignore_new(TRU1);
556 n_ignore_insert(itp, TRU1, "from", sizeof("from") -1);
557 n_ignore_insert(itp, TRU1, "to", sizeof("to") -1);
558 n_ignore_insert(itp, TRU1, "cc", sizeof("cc") -1);
559 n_ignore_insert(itp, TRU1, "subject", sizeof("subject") -1);
562 rv = !a_cmsg_top(v, itp);
563 NYD_LEAVE;
564 return rv;
567 FL int
568 c_Top(void *v){
569 int rv;
570 NYD_ENTER;
572 rv = !a_cmsg_top(v, n_IGNORE_TYPE);
573 NYD_LEAVE;
574 return rv;
577 FL int
578 c_next(void *v)
580 int list[2], *ip, *ip2, mdot, *msgvec = v, rv = 1;
581 struct message *mp;
582 NYD_ENTER;
584 if (*msgvec != 0) {
585 /* If some messages were supplied, find the first applicable one
586 * following dot using wrap around */
587 mdot = (int)PTR2SIZE(dot - message + 1);
589 /* Find first message in supplied message list which follows dot */
590 for (ip = msgvec; *ip != 0; ++ip) {
591 if ((mb.mb_threaded ? message[*ip - 1].m_threadpos > dot->m_threadpos
592 : *ip > mdot))
593 break;
595 if (*ip == 0)
596 ip = msgvec;
597 ip2 = ip;
598 do {
599 mp = message + *ip2 - 1;
600 if (!(mp->m_flag & MMNDEL)) {
601 setdot(mp);
602 goto jhitit;
604 if (*ip2 != 0)
605 ++ip2;
606 if (*ip2 == 0)
607 ip2 = msgvec;
608 } while (ip2 != ip);
609 fprintf(n_stdout, _("No messages applicable\n"));
610 goto jleave;
613 /* If this is the first command, select message 1. Note that this must
614 * exist for us to get here at all */
615 if (!(n_pstate & n_PS_SAW_COMMAND)) {
616 if (msgCount == 0)
617 goto jateof;
618 goto jhitit;
621 /* Just find the next good message after dot, no wraparound */
622 if (mb.mb_threaded == 0) {
623 for (mp = dot + !!(n_pstate & n_PS_DID_PRINT_DOT);
624 PTRCMP(mp, <, message + msgCount); ++mp)
625 if (!(mp->m_flag & MMNORM))
626 break;
627 } else {
628 /* TODO The threading code had some bugs that caused crashes.
629 * TODO The last thing (before the deep look) happens here,
630 * TODO so let's not trust n_PS_DID_PRINT_DOT but check & hope it fixes */
631 if ((mp = dot) != NULL && (n_pstate & n_PS_DID_PRINT_DOT))
632 mp = next_in_thread(mp);
633 while (mp != NULL && (mp->m_flag & MMNORM))
634 mp = next_in_thread(mp);
636 if (mp == NULL || PTRCMP(mp, >=, message + msgCount)) {
637 jateof:
638 fprintf(n_stdout, _("At EOF\n"));
639 rv = 0;
640 goto jleave;
642 setdot(mp);
644 /* Print dot */
645 jhitit:
646 list[0] = (int)PTR2SIZE(dot - message + 1);
647 list[1] = 0;
648 rv = c_type(list);
649 jleave:
650 NYD_LEAVE;
651 return rv;
654 FL int
655 c_pdot(void *vp){
656 char cbuf[n_IENC_BUFFER_SIZE], sep1, sep2;
657 struct n_string s, *sp;
658 int *mlp;
659 struct n_cmd_arg_ctx *cacp;
660 NYD_ENTER;
661 n_UNUSED(vp);
663 n_pstate_err_no = n_ERR_NONE;
664 sp = n_string_creat_auto(&s);
665 sep1 = *ok_vlook(ifs);
666 sep2 = *ok_vlook(ifs_ws);
667 if(sep1 == sep2)
668 sep2 = '\0';
669 if(sep1 == '\0')
670 sep1 = ' ';
672 cacp = vp;
674 for(mlp = cacp->cac_arg->ca_arg.ca_msglist; *mlp != 0; ++mlp){
675 if(!n_string_can_book(sp, n_IENC_BUFFER_SIZE + 2u)){
676 n_err(_("`=': overflow: string too long!\n"));
677 n_pstate_err_no = n_ERR_OVERFLOW;
678 vp = NULL;
679 goto jleave;
681 if(sp->s_len > 0){
682 sp = n_string_push_c(sp, sep1);
683 if(sep2 != '\0')
684 sp = n_string_push_c(sp, sep2);
686 sp = n_string_push_cp(sp,
687 n_ienc_buf(cbuf, (ui32_t)*mlp, 10, n_IENC_MODE_NONE));
690 (void)n_string_cp(sp);
691 if(cacp->cac_vput == NULL){
692 if(fprintf(n_stdout, "%s\n", sp->s_dat) < 0){
693 n_pstate_err_no = n_err_no;
694 vp = NULL;
696 }else if(!n_var_vset(cacp->cac_vput, (uintptr_t)sp->s_dat)){
697 n_pstate_err_no = n_ERR_NOTSUP;
698 vp = NULL;
700 jleave:
701 /* n_string_gut(sp); */
702 NYD_LEAVE;
703 return (vp == NULL);
706 FL int
707 c_messize(void *v)
709 int *msgvec = v, *ip, mesg;
710 struct message *mp;
711 NYD_ENTER;
713 for (ip = msgvec; *ip != 0; ++ip) {
714 mesg = *ip;
715 mp = message + mesg - 1;
716 fprintf(n_stdout, "%d: ", mesg);
717 if (mp->m_xlines > 0)
718 fprintf(n_stdout, "%ld", mp->m_xlines);
719 else
720 putc(' ', n_stdout);
721 fprintf(n_stdout, "/%lu\n", (ul_i)mp->m_xsize);
723 NYD_LEAVE;
724 return 0;
727 FL int
728 c_delete(void *v)
730 int *msgvec = v;
731 NYD_ENTER;
733 delm(msgvec);
734 NYD_LEAVE;
735 return 0;
738 FL int
739 c_deltype(void *v)
741 int list[2], rv = 0, *msgvec = v, lastdot;
742 NYD_ENTER;
744 lastdot = (int)PTR2SIZE(dot - message + 1);
745 if (delm(msgvec) >= 0) {
746 list[0] = (int)PTR2SIZE(dot - message + 1);
747 if (list[0] > lastdot) {
748 touch(dot);
749 list[1] = 0;
750 rv = c_type(list);
751 goto jleave;
753 fprintf(n_stdout, _("At EOF\n"));
754 } else
755 fprintf(n_stdout, _("No more messages\n"));
756 jleave:
757 NYD_LEAVE;
758 return rv;
761 FL int
762 c_undelete(void *v)
764 int *msgvec = v, *ip;
765 struct message *mp;
766 NYD_ENTER;
768 for (ip = msgvec; *ip != 0; ++ip) {
769 mp = &message[*ip - 1];
770 touch(mp);
771 setdot(mp);
772 if (mp->m_flag & (MDELETED | MSAVED))
773 mp->m_flag &= ~(MDELETED | MSAVED);
774 else
775 mp->m_flag &= ~MDELETED;
776 #ifdef HAVE_IMAP
777 if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
778 imap_undelete(mp, *ip);
779 #endif
781 NYD_LEAVE;
782 return 0;
785 FL int
786 c_stouch(void *v)
788 int *msgvec = v, *ip;
789 NYD_ENTER;
791 for (ip = msgvec; *ip != 0; ++ip) {
792 setdot(message + *ip - 1);
793 dot->m_flag |= MTOUCH;
794 dot->m_flag &= ~MPRESERVE;
795 n_pstate |= n_PS_DID_PRINT_DOT;
797 NYD_LEAVE;
798 return 0;
801 FL int
802 c_mboxit(void *v)
804 int *msgvec = v, *ip;
805 NYD_ENTER;
807 if (n_pstate & n_PS_EDIT) {
808 n_err(_("`mbox' can only be used in a system mailbox\n")); /* TODO */
809 goto jleave;
812 for (ip = msgvec; *ip != 0; ++ip) {
813 setdot(message + *ip - 1);
814 dot->m_flag |= MTOUCH | MBOX;
815 dot->m_flag &= ~MPRESERVE;
816 n_pstate |= n_PS_DID_PRINT_DOT;
818 jleave:
819 NYD_LEAVE;
820 return 0;
823 FL int
824 c_preserve(void *v)
826 int *msgvec = v, *ip, mesg, rv = 1;
827 struct message *mp;
828 NYD_ENTER;
830 if (n_pstate & n_PS_EDIT) {
831 fprintf(n_stdout, _("Cannot `preserve' in a system mailbox\n"));
832 goto jleave;
835 for (ip = msgvec; *ip != 0; ++ip) {
836 mesg = *ip;
837 mp = message + mesg - 1;
838 mp->m_flag |= MPRESERVE;
839 mp->m_flag &= ~MBOX;
840 setdot(mp);
841 n_pstate |= n_PS_DID_PRINT_DOT;
843 rv = 0;
844 jleave:
845 NYD_LEAVE;
846 return rv;
849 FL int
850 c_unread(void *v)
852 struct message *mp;
853 int *msgvec = v, *ip;
854 NYD_ENTER;
856 for (ip = msgvec; *ip != 0; ++ip) {
857 mp = &message[*ip - 1];
858 setdot(mp);
859 dot->m_flag &= ~(MREAD | MTOUCH);
860 dot->m_flag |= MSTATUS;
861 #ifdef HAVE_IMAP
862 if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
863 imap_unread(mp, *ip); /* TODO return? */
864 #endif
865 n_pstate |= n_PS_DID_PRINT_DOT;
867 NYD_LEAVE;
868 return 0;
871 FL int
872 c_seen(void *v)
874 int *msgvec = v, *ip;
875 NYD_ENTER;
877 for (ip = msgvec; *ip != 0; ++ip) {
878 struct message *mp = message + *ip - 1;
879 setdot(mp);
880 touch(mp);
882 NYD_LEAVE;
883 return 0;
886 FL int
887 c_flag(void *v)
889 struct message *m;
890 int *msgvec = v, *ip;
891 NYD_ENTER;
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;
899 NYD_LEAVE;
900 return 0;
903 FL int
904 c_unflag(void *v)
906 struct message *m;
907 int *msgvec = v, *ip;
908 NYD_ENTER;
910 for (ip = msgvec; *ip != 0; ++ip) {
911 m = message + *ip - 1;
912 setdot(m);
913 if (m->m_flag & (MFLAG | MFLAGGED)) {
914 m->m_flag &= ~(MFLAG | MFLAGGED);
915 m->m_flag |= MUNFLAG;
918 NYD_LEAVE;
919 return 0;
922 FL int
923 c_answered(void *v)
925 struct message *m;
926 int *msgvec = v, *ip;
927 NYD_ENTER;
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;
935 NYD_LEAVE;
936 return 0;
939 FL int
940 c_unanswered(void *v)
942 struct message *m;
943 int *msgvec = v, *ip;
944 NYD_ENTER;
946 for (ip = msgvec; *ip != 0; ++ip) {
947 m = message + *ip - 1;
948 setdot(m);
949 if (m->m_flag & (MANSWER | MANSWERED)) {
950 m->m_flag &= ~(MANSWER | MANSWERED);
951 m->m_flag |= MUNANSWER;
954 NYD_LEAVE;
955 return 0;
958 FL int
959 c_draft(void *v)
961 struct message *m;
962 int *msgvec = v, *ip;
963 NYD_ENTER;
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;
971 NYD_LEAVE;
972 return 0;
975 FL int
976 c_undraft(void *v)
978 struct message *m;
979 int *msgvec = v, *ip;
980 NYD_ENTER;
982 for (ip = msgvec; *ip != 0; ++ip) {
983 m = message + *ip - 1;
984 setdot(m);
985 if (m->m_flag & (MDRAFT | MDRAFTED)) {
986 m->m_flag &= ~(MDRAFT | MDRAFTED);
987 m->m_flag |= MUNDRAFT;
990 NYD_LEAVE;
991 return 0;
994 /* s-it-mode */