Review: list.c
[s-mailx.git] / cmd3.c
blob2d0eb604fedde7c4dd74525e6d3d6bbfe07b5e07
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Still more user commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 struct cond_stack {
45 struct cond_stack *c_outer;
46 bool_t c_noop; /* Outer stack !c_go, entirely no-op */
47 bool_t c_go; /* Green light */
48 bool_t c_else; /* In `else' clause */
49 ui8_t __dummy[5];
52 static struct cond_stack *_cond_stack;
53 static char *_bang_buf;
54 static size_t _bang_size;
56 /* Modify subject we reply to to begin with Re: if it does not already */
57 static char * _reedit(char *subj);
59 /* Expand the shell escape by expanding unescaped !'s into the last issued
60 * command where possible */
61 static void _bangexp(char **str, size_t *size);
63 static void make_ref_and_cs(struct message *mp, struct header *head);
65 /* Get PTF to implementation of command `c' (i.e., take care for *flipr*) */
66 static int (* respond_or_Respond(int c))(int *, int);
68 /* Reply to a single message. Extract each name from the message header and
69 * send them off to mail1() */
70 static int respond_internal(int *msgvec, int recipient_record);
72 /* Reply to a series of messages by simply mailing to the senders and not
73 * messing around with the To: and Cc: lists as in normal reply */
74 static int Respond_internal(int *msgvec, int recipient_record);
76 /* Forward a message to a new recipient, in the sense of RFC 2822 */
77 static int forward1(char *str, int recipient_record);
79 /* Modify the subject we are replying to to begin with Fwd: */
80 static char * fwdedit(char *subj);
82 /* Sort the passed string vecotor into ascending dictionary order */
83 static void asort(char **list);
85 /* Do a dictionary order comparison of the arguments from qsort */
86 static int diction(void const *a, void const *b);
88 /* Do the real work of resending */
89 static int resend1(void *v, int add_resent);
91 /* ..to stdout */
92 static void list_shortcuts(void);
94 /* */
95 static enum okay delete_shortcut(char const *str);
97 static char *
98 _reedit(char *subj)
100 struct str in, out;
101 char *newsubj = NULL;
102 NYD_ENTER;
104 if (subj == NULL || *subj == '\0')
105 goto j_leave;
107 in.s = subj;
108 in.l = strlen(subj);
109 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
111 /* TODO _reedit: should be localizable (see cmd1.c:__subject_trim()!) */
112 if ((out.s[0] == 'r' || out.s[0] == 'R') &&
113 (out.s[1] == 'e' || out.s[1] == 'E') && out.s[2] == ':') {
114 newsubj = savestr(out.s);
115 goto jleave;
117 newsubj = salloc(out.l + 4 +1);
118 sstpcpy(sstpcpy(newsubj, "Re: "), out.s);
119 jleave:
120 free(out.s);
121 j_leave:
122 NYD_LEAVE;
123 return newsubj;
126 static void
127 _bangexp(char **str, size_t *size)
129 char *bangbuf;
130 int changed = 0;
131 bool_t dobang;
132 size_t sz, i, j, bangbufsize;
133 NYD_ENTER;
135 dobang = ok_blook(bang);
137 bangbuf = smalloc(bangbufsize = *size);
138 i = j = 0;
139 while ((*str)[i]) {
140 if (dobang) {
141 if ((*str)[i] == '!') {
142 sz = strlen(_bang_buf);
143 bangbuf = srealloc(bangbuf, bangbufsize += sz);
144 ++changed;
145 memcpy(bangbuf + j, _bang_buf, sz + 1);
146 j += sz;
147 i++;
148 continue;
151 if ((*str)[i] == '\\' && (*str)[i + 1] == '!') {
152 bangbuf[j++] = '!';
153 i += 2;
154 ++changed;
156 bangbuf[j++] = (*str)[i++];
158 bangbuf[j] = '\0';
159 if (changed) {
160 printf("!%s\n", bangbuf);
161 fflush(stdout);
163 sz = j + 1;
164 if (sz > *size)
165 *str = srealloc(*str, *size = sz);
166 memcpy(*str, bangbuf, sz);
167 if (sz > _bang_size)
168 _bang_buf = srealloc(_bang_buf, _bang_size = sz);
169 memcpy(_bang_buf, bangbuf, sz);
170 free(bangbuf);
171 NYD_LEAVE;
174 static void
175 make_ref_and_cs(struct message *mp, struct header *head)
177 char *oldref, *oldmsgid, *newref, *cp;
178 size_t oldreflen = 0, oldmsgidlen = 0, reflen;
179 unsigned i;
180 struct name *n;
181 NYD_ENTER;
183 oldref = hfield1("references", mp);
184 oldmsgid = hfield1("message-id", mp);
185 if (oldmsgid == NULL || *oldmsgid == '\0') {
186 head->h_ref = NULL;
187 goto jleave;
190 reflen = 1;
191 if (oldref) {
192 oldreflen = strlen(oldref);
193 reflen += oldreflen + 2;
195 if (oldmsgid) {
196 oldmsgidlen = strlen(oldmsgid);
197 reflen += oldmsgidlen;
200 newref = ac_alloc(reflen);
201 if (oldref != NULL) {
202 memcpy(newref, oldref, oldreflen +1);
203 if (oldmsgid != NULL) {
204 newref[oldreflen++] = ',';
205 newref[oldreflen++] = ' ';
206 memcpy(newref + oldreflen, oldmsgid, oldmsgidlen +1);
208 } else if (oldmsgid)
209 memcpy(newref, oldmsgid, oldmsgidlen +1);
210 n = extract(newref, GREF);
211 ac_free(newref);
213 /* Limit number of references */
214 while (n->n_flink != NULL)
215 n = n->n_flink;
216 for (i = 1; i < 21; ++i) { /* XXX no magics */
217 if (n->n_blink != NULL)
218 n = n->n_blink;
219 else
220 break;
222 n->n_blink = NULL;
223 head->h_ref = n;
224 if (ok_blook(reply_in_same_charset) &&
225 (cp = hfield1("content-type", mp)) != NULL)
226 head->h_charset = mime_getparam("charset", cp);
227 jleave:
228 NYD_LEAVE;
231 static int
232 (*respond_or_Respond(int c))(int *, int)
234 int opt;
235 int (*rv)(int*, int);
236 NYD_ENTER;
238 opt = ok_blook(Replyall);
239 opt += ok_blook(flipr);
240 rv = ((opt == 1) ^ (c == 'R')) ? &Respond_internal : &respond_internal;
241 NYD_LEAVE;
242 return rv;
245 static int
246 respond_internal(int *msgvec, int recipient_record)
248 struct header head;
249 struct message *mp;
250 char *cp, *rcv;
251 struct name *np = NULL;
252 enum gfield gf;
253 int rv = 1;
254 NYD_ENTER;
256 gf = ok_blook(fullnames) ? GFULL : GSKIN;
258 if (msgvec[1] != 0) {
259 fprintf(stderr, tr(37,
260 "Sorry, can't reply to multiple messages at once\n"));
261 goto jleave;
264 mp = message + msgvec[0] - 1;
265 touch(mp);
266 setdot(mp);
268 if ((rcv = hfield1("reply-to", mp)) == NULL)
269 if ((rcv = hfield1("from", mp)) == NULL)
270 rcv = nameof(mp, 1);
271 if (rcv != NULL)
272 np = lextract(rcv, GTO | gf);
273 if (!ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
274 np = cat(np, lextract(cp, GTO | gf));
275 /* Delete my name from reply list, and with it, all my alternate names */
276 np = elide(delete_alternates(np));
277 if (np == NULL)
278 np = lextract(rcv, GTO | gf);
280 memset(&head, 0, sizeof head);
281 head.h_to = np;
282 head.h_subject = hfield1("subject", mp);
283 head.h_subject = _reedit(head.h_subject);
285 /* Cc: */
286 np = NULL;
287 if (ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
288 np = lextract(cp, GCC | gf);
289 if ((cp = hfield1("cc", mp)) != NULL)
290 np = cat(np, lextract(cp, GCC | gf));
291 if (np != NULL)
292 head.h_cc = elide(delete_alternates(np));
293 make_ref_and_cs(mp, &head);
295 if (ok_blook(quote_as_attachment)) {
296 head.h_attach = csalloc(1, sizeof *head.h_attach);
297 head.h_attach->a_msgno = *msgvec;
298 head.h_attach->a_content_description = tr(512,
299 "Original message content");
302 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
303 ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
304 mp->m_flag |= MANSWER | MANSWERED;
305 rv = 0;
306 jleave:
307 NYD_LEAVE;
308 return rv;
311 static int
312 Respond_internal(int *msgvec, int recipient_record)
314 struct header head;
315 struct message *mp;
316 int *ap;
317 char *cp;
318 enum gfield gf;
319 NYD_ENTER;
321 memset(&head, 0, sizeof head);
322 gf = ok_blook(fullnames) ? GFULL : GSKIN;
324 for (ap = msgvec; *ap != 0; ++ap) {
325 mp = message + *ap - 1;
326 touch(mp);
327 setdot(mp);
328 if ((cp = hfield1("reply-to", mp)) == NULL)
329 if ((cp = hfield1("from", mp)) == NULL)
330 cp = nameof(mp, 2);
331 head.h_to = cat(head.h_to, lextract(cp, GTO | gf));
333 if (head.h_to == NULL)
334 goto jleave;
336 mp = message + msgvec[0] - 1;
337 head.h_subject = hfield1("subject", mp);
338 head.h_subject = _reedit(head.h_subject);
339 make_ref_and_cs(mp, &head);
341 if (ok_blook(quote_as_attachment)) {
342 head.h_attach = csalloc(1, sizeof *head.h_attach);
343 head.h_attach->a_msgno = *msgvec;
344 head.h_attach->a_content_description = tr(512,
345 "Original message content");
348 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
349 ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
350 mp->m_flag |= MANSWER | MANSWERED;
351 jleave:
352 NYD_LEAVE;
353 return 0;
356 static int
357 forward1(char *str, int recipient_record)
359 struct header head;
360 int *msgvec, rv = 1;
361 char *recipient;
362 struct message *mp;
363 bool_t f, forward_as_attachment;
364 NYD_ENTER;
366 if ((recipient = laststring(str, &f, 0)) == NULL) {
367 puts(tr(47, "No recipient specified."));
368 goto jleave;
371 forward_as_attachment = ok_blook(forward_as_attachment);
372 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
374 if (!f) {
375 *msgvec = first(0, MMNORM);
376 if (*msgvec == 0) {
377 if (inhook) {
378 rv = 0;
379 goto jleave;
381 printf("No messages to forward.\n");
382 goto jleave;
384 msgvec[1] = 0;
385 } else if (getmsglist(str, msgvec, 0) < 0)
386 goto jleave;
388 if (*msgvec == 0) {
389 if (inhook) {
390 rv = 0;
391 goto jleave;
393 printf("No applicable messages.\n");
394 goto jleave;
396 if (msgvec[1] != 0) {
397 printf("Cannot forward multiple messages at once\n");
398 goto jleave;
401 memset(&head, 0, sizeof head);
402 if ((head.h_to = lextract(recipient,
403 (GTO | (ok_blook(fullnames) ? GFULL : GSKIN)))) == NULL)
404 goto jleave;
406 mp = message + *msgvec - 1;
408 if (forward_as_attachment) {
409 head.h_attach = csalloc(1, sizeof *head.h_attach);
410 head.h_attach->a_msgno = *msgvec;
411 head.h_attach->a_content_description = "Forwarded message";
412 } else {
413 touch(mp);
414 setdot(mp);
416 head.h_subject = hfield1("subject", mp);
417 head.h_subject = fwdedit(head.h_subject);
418 mail1(&head, 1, (forward_as_attachment ? NULL : mp), NULL, recipient_record,
420 rv = 0;
421 jleave:
422 NYD_LEAVE;
423 return rv;
426 static char *
427 fwdedit(char *subj)
429 struct str in, out;
430 char *newsubj = NULL;
431 NYD_ENTER;
433 if (subj == NULL || *subj == '\0')
434 goto jleave;
436 in.s = subj;
437 in.l = strlen(subj);
438 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
440 newsubj = salloc(out.l + 6);
441 memcpy(newsubj, "Fwd: ", 5); /* XXX localizable */
442 memcpy(newsubj + 5, out.s, out.l + 1);
443 free(out.s);
444 jleave:
445 NYD_LEAVE;
446 return newsubj;
449 static void
450 asort(char **list)
452 char **ap;
453 size_t i;
454 NYD_ENTER;
456 for (ap = list; *ap != NULL; ++ap)
458 if ((i = PTR2SIZE(ap - list)) >= 2)
459 qsort(list, i, sizeof *list, diction);
460 NYD_LEAVE;
463 static int
464 diction(void const *a, void const *b)
466 int rv;
467 NYD_ENTER;
469 rv = strcmp(*(char**)UNCONST(a), *(char**)UNCONST(b));
470 NYD_LEAVE;
471 return rv;
474 static int
475 resend1(void *v, int add_resent)
477 char *name, *str;
478 struct name *to, *sn;
479 int *ip, *msgvec;
480 bool_t f = TRU1;
481 NYD_ENTER;
483 str = v;
484 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
485 name = laststring(str, &f, 1);
486 if (name == NULL) {
487 puts(tr(47, "No recipient specified."));
488 goto jleave;
491 if (!f) {
492 *msgvec = first(0, MMNORM);
493 if (*msgvec == 0) {
494 if (inhook) {
495 f = FAL0;
496 goto jleave;
498 puts(tr(48, "No applicable messages."));
499 goto jleave;
501 msgvec[1] = 0;
502 } else if (getmsglist(str, msgvec, 0) < 0)
503 goto jleave;
505 if (*msgvec == 0) {
506 if (inhook) {
507 f = FAL0;
508 goto jleave;
510 printf("No applicable messages.\n");
511 goto jleave;
514 sn = nalloc(name, GTO);
515 to = usermap(sn, FAL0);
516 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
517 ++ip)
518 if (resend_msg(message + *ip - 1, to, add_resent) != OKAY)
519 goto jleave;
520 f = FAL0;
521 jleave:
522 NYD_LEAVE;
523 return (f != FAL0);
526 static void
527 list_shortcuts(void)
529 struct shortcut *s;
530 NYD_ENTER;
532 for (s = shortcuts; s != NULL; s = s->sh_next)
533 printf("%s=%s\n", s->sh_short, s->sh_long);
534 NYD_LEAVE;
537 static enum okay
538 delete_shortcut(char const *str)
540 struct shortcut *sp, *sq;
541 enum okay rv = STOP;
542 NYD_ENTER;
544 for (sp = shortcuts, sq = NULL; sp != NULL; sq = sp, sp = sp->sh_next) {
545 if (!strcmp(sp->sh_short, str)) {
546 free(sp->sh_short);
547 free(sp->sh_long);
548 if (sq != NULL)
549 sq->sh_next = sp->sh_next;
550 if (sp == shortcuts)
551 shortcuts = sp->sh_next;
552 free(sp);
553 rv = OKAY;
554 break;
557 NYD_LEAVE;
558 return rv;
561 FL int
562 c_shell(void *v)
564 char const *sh = NULL;
565 char *str = v, *cmd;
566 size_t cmdsize;
567 sighandler_type sigint;
568 NYD_ENTER;
570 cmd = smalloc(cmdsize = strlen(str) +1);
571 memcpy(cmd, str, cmdsize);
572 _bangexp(&cmd, &cmdsize);
573 if ((sh = ok_vlook(SHELL)) == NULL)
574 sh = XSHELL;
576 sigint = safe_signal(SIGINT, SIG_IGN);
577 run_command(sh, 0, -1, -1, "-c", cmd, NULL);
578 safe_signal(SIGINT, sigint);
579 printf("!\n");
581 free(cmd);
582 NYD_LEAVE;
583 return 0;
586 FL int
587 c_dosh(void *v)
589 sighandler_type sigint;
590 char const *sh;
591 NYD_ENTER;
592 UNUSED(v);
594 if ((sh = ok_vlook(SHELL)) == NULL)
595 sh = XSHELL;
597 sigint = safe_signal(SIGINT, SIG_IGN);
598 run_command(sh, 0, -1, -1, NULL, NULL, NULL);
599 safe_signal(SIGINT, sigint);
600 putchar('\n');
601 NYD_LEAVE;
602 return 0;
605 FL int
606 c_help(void *v)
608 int ret = 0;
609 char *arg;
610 NYD_ENTER;
612 arg = *(char**)v;
614 if (arg != NULL) {
615 #ifdef HAVE_DOCSTRINGS
616 ret = !print_comm_docstr(arg);
617 if (ret)
618 fprintf(stderr, tr(91, "Unknown command: `%s'\n"), arg);
619 #else
620 ret = c_cmdnotsupp(NULL);
621 #endif
622 goto jleave;
625 /* Very ugly, but take care for compiler supported string lengths :( */
626 printf(tr(295, "%s commands:\n"), progname);
627 puts(tr(296,
628 "type <message list> type messages\n"
629 "next goto and type next message\n"
630 "from <message list> give head lines of messages\n"
631 "headers print out active message headers\n"
632 "delete <message list> delete messages\n"
633 "undelete <message list> undelete messages\n"));
634 puts(tr(297,
635 "save <message list> folder append messages to folder and mark as saved\n"
636 "copy <message list> folder append messages to folder without marking them\n"
637 "write <message list> file append message texts to file, save attachments\n"
638 "preserve <message list> keep incoming messages in mailbox even if saved\n"
639 "Reply <message list> reply to message senders\n"
640 "reply <message list> reply to message senders and all recipients\n"));
641 puts(tr(298,
642 "mail addresses mail to specific recipients\n"
643 "file folder change to another folder\n"
644 "quit quit and apply changes to folder\n"
645 "xit quit and discard changes made to folder\n"
646 "! shell escape\n"
647 "cd <directory> chdir to directory or home if none given\n"
648 "list list names of all available commands\n"));
649 printf(tr(299,
650 "\nA <message list> consists of integers, ranges of same, or other criteria\n"
651 "separated by spaces. If omitted, %s uses the last message typed.\n"),
652 progname);
654 jleave:
655 NYD_LEAVE;
656 return ret;
659 FL int
660 c_cwd(void *v)
662 char buf[PATH_MAX]; /* TODO getcwd(3) may return a larger value */
663 NYD_ENTER;
665 if (getcwd(buf, sizeof buf) != NULL) {
666 puts(buf);
667 v = (void*)0x1;
668 } else {
669 perror("getcwd");
670 v = NULL;
672 NYD_LEAVE;
673 return (v == NULL);
676 FL int
677 c_chdir(void *v)
679 char **arglist = v;
680 char const *cp;
681 NYD_ENTER;
683 if (*arglist == NULL)
684 cp = homedir;
685 else if ((cp = file_expand(*arglist)) == NULL)
686 goto jleave;
687 if (chdir(cp) == -1) {
688 perror(cp);
689 cp = NULL;
691 jleave:
692 NYD_LEAVE;
693 return (cp == NULL);
696 FL int
697 c_respond(void *v)
699 int rv;
700 NYD_ENTER;
702 rv = (*respond_or_Respond('r'))(v, 0);
703 NYD_LEAVE;
704 return rv;
707 FL int
708 c_respondall(void *v)
710 int rv;
711 NYD_ENTER;
713 rv = respond_internal(v, 0);
714 NYD_LEAVE;
715 return rv;
718 FL int
719 c_respondsender(void *v)
721 int rv;
722 NYD_ENTER;
724 rv = Respond_internal(v, 0);
725 NYD_LEAVE;
726 return rv;
729 FL int
730 c_Respond(void *v)
732 int rv;
733 NYD_ENTER;
735 rv = (*respond_or_Respond('R'))(v, 0);
736 NYD_LEAVE;
737 return rv;
740 FL int
741 c_followup(void *v)
743 int rv;
744 NYD_ENTER;
746 rv = (*respond_or_Respond('r'))(v, 1);
747 NYD_LEAVE;
748 return rv;
751 FL int
752 c_followupall(void *v)
754 int rv;
755 NYD_ENTER;
757 rv = respond_internal(v, 1);
758 NYD_LEAVE;
759 return rv;
762 FL int
763 c_followupsender(void *v)
765 int rv;
766 NYD_ENTER;
768 rv = Respond_internal(v, 1);
769 NYD_LEAVE;
770 return rv;
773 FL int
774 c_Followup(void *v)
776 int rv;
777 NYD_ENTER;
779 rv = (*respond_or_Respond('R'))(v, 1);
780 NYD_LEAVE;
781 return rv;
784 FL int
785 c_forward(void *v)
787 int rv;
788 NYD_ENTER;
790 rv = forward1(v, 0);
791 NYD_LEAVE;
792 return rv;
795 FL int
796 c_Forward(void *v)
798 int rv;
799 NYD_ENTER;
801 rv = forward1(v, 1);
802 NYD_LEAVE;
803 return rv;
806 FL int
807 c_resend(void *v)
809 int rv;
810 NYD_ENTER;
812 rv = resend1(v, 1);
813 NYD_LEAVE;
814 return rv;
817 FL int
818 c_Resend(void *v)
820 int rv;
821 NYD_ENTER;
823 rv = resend1(v, 0);
824 NYD_LEAVE;
825 return rv;
828 FL int
829 c_preserve(void *v)
831 int *msgvec = v, *ip, mesg, rv = 1;
832 struct message *mp;
833 NYD_ENTER;
835 if (edit) {
836 printf(tr(39, "Cannot \"preserve\" in edit mode\n"));
837 goto jleave;
840 for (ip = msgvec; *ip != 0; ++ip) {
841 mesg = *ip;
842 mp = message + mesg - 1;
843 mp->m_flag |= MPRESERVE;
844 mp->m_flag &= ~MBOX;
845 setdot(mp);
846 did_print_dot = TRU1;
848 rv = 0;
849 jleave:
850 NYD_LEAVE;
851 return rv;
854 FL int
855 c_unread(void *v)
857 int *msgvec = v, *ip;
858 NYD_ENTER;
860 for (ip = msgvec; *ip != 0; ++ip) {
861 setdot(message + *ip - 1);
862 dot->m_flag &= ~(MREAD | MTOUCH);
863 dot->m_flag |= MSTATUS;
864 #ifdef HAVE_IMAP
865 if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
866 imap_unread(message + *ip - 1, *ip); /* TODO return? */
867 #endif
868 did_print_dot = TRU1;
870 NYD_LEAVE;
871 return 0;
874 FL int
875 c_seen(void *v)
877 int *msgvec = v, *ip;
878 NYD_ENTER;
880 for (ip = msgvec; *ip != 0; ++ip) {
881 struct message *mp = message + *ip - 1;
882 setdot(mp);
883 touch(mp);
885 NYD_LEAVE;
886 return 0;
889 FL int
890 c_messize(void *v)
892 int *msgvec = v, *ip, mesg;
893 struct message *mp;
894 NYD_ENTER;
896 for (ip = msgvec; *ip != 0; ++ip) {
897 mesg = *ip;
898 mp = message + mesg - 1;
899 printf("%d: ", mesg);
900 if (mp->m_xlines > 0)
901 printf("%ld", mp->m_xlines);
902 else
903 putchar(' ');
904 printf("/%lu\n", (ul_it)mp->m_xsize);
906 NYD_LEAVE;
907 return 0;
910 FL int
911 c_rexit(void *v)
913 UNUSED(v);
914 NYD_ENTER;
916 if (!sourcing)
917 exit(0);
918 NYD_LEAVE;
919 return 1;
922 FL int
923 c_set(void *v)
925 char **ap = v, *cp, *cp2, *varbuf, c;
926 int errs = 0;
927 NYD_ENTER;
929 if (*ap == NULL) {
930 var_list_all();
931 goto jleave;
934 for (; *ap != NULL; ++ap) {
935 cp = *ap;
936 cp2 = varbuf = ac_alloc(strlen(cp) +1);
937 for (; (c = *cp) != '=' && c != '\0'; ++cp)
938 *cp2++ = c;
939 *cp2 = '\0';
940 if (c == '\0')
941 cp = UNCONST("");
942 else
943 ++cp;
944 if (varbuf == cp2) {
945 fprintf(stderr, tr(41, "Non-null variable name required\n"));
946 ++errs;
947 goto jnext;
949 if (varbuf[0] == 'n' && varbuf[1] == 'o')
950 errs += _var_vokclear(&varbuf[2]);
951 else
952 errs += _var_vokset(varbuf, (uintptr_t)cp);
953 jnext:
954 ac_free(varbuf);
956 jleave:
957 NYD_LEAVE;
958 return errs;
961 FL int
962 c_unset(void *v)
964 int errs;
965 char **ap;
966 NYD_ENTER;
968 errs = 0;
969 for (ap = v; *ap != NULL; ++ap)
970 errs += _var_vokclear(*ap);
971 NYD_LEAVE;
972 return errs;
975 FL int
976 c_group(void *v)
978 char **argv = v, **ap, *gname, **p;
979 struct grouphead *gh;
980 struct group *gp;
981 int h, s;
982 NYD_ENTER;
984 if (*argv == NULL) {
985 for (h = 0, s = 1; h < HSHSIZE; ++h)
986 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
987 ++s;
988 ap = salloc(s * sizeof *ap);
990 for (h = 0, p = ap; h < HSHSIZE; ++h)
991 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
992 *p++ = gh->g_name;
993 *p = NULL;
995 asort(ap);
997 for (p = ap; *p != NULL; ++p)
998 printgroup(*p);
999 goto jleave;
1002 if (argv[1] == NULL) {
1003 printgroup(*argv);
1004 goto jleave;
1007 gname = *argv;
1008 h = hash(gname);
1009 if ((gh = findgroup(gname)) == NULL) {
1010 gh = scalloc(1, sizeof *gh);
1011 gh->g_name = sstrdup(gname);
1012 gh->g_list = NULL;
1013 gh->g_link = groups[h];
1014 groups[h] = gh;
1017 /* Insert names from the command list into the group. Who cares if there
1018 * are duplicates? They get tossed later anyway */
1019 for (ap = argv + 1; *ap != NULL; ++ap) {
1020 gp = scalloc(1, sizeof *gp);
1021 gp->ge_name = sstrdup(*ap);
1022 gp->ge_link = gh->g_list;
1023 gh->g_list = gp;
1025 jleave:
1026 NYD_LEAVE;
1027 return 0;
1030 FL int
1031 c_ungroup(void *v)
1033 char **argv = v;
1034 int rv = 1;
1035 NYD_ENTER;
1037 if (*argv == NULL) {
1038 fprintf(stderr, tr(209, "Must specify alias to remove\n"));
1039 goto jleave;
1043 remove_group(*argv);
1044 while (*++argv != NULL);
1045 rv = 0;
1046 jleave:
1047 NYD_LEAVE;
1048 return rv;
1051 FL int
1052 c_file(void *v)
1054 char **argv = v;
1055 int i;
1056 NYD_ENTER;
1058 if (*argv == NULL) {
1059 newfileinfo();
1060 i = 0;
1061 goto jleave;
1064 if (inhook) {
1065 fprintf(stderr, tr(516, "Cannot change folder from within a hook.\n"));
1066 i = 1;
1067 goto jleave;
1070 save_mbox_for_possible_quitstuff();
1072 i = setfile(*argv, 0);
1073 if (i < 0) {
1074 i = 1;
1075 goto jleave;
1077 callhook(mailname, 0);
1078 if (i > 0 && !ok_blook(emptystart)) {
1079 i = 1;
1080 goto jleave;
1082 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1083 i = 0;
1084 jleave:
1085 NYD_LEAVE;
1086 return i;
1089 FL int
1090 c_echo(void *v)
1092 char const **argv = v, **ap, *cp;
1093 int c;
1094 NYD_ENTER;
1096 for (ap = argv; *ap != NULL; ++ap) {
1097 cp = *ap;
1098 if ((cp = fexpand(cp, FEXP_NSHORTCUT)) != NULL) {
1099 if (ap != argv)
1100 putchar(' ');
1101 c = 0;
1102 while (*cp != '\0' && (c = expand_shell_escape(&cp, FAL0)) > 0)
1103 putchar(c);
1104 /* \c ends overall processing */
1105 if (c < 0)
1106 goto jleave;
1109 putchar('\n');
1110 jleave:
1111 NYD_LEAVE;
1112 return 0;
1115 FL int
1116 c_if(void *v)
1118 struct cond_stack *csp;
1119 int rv = 1;
1120 char **argv = v, *cp, *op;
1121 NYD_ENTER;
1123 csp = smalloc(sizeof *csp);
1124 csp->c_outer = _cond_stack;
1125 csp->c_noop = condstack_isskip();
1126 csp->c_go = TRU1;
1127 csp->c_else = FAL0;
1128 _cond_stack = csp;
1130 cp = argv[0];
1131 if (*cp != '$' && argv[1] != NULL) {
1132 jesyn:
1133 fprintf(stderr, tr(528, "Invalid conditional expression \"%s %s %s\"\n"),
1134 argv[0], (argv[1] != NULL ? argv[1] : ""),
1135 (argv[2] != NULL ? argv[2] : ""));
1136 goto jleave;
1139 switch (*cp) {
1140 case '0':
1141 csp->c_go = FAL0;
1142 break;
1143 case 'R': case 'r':
1144 csp->c_go = !(options & OPT_SENDMODE);
1145 break;
1146 case 'S': case 's':
1147 csp->c_go = ((options & OPT_SENDMODE) != 0);
1148 break;
1149 case 'T': case 't':
1150 csp->c_go = ((options & OPT_TTYIN) != 0);
1151 break;
1152 case '$':
1153 /* Look up the value in question, we need it anyway */
1154 v = vok_vlook(++cp);
1156 /* Single argument, "implicit boolean" form? */
1157 if ((op = argv[1]) == NULL) {
1158 csp->c_go = (v != NULL);
1159 break;
1162 /* Three argument comparison form? */
1163 if (argv[2] == NULL || op[0] == '\0' || op[1] != '=' || op[2] != '\0')
1164 goto jesyn;
1165 /* A null value is treated as the empty string */
1166 if (v == NULL)
1167 v = UNCONST("");
1168 if (strcmp(v, argv[2]))
1169 v = NULL;
1170 switch (op[0]) {
1171 case '!':
1172 case '=':
1173 csp->c_go = ((op[0] == '=') ^ (v == NULL));
1174 break;
1175 default:
1176 goto jesyn;
1178 break;
1179 default:
1180 fprintf(stderr, tr(43, "Unrecognized if-keyword: \"%s\"\n"), cp);
1181 case '1':
1182 csp->c_go = TRU1;
1183 goto jleave;
1185 rv = 0;
1186 jleave:
1187 NYD_LEAVE;
1188 return rv;
1191 FL int
1192 c_else(void *v)
1194 int rv;
1195 NYD_ENTER;
1196 UNUSED(v);
1198 if (_cond_stack == NULL || _cond_stack->c_else) {
1199 fprintf(stderr, tr(44, "\"else\" without matching \"if\"\n"));
1200 rv = 1;
1201 } else {
1202 _cond_stack->c_go = !_cond_stack->c_go;
1203 _cond_stack->c_else = TRU1;
1204 rv = 0;
1206 NYD_LEAVE;
1207 return rv;
1210 FL int
1211 c_endif(void *v)
1213 struct cond_stack *csp;
1214 int rv;
1215 NYD_ENTER;
1216 UNUSED(v);
1218 if ((csp = _cond_stack) == NULL) {
1219 fprintf(stderr, tr(46, "\"endif\" without matching \"if\"\n"));
1220 rv = 1;
1221 } else {
1222 _cond_stack = csp->c_outer;
1223 free(csp);
1224 rv = 0;
1226 NYD_LEAVE;
1227 return rv;
1230 FL bool_t
1231 condstack_isskip(void)
1233 bool_t rv;
1234 NYD_ENTER;
1236 rv = (_cond_stack != NULL && (_cond_stack->c_noop || !_cond_stack->c_go));
1237 NYD_LEAVE;
1238 return rv;
1241 FL void *
1242 condstack_release(void)
1244 void *rv;
1245 NYD_ENTER;
1247 rv = _cond_stack;
1248 _cond_stack = NULL;
1249 NYD_LEAVE;
1250 return rv;
1253 FL bool_t
1254 condstack_take(void *self)
1256 struct cond_stack *csp;
1257 bool_t rv;
1258 NYD_ENTER;
1260 if (!(rv = ((csp = _cond_stack) == NULL)))
1261 do {
1262 _cond_stack = csp->c_outer;
1263 free(csp);
1264 } while ((csp = _cond_stack) != NULL);
1266 _cond_stack = self;
1267 NYD_LEAVE;
1268 return rv;
1271 FL int
1272 c_alternates(void *v)
1274 size_t l;
1275 char **namelist = v, **ap, **ap2, *cp;
1276 NYD_ENTER;
1278 l = argcount(namelist) + 1;
1279 if (l == 1) {
1280 if (altnames == NULL)
1281 goto jleave;
1282 for (ap = altnames; *ap != NULL; ++ap)
1283 printf("%s ", *ap);
1284 printf("\n");
1285 goto jleave;
1288 if (altnames != NULL) {
1289 for (ap = altnames; *ap != NULL; ++ap)
1290 free(*ap);
1291 free(altnames);
1293 altnames = smalloc(l * sizeof(char*));
1294 for (ap = namelist, ap2 = altnames; *ap != NULL; ++ap, ++ap2) {
1295 l = strlen(*ap) + 1;
1296 cp = smalloc(l);
1297 memcpy(cp, *ap, l);
1298 *ap2 = cp;
1300 *ap2 = NULL;
1301 jleave:
1302 NYD_LEAVE;
1303 return 0;
1306 FL int
1307 c_newmail(void *v)
1309 int val = 1, mdot;
1310 NYD_ENTER;
1311 UNUSED(v);
1313 if (
1314 #ifdef HAVE_IMAP
1315 (mb.mb_type != MB_IMAP || imap_newmail(1)) &&
1316 #endif
1317 (val = setfile(mailname, 1)) == 0) {
1318 mdot = getmdot(1);
1319 setdot(message + mdot - 1);
1321 NYD_LEAVE;
1322 return val;
1325 FL int
1326 c_shortcut(void *v)
1328 char **args = v;
1329 struct shortcut *s;
1330 int rv;
1331 NYD_ENTER;
1333 if (args[0] == NULL) {
1334 list_shortcuts();
1335 rv = 0;
1336 goto jleave;
1339 rv = 1;
1340 if (args[1] == NULL) {
1341 fprintf(stderr, tr(220, "expansion name for shortcut missing\n"));
1342 goto jleave;
1344 if (args[2] != NULL) {
1345 fprintf(stderr, tr(221, "too many arguments\n"));
1346 goto jleave;
1349 if ((s = get_shortcut(args[0])) != NULL) {
1350 free(s->sh_long);
1351 s->sh_long = sstrdup(args[1]);
1352 } else {
1353 s = scalloc(1, sizeof *s);
1354 s->sh_short = sstrdup(args[0]);
1355 s->sh_long = sstrdup(args[1]);
1356 s->sh_next = shortcuts;
1357 shortcuts = s;
1359 rv = 0;
1360 jleave:
1361 NYD_LEAVE;
1362 return rv;
1365 FL struct shortcut *
1366 get_shortcut(char const *str)
1368 struct shortcut *s;
1369 NYD_ENTER;
1371 for (s = shortcuts; s != NULL; s = s->sh_next)
1372 if (!strcmp(str, s->sh_short))
1373 break;
1374 NYD_LEAVE;
1375 return s;
1378 FL int
1379 c_unshortcut(void *v)
1381 char **args = v;
1382 bool_t errs = FAL0;
1383 NYD_ENTER;
1385 if (args[0] == NULL) {
1386 fprintf(stderr, tr(222, "need shortcut names to remove\n"));
1387 errs = TRU1;
1388 goto jleave;
1391 while (*args != NULL) {
1392 if (delete_shortcut(*args) != OKAY) {
1393 errs = TRU1;
1394 fprintf(stderr, tr(223, "%s: no such shortcut\n"), *args);
1396 ++args;
1398 jleave:
1399 NYD_LEAVE;
1400 return errs;
1403 FL int
1404 c_flag(void *v)
1406 struct message *m;
1407 int *msgvec = v, *ip;
1408 NYD_ENTER;
1410 for (ip = msgvec; *ip != 0; ++ip) {
1411 m = message + *ip - 1;
1412 setdot(m);
1413 if (!(m->m_flag & (MFLAG | MFLAGGED)))
1414 m->m_flag |= MFLAG | MFLAGGED;
1416 NYD_LEAVE;
1417 return 0;
1420 FL int
1421 c_unflag(void *v)
1423 struct message *m;
1424 int *msgvec = v, *ip;
1425 NYD_ENTER;
1427 for (ip = msgvec; *ip != 0; ++ip) {
1428 m = message + *ip - 1;
1429 setdot(m);
1430 if (m->m_flag & (MFLAG | MFLAGGED)) {
1431 m->m_flag &= ~(MFLAG | MFLAGGED);
1432 m->m_flag |= MUNFLAG;
1435 NYD_LEAVE;
1436 return 0;
1439 FL int
1440 c_answered(void *v)
1442 struct message *m;
1443 int *msgvec = v, *ip;
1444 NYD_ENTER;
1446 for (ip = msgvec; *ip != 0; ++ip) {
1447 m = message + *ip - 1;
1448 setdot(m);
1449 if (!(m->m_flag & (MANSWER | MANSWERED)))
1450 m->m_flag |= MANSWER | MANSWERED;
1452 NYD_LEAVE;
1453 return 0;
1456 FL int
1457 c_unanswered(void *v)
1459 struct message *m;
1460 int *msgvec = v, *ip;
1461 NYD_ENTER;
1463 for (ip = msgvec; *ip != 0; ++ip) {
1464 m = message + *ip - 1;
1465 setdot(m);
1466 if (m->m_flag & (MANSWER | MANSWERED)) {
1467 m->m_flag &= ~(MANSWER | MANSWERED);
1468 m->m_flag |= MUNANSWER;
1471 NYD_LEAVE;
1472 return 0;
1475 FL int
1476 c_draft(void *v)
1478 struct message *m;
1479 int *msgvec = v, *ip;
1480 NYD_ENTER;
1482 for (ip = msgvec; *ip != 0; ++ip) {
1483 m = message + *ip - 1;
1484 setdot(m);
1485 if (!(m->m_flag & (MDRAFT | MDRAFTED)))
1486 m->m_flag |= MDRAFT | MDRAFTED;
1488 NYD_LEAVE;
1489 return 0;
1492 FL int
1493 c_undraft(void *v)
1495 struct message *m;
1496 int *msgvec = v, *ip;
1497 NYD_ENTER;
1499 for (ip = msgvec; *ip != 0; ++ip) {
1500 m = message + *ip - 1;
1501 setdot(m);
1502 if (m->m_flag & (MDRAFT | MDRAFTED)) {
1503 m->m_flag &= ~(MDRAFT | MDRAFTED);
1504 m->m_flag |= MUNDRAFT;
1507 NYD_LEAVE;
1508 return 0;
1511 FL int
1512 c_noop(void *v)
1514 int rv = 0;
1515 NYD_ENTER;
1516 UNUSED(v);
1518 switch (mb.mb_type) {
1519 case MB_IMAP:
1520 #ifdef HAVE_IMAP
1521 imap_noop();
1522 #else
1523 rv = c_cmdnotsupp(NULL);
1524 #endif
1525 break;
1526 case MB_POP3:
1527 #ifdef HAVE_POP3
1528 pop3_noop();
1529 #else
1530 rv = c_cmdnotsupp(NULL);
1531 #endif
1532 break;
1533 default:
1534 break;
1536 NYD_LEAVE;
1537 return rv;
1540 FL int
1541 c_remove(void *v)
1543 char const *fmt;
1544 size_t fmt_len;
1545 char **args = v, *name;
1546 int ec = 0;
1547 NYD_ENTER;
1549 if (*args == NULL) {
1550 fprintf(stderr, tr(290, "Syntax is: remove mailbox ...\n"));
1551 ec = 1;
1552 goto jleave;
1555 fmt = tr(287, "Remove \"%s\" (y/n) ? ");
1556 fmt_len = strlen(fmt);
1557 do {
1558 if ((name = expand(*args)) == NULL)
1559 continue;
1561 if (!strcmp(name, mailname)) {
1562 fprintf(stderr, tr(286, "Cannot remove current mailbox \"%s\".\n"),
1563 name);
1564 ec |= 1;
1565 continue;
1568 size_t vl = strlen(name) + fmt_len +1;
1569 char *vb = ac_alloc(vl);
1570 bool_t asw;
1571 snprintf(vb, vl, fmt, name);
1572 asw = getapproval(vb, TRU1);
1573 ac_free(vb);
1574 if (!asw)
1575 continue;
1578 switch (which_protocol(name)) {
1579 case PROTO_FILE:
1580 if (unlink(name) == -1) { /* TODO do not handle .gz .bz2 .xz.. */
1581 perror(name);
1582 ec |= 1;
1584 break;
1585 case PROTO_POP3:
1586 fprintf(stderr, tr(288, "Cannot remove POP3 mailbox \"%s\".\n"),name);
1587 ec |= 1;
1588 break;
1589 case PROTO_IMAP:
1590 #ifdef HAVE_IMAP
1591 if (imap_remove(name) != OKAY)
1592 #endif
1593 ec |= 1;
1594 break;
1595 case PROTO_MAILDIR:
1596 if (maildir_remove(name) != OKAY)
1597 ec |= 1;
1598 break;
1599 case PROTO_UNKNOWN:
1600 fprintf(stderr, tr(289, "Unknown protocol in \"%s\". Not removed.\n"),
1601 name);
1602 ec |= 1;
1603 break;
1605 } while (*++args != NULL);
1606 jleave:
1607 NYD_LEAVE;
1608 return ec;
1611 FL int
1612 c_rename(void *v)
1614 char **args = v, *old, *new;
1615 enum protocol oldp, newp;
1616 int ec;
1617 NYD_ENTER;
1619 ec = 1;
1621 if (args[0] == NULL || args[1] == NULL || args[2] != NULL) {
1622 fprintf(stderr, "Syntax: rename old new\n");
1623 goto jleave;
1626 if ((old = expand(args[0])) == NULL)
1627 goto jleave;
1628 oldp = which_protocol(old);
1629 if ((new = expand(args[1])) == NULL)
1630 goto jleave;
1631 newp = which_protocol(new);
1633 if (!strcmp(old, mailname) || !strcmp(new, mailname)) {
1634 fprintf(stderr, tr(291, "Cannot rename current mailbox \"%s\".\n"), old);
1635 goto jleave;
1637 if ((oldp == PROTO_IMAP || newp == PROTO_IMAP) && oldp != newp) {
1638 fprintf(stderr, tr(292, "Can only rename folders of same type.\n"));
1639 goto jleave;
1642 ec = 0;
1644 if (newp == PROTO_POP3)
1645 goto jnopop3;
1646 switch (oldp) {
1647 case PROTO_FILE:
1648 if (link(old, new) == -1) {
1649 switch (errno) {
1650 case EACCES:
1651 case EEXIST:
1652 case ENAMETOOLONG:
1653 case ENOENT:
1654 case ENOSPC:
1655 case EXDEV:
1656 perror(new);
1657 break;
1658 default:
1659 perror(old);
1661 ec |= 1;
1662 } else if (unlink(old) == -1) {
1663 perror(old);
1664 ec |= 1;
1666 break;
1667 case PROTO_MAILDIR:
1668 if (rename(old, new) == -1) {
1669 perror(old);
1670 ec |= 1;
1672 break;
1673 case PROTO_POP3:
1674 jnopop3:
1675 fprintf(stderr, tr(293, "Cannot rename POP3 mailboxes.\n"));
1676 ec |= 1;
1677 break;
1678 #ifdef HAVE_IMAP
1679 case PROTO_IMAP:
1680 if (imap_rename(old, new) != OKAY)
1681 ec |= 1;
1682 break;
1683 #endif
1684 case PROTO_UNKNOWN:
1685 default:
1686 fprintf(stderr, tr(294,
1687 "Unknown protocol in \"%s\" and \"%s\". Not renamed.\n"), old, new);
1688 ec |= 1;
1689 break;
1691 jleave:
1692 NYD_LEAVE;
1693 return ec;
1696 /* vim:set fenc=utf-8:s-it-mode */