Fix [1785be65] from 2017-03-xx
[s-mailx.git] / cmd-message.c
blobad2c026239ad336a9151f35fdc7e5f380fbf23b6
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 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
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.
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.
35 #undef n_FILE
36 #define n_FILE cmd_message
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 /* Prepare and print "[Message: xy]:" intro */
43 static bool_t a_cmsg_show_overview(FILE *obuf, struct message *mp, int msg_no);
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);
49 /* Pipe the requested messages */
50 static int _pipe1(char *str, int doign);
52 /* `top' / `Top' */
53 static int a_cmsg_top(void *vp, struct n_ignore const *itp);
55 /* Delete the indicated messages. Set dot to some nice place afterwards */
56 static int delm(int *msgvec);
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;
64 cpre = csuf = n_empty;
65 #ifdef HAVE_COLOUR
66 if(n_COLOUR_IS_ACTIVE()){
67 struct n_colour_pen *cpen;
69 if((cpen = n_colour_pen_create(n_COLOUR_ID_VIEW_MSGINFO, NULL)) != NULL){
70 struct str const *sp;
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;
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;
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)
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;
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);
109 if (dopipe) {
110 if ((obuf = Popen(cmd, "w", ok_vlook(SHELL), NULL, 1)) == NULL) {
111 n_perr(cmd, 0);
112 obuf = n_stdout;
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;
118 nlines = 0;
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! */
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;
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);
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);
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;
159 if(action != SEND_MBOX && !a_cmsg_show_overview(obuf, mp, *ip)){
160 rv = 1;
161 break;
163 if(sendmp(mp, obuf, (doign ? n_IGNORE_TYPE : NULL), NULL, action, mstats
164 ) < 0){
165 rv = 1;
166 break;
168 srelax();
169 if(formfeed){ /* TODO a nicer way to separate piped messages! */
170 if(putc('\f', obuf) == EOF){
171 rv = 1;
172 break;
175 if (tstats != NULL)
176 tstats[0] += mstats[0];
178 srelax_rele();
179 n_COLOUR(
180 if(!dopipe && (action == SEND_TODISP || action == SEND_TODISP_ALL))
181 n_colour_env_gut();
183 jleave:
184 if (obuf != n_stdout)
185 n_pager_close(obuf);
186 NYD_LEAVE;
187 return rv;
190 static int
191 _pipe1(char *str, int doign)
193 ui64_t stats[1];
194 char const *cmd, *cmdq;
195 int *msgvec, rv = 1;
196 bool_t needs_list;
197 NYD_ENTER;
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;
207 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
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;
216 fputs(_("No messages to pipe.\n"), n_stdout);
217 goto jleave;
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;
227 fprintf(n_stdout, "No applicable messages.\n");
228 goto jleave;
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;
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;
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;
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;
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;
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;
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;
285 }else
286 tmax = (size_t)l;
288 f = ok_blook(topsqueeze) ? a_SQUEEZE : a_NONE;
290 for(ip = msgvec = vp;
291 *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount); ++ip){
292 struct message *mp;
294 mp = &message[*ip - 1];
295 touch(mp);
296 setdot(mp);
297 n_pstate |= n_PS_DID_PRINT_DOT;
298 uncollapse1(mp, 1);
300 rewind(iobuf);
301 if(ftruncate(fileno(iobuf), 0)){
302 n_perr(_("`top': ftruncate(2)"), 0);
303 vp = NULL;
304 break;
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;
313 fflush_rewind(iobuf);
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;
320 if((c = getc(iobuf)) == EOF || putc(c, pbuf) == EOF){
321 vp = NULL;
322 break;
323 }else if(c == '\n')
324 break;
326 if(vp == NULL)
327 break;
328 ++plines;
330 /* C99 */{
331 size_t l;
333 n_string_trunc(&s, 0);
334 for(l = 0, f &= ~a_WORKMASK; !(f & a_STOP);){
335 int c;
337 if((c = getc(iobuf)) == EOF){
338 f |= a_STOP;
339 c = '\n';
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;
351 f |= a_EMPTY;
352 ++l;
353 }else{
354 char const *cp, *xcp;
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;
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;
379 if(c != '\0'){
380 if(fputs(n_string_cp_const(&s), pbuf) == EOF ||
381 putc('\n', pbuf) == EOF){
382 vp = NULL;
383 break;
385 if(++l >= tmax)
386 break;
387 f &= ~a_EMPTY;
388 }else
389 f |= a_EMPTY;
390 n_string_trunc(&s, 0);
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;
402 ++plines;
407 n_string_gut(&s);
408 n_COLOUR( n_colour_env_gut(); )
410 fflush(pbuf);
411 page_or_print(pbuf, plines);
413 Fclose(pbuf);
414 jleave1:
415 Fclose(iobuf);
416 jleave:
417 NYD2_LEAVE;
418 return (vp != NULL);
421 static int
422 delm(int *msgvec)
424 struct message *mp;
425 int rv = -1, *ip, last;
426 NYD_ENTER;
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;
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);
446 NYD_LEAVE;
447 return rv;
450 FL int
451 c_more(void *v)
453 int *msgvec = v, rv;
454 NYD_ENTER;
456 rv = _type1(msgvec, TRU1, TRU1, FAL0, FAL0, NULL, NULL);
457 NYD_LEAVE;
458 return rv;
461 FL int
462 c_More(void *v)
464 int *msgvec = v, rv;
465 NYD_ENTER;
467 rv = _type1(msgvec, FAL0, TRU1, FAL0, FAL0, NULL, NULL);
468 NYD_LEAVE;
469 return rv;
472 FL int
473 c_type(void *v)
475 int *msgvec = v, rv;
476 NYD_ENTER;
478 rv = _type1(msgvec, TRU1, FAL0, FAL0, FAL0, NULL, NULL);
479 NYD_LEAVE;
480 return rv;
483 FL int
484 c_Type(void *v)
486 int *msgvec = v, rv;
487 NYD_ENTER;
489 rv = _type1(msgvec, FAL0, FAL0, FAL0, FAL0, NULL, NULL);
490 NYD_LEAVE;
491 return rv;
494 FL int
495 c_show(void *v)
497 int *msgvec = v, rv;
498 NYD_ENTER;
500 rv = _type1(msgvec, FAL0, FAL0, FAL0, TRU1, NULL, NULL);
501 NYD_LEAVE;
502 return rv;
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;
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;
518 mp = &message[*msgvec - 1];
519 touch(mp);
520 setdot(mp);
521 n_pstate |= n_PS_DID_PRINT_DOT;
522 uncollapse1(mp, 1);
524 n_COLOUR(
525 n_colour_env_create(n_COLOUR_CTX_VIEW, n_stdout, FAL0);
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;
536 n_COLOUR(
537 n_colour_env_gut();
540 rv = (n_pstate_err_no != n_ERR_NONE);
541 jleave:
542 NYD_LEAVE;
543 return rv;
546 FL int
547 c_pipe(void *v)
549 char *str = v;
550 int rv;
551 NYD_ENTER;
553 rv = _pipe1(str, 1);
554 NYD_LEAVE;
555 return rv;
558 FL int
559 c_Pipe(void *v)
561 char *str = v;
562 int rv;
563 NYD_ENTER;
565 rv = _pipe1(str, 0);
566 NYD_LEAVE;
567 return rv;
570 FL int
571 c_top(void *v){
572 struct n_ignore *itp;
573 int rv;
574 NYD_ENTER;
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);
586 rv = !a_cmsg_top(v, itp);
587 NYD_LEAVE;
588 return rv;
591 FL int
592 c_Top(void *v){
593 int rv;
594 NYD_ENTER;
596 rv = !a_cmsg_top(v, n_IGNORE_TYPE);
597 NYD_LEAVE;
598 return rv;
601 FL int
602 c_next(void *v)
604 int list[2], *ip, *ip2, mdot, *msgvec = v, rv = 1;
605 struct message *mp;
606 NYD_ENTER;
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);
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;
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;
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;
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;
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);
660 if (mp == NULL || PTRCMP(mp, >=, message + msgCount)) {
661 jateof:
662 fprintf(n_stdout, _("At EOF\n"));
663 rv = 0;
664 goto jleave;
666 setdot(mp);
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;
678 FL int
679 c_pdot(void *vp)
681 NYD_ENTER;
682 n_UNUSED(vp);
683 fprintf(n_stdout, "%d\n", (int)PTR2SIZE(dot - message + 1));
684 NYD_LEAVE;
685 return 0;
688 FL int
689 c_messize(void *v)
691 int *msgvec = v, *ip, mesg;
692 struct message *mp;
693 NYD_ENTER;
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);
705 NYD_LEAVE;
706 return 0;
709 FL int
710 c_delete(void *v)
712 int *msgvec = v;
713 NYD_ENTER;
715 delm(msgvec);
716 NYD_LEAVE;
717 return 0;
720 FL int
721 c_deltype(void *v)
723 int list[2], rv = 0, *msgvec = v, lastdot;
724 NYD_ENTER;
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;
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;
743 FL int
744 c_undelete(void *v)
746 int *msgvec = v, *ip;
747 struct message *mp;
748 NYD_ENTER;
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
764 NYD_LEAVE;
765 return 0;
768 FL int
769 c_stouch(void *v)
771 int *msgvec = v, *ip;
772 NYD_ENTER;
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;
780 NYD_LEAVE;
781 return 0;
784 FL int
785 c_mboxit(void *v)
787 int *msgvec = v, *ip;
788 NYD_ENTER;
790 if (n_pstate & n_PS_EDIT) {
791 n_err(_("`mbox' can only be used in a system mailbox\n")); /* TODO */
792 goto jleave;
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;
801 jleave:
802 NYD_LEAVE;
803 return 0;
806 FL int
807 c_preserve(void *v)
809 int *msgvec = v, *ip, mesg, rv = 1;
810 struct message *mp;
811 NYD_ENTER;
813 if (n_pstate & n_PS_EDIT) {
814 fprintf(n_stdout, _("Cannot `preserve' in a system mailbox\n"));
815 goto jleave;
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;
826 rv = 0;
827 jleave:
828 NYD_LEAVE;
829 return rv;
832 FL int
833 c_unread(void *v)
835 struct message *mp;
836 int *msgvec = v, *ip;
837 NYD_ENTER;
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;
850 NYD_LEAVE;
851 return 0;
854 FL int
855 c_seen(void *v)
857 int *msgvec = v, *ip;
858 NYD_ENTER;
860 for (ip = msgvec; *ip != 0; ++ip) {
861 struct message *mp = message + *ip - 1;
862 setdot(mp);
863 touch(mp);
865 NYD_LEAVE;
866 return 0;
869 FL int
870 c_flag(void *v)
872 struct message *m;
873 int *msgvec = v, *ip;
874 NYD_ENTER;
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;
882 NYD_LEAVE;
883 return 0;
886 FL int
887 c_unflag(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);
898 m->m_flag |= MUNFLAG;
901 NYD_LEAVE;
902 return 0;
905 FL int
906 c_answered(void *v)
908 struct message *m;
909 int *msgvec = v, *ip;
910 NYD_ENTER;
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;
918 NYD_LEAVE;
919 return 0;
922 FL int
923 c_unanswered(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);
934 m->m_flag |= MUNANSWER;
937 NYD_LEAVE;
938 return 0;
941 FL int
942 c_draft(void *v)
944 struct message *m;
945 int *msgvec = v, *ip;
946 NYD_ENTER;
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;
954 NYD_LEAVE;
955 return 0;
958 FL int
959 c_undraft(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);
970 m->m_flag |= MUNDRAFT;
973 NYD_LEAVE;
974 return 0;
977 /* s-it-mode */