Bump S-nail v14.7.6
[s-mailx.git] / cmd3.c
blob4aba41fdd3575851f1f264f6a41b410164985ce4
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 _fwd(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, bool_t add_resent);
91 /* c_file, c_File */
92 static int _c_file(void *v, enum fedit_mode fm);
94 /* ..to stdout */
95 static void list_shortcuts(void);
97 /* */
98 static enum okay delete_shortcut(char const *str);
100 static char *
101 _reedit(char *subj)
103 struct str in, out;
104 char *newsubj = NULL;
105 NYD_ENTER;
107 if (subj == NULL || *subj == '\0')
108 goto jleave;
110 in.s = subj;
111 in.l = strlen(subj);
112 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
114 if ((newsubj = subject_re_trim(out.s)) != out.s)
115 newsubj = savestr(out.s);
116 else {
117 newsubj = salloc(out.l + 4 +1);
118 sstpcpy(sstpcpy(newsubj, "Re: "), out.s);
121 free(out.s);
122 jleave:
123 NYD_LEAVE;
124 return newsubj;
127 static void
128 _bangexp(char **str, size_t *size)
130 char *bangbuf;
131 int changed = 0;
132 bool_t dobang;
133 size_t sz, i, j, bangbufsize;
134 NYD_ENTER;
136 dobang = ok_blook(bang);
138 bangbuf = smalloc(bangbufsize = *size);
139 i = j = 0;
140 while ((*str)[i]) {
141 if (dobang) {
142 if ((*str)[i] == '!') {
143 sz = strlen(_bang_buf);
144 bangbuf = srealloc(bangbuf, bangbufsize += sz);
145 ++changed;
146 memcpy(bangbuf + j, _bang_buf, sz + 1);
147 j += sz;
148 i++;
149 continue;
152 if ((*str)[i] == '\\' && (*str)[i + 1] == '!') {
153 bangbuf[j++] = '!';
154 i += 2;
155 ++changed;
157 bangbuf[j++] = (*str)[i++];
159 bangbuf[j] = '\0';
160 if (changed) {
161 printf("!%s\n", bangbuf);
162 fflush(stdout);
164 sz = j + 1;
165 if (sz > *size)
166 *str = srealloc(*str, *size = sz);
167 memcpy(*str, bangbuf, sz);
168 if (sz > _bang_size)
169 _bang_buf = srealloc(_bang_buf, _bang_size = sz);
170 memcpy(_bang_buf, bangbuf, sz);
171 free(bangbuf);
172 NYD_LEAVE;
175 static void
176 make_ref_and_cs(struct message *mp, struct header *head)
178 char *oldref, *oldmsgid, *newref, *cp;
179 size_t oldreflen = 0, oldmsgidlen = 0, reflen;
180 unsigned i;
181 struct name *n;
182 NYD_ENTER;
184 oldref = hfield1("references", mp);
185 oldmsgid = hfield1("message-id", mp);
186 if (oldmsgid == NULL || *oldmsgid == '\0') {
187 head->h_ref = NULL;
188 goto jleave;
191 reflen = 1;
192 if (oldref) {
193 oldreflen = strlen(oldref);
194 reflen += oldreflen + 2;
196 if (oldmsgid) {
197 oldmsgidlen = strlen(oldmsgid);
198 reflen += oldmsgidlen;
201 newref = ac_alloc(reflen);
202 if (oldref != NULL) {
203 memcpy(newref, oldref, oldreflen +1);
204 if (oldmsgid != NULL) {
205 newref[oldreflen++] = ',';
206 newref[oldreflen++] = ' ';
207 memcpy(newref + oldreflen, oldmsgid, oldmsgidlen +1);
209 } else if (oldmsgid)
210 memcpy(newref, oldmsgid, oldmsgidlen +1);
211 n = extract(newref, GREF);
212 ac_free(newref);
214 /* Limit number of references */
215 while (n->n_flink != NULL)
216 n = n->n_flink;
217 for (i = 1; i < 21; ++i) { /* XXX no magics */
218 if (n->n_blink != NULL)
219 n = n->n_blink;
220 else
221 break;
223 n->n_blink = NULL;
224 head->h_ref = n;
225 if (ok_blook(reply_in_same_charset) &&
226 (cp = hfield1("content-type", mp)) != NULL)
227 head->h_charset = mime_getparam("charset", cp);
228 jleave:
229 NYD_LEAVE;
232 static int
233 (*respond_or_Respond(int c))(int *, int)
235 int opt;
236 int (*rv)(int*, int);
237 NYD_ENTER;
239 opt = ok_blook(Replyall);
240 opt += ok_blook(flipr);
241 rv = ((opt == 1) ^ (c == 'R')) ? &Respond_internal : &respond_internal;
242 NYD_LEAVE;
243 return rv;
246 static int
247 respond_internal(int *msgvec, int recipient_record)
249 struct header head;
250 struct message *mp;
251 char *cp, *rcv;
252 struct name *np = NULL;
253 enum gfield gf;
254 int rv = 1;
255 NYD_ENTER;
257 gf = ok_blook(fullnames) ? GFULL : GSKIN;
259 if (msgvec[1] != 0) {
260 fprintf(stderr, _(
261 "Sorry, can't reply to multiple messages at once\n"));
262 goto jleave;
265 mp = message + msgvec[0] - 1;
266 touch(mp);
267 setdot(mp);
269 if ((rcv = hfield1("reply-to", mp)) == NULL)
270 if ((rcv = hfield1("from", mp)) == NULL)
271 rcv = nameof(mp, 1);
272 if (rcv != NULL)
273 np = lextract(rcv, GTO | gf);
274 if (!ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
275 np = cat(np, lextract(cp, GTO | gf));
276 /* Delete my name from reply list, and with it, all my alternate names */
277 np = elide(delete_alternates(np));
278 if (np == NULL)
279 np = lextract(rcv, GTO | gf);
281 memset(&head, 0, sizeof head);
282 head.h_to = np;
283 head.h_subject = hfield1("subject", mp);
284 head.h_subject = _reedit(head.h_subject);
286 /* Cc: */
287 np = NULL;
288 if (ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
289 np = lextract(cp, GCC | gf);
290 if ((cp = hfield1("cc", mp)) != NULL)
291 np = cat(np, lextract(cp, GCC | gf));
292 if (np != NULL)
293 head.h_cc = elide(delete_alternates(np));
294 make_ref_and_cs(mp, &head);
296 if (ok_blook(quote_as_attachment)) {
297 head.h_attach = csalloc(1, sizeof *head.h_attach);
298 head.h_attach->a_msgno = *msgvec;
299 head.h_attach->a_content_description = _(
300 "Original message content");
303 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
304 ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
305 mp->m_flag |= MANSWER | MANSWERED;
306 rv = 0;
307 jleave:
308 NYD_LEAVE;
309 return rv;
312 static int
313 Respond_internal(int *msgvec, int recipient_record)
315 struct header head;
316 struct message *mp;
317 int *ap;
318 char *cp;
319 enum gfield gf;
320 NYD_ENTER;
322 memset(&head, 0, sizeof head);
323 gf = ok_blook(fullnames) ? GFULL : GSKIN;
325 for (ap = msgvec; *ap != 0; ++ap) {
326 mp = message + *ap - 1;
327 touch(mp);
328 setdot(mp);
329 if ((cp = hfield1("reply-to", mp)) == NULL)
330 if ((cp = hfield1("from", mp)) == NULL)
331 cp = nameof(mp, 2);
332 head.h_to = cat(head.h_to, lextract(cp, GTO | gf));
334 if (head.h_to == NULL)
335 goto jleave;
337 mp = message + msgvec[0] - 1;
338 head.h_subject = hfield1("subject", mp);
339 head.h_subject = _reedit(head.h_subject);
340 make_ref_and_cs(mp, &head);
342 if (ok_blook(quote_as_attachment)) {
343 head.h_attach = csalloc(1, sizeof *head.h_attach);
344 head.h_attach->a_msgno = *msgvec;
345 head.h_attach->a_content_description = _(
346 "Original message content");
349 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
350 ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
351 mp->m_flag |= MANSWER | MANSWERED;
352 jleave:
353 NYD_LEAVE;
354 return 0;
357 static int
358 _fwd(char *str, int recipient_record)
360 struct header head;
361 int *msgvec, rv = 1;
362 char *recipient;
363 struct message *mp;
364 bool_t f, forward_as_attachment;
365 NYD_ENTER;
367 if ((recipient = laststring(str, &f, TRU1)) == NULL) {
368 puts(_("No recipient specified."));
369 goto jleave;
372 forward_as_attachment = ok_blook(forward_as_attachment);
373 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
375 if (!f) {
376 *msgvec = first(0, MMNORM);
377 if (*msgvec == 0) {
378 if (inhook) {
379 rv = 0;
380 goto jleave;
382 printf("No messages to forward.\n");
383 goto jleave;
385 msgvec[1] = 0;
386 } else if (getmsglist(str, msgvec, 0) < 0)
387 goto jleave;
389 if (*msgvec == 0) {
390 if (inhook) {
391 rv = 0;
392 goto jleave;
394 printf("No applicable messages.\n");
395 goto jleave;
397 if (msgvec[1] != 0) {
398 printf("Cannot forward multiple messages at once\n");
399 goto jleave;
402 memset(&head, 0, sizeof head);
403 if ((head.h_to = lextract(recipient,
404 (GTO | (ok_blook(fullnames) ? GFULL : GSKIN)))) == NULL)
405 goto jleave;
407 mp = message + *msgvec - 1;
409 if (forward_as_attachment) {
410 head.h_attach = csalloc(1, sizeof *head.h_attach);
411 head.h_attach->a_msgno = *msgvec;
412 head.h_attach->a_content_description = "Forwarded message";
413 } else {
414 touch(mp);
415 setdot(mp);
417 head.h_subject = hfield1("subject", mp);
418 head.h_subject = __fwdedit(head.h_subject);
419 mail1(&head, 1, (forward_as_attachment ? NULL : mp), NULL, recipient_record,
421 rv = 0;
422 jleave:
423 NYD_LEAVE;
424 return rv;
427 static char *
428 __fwdedit(char *subj)
430 struct str in, out;
431 char *newsubj = NULL;
432 NYD_ENTER;
434 if (subj == NULL || *subj == '\0')
435 goto jleave;
437 in.s = subj;
438 in.l = strlen(subj);
439 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
441 newsubj = salloc(out.l + 6);
442 memcpy(newsubj, "Fwd: ", 5); /* XXX localizable */
443 memcpy(newsubj + 5, out.s, out.l +1);
444 free(out.s);
445 jleave:
446 NYD_LEAVE;
447 return newsubj;
450 static void
451 asort(char **list)
453 char **ap;
454 size_t i;
455 NYD_ENTER;
457 for (ap = list; *ap != NULL; ++ap)
459 if ((i = PTR2SIZE(ap - list)) >= 2)
460 qsort(list, i, sizeof *list, diction);
461 NYD_LEAVE;
464 static int
465 diction(void const *a, void const *b)
467 int rv;
468 NYD_ENTER;
470 rv = strcmp(*(char**)UNCONST(a), *(char**)UNCONST(b));
471 NYD_LEAVE;
472 return rv;
475 static int
476 _resend1(void *v, bool_t add_resent)
478 char *name, *str;
479 struct name *to, *sn;
480 int *ip, *msgvec;
481 bool_t f = TRU1;
482 NYD_ENTER;
484 str = v;
485 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
486 name = laststring(str, &f, TRU1);
487 if (name == NULL) {
488 puts(_("No recipient specified."));
489 goto jleave;
492 if (!f) {
493 *msgvec = first(0, MMNORM);
494 if (*msgvec == 0) {
495 if (inhook) {
496 f = FAL0;
497 goto jleave;
499 puts(_("No applicable messages."));
500 goto jleave;
502 msgvec[1] = 0;
503 } else if (getmsglist(str, msgvec, 0) < 0)
504 goto jleave;
506 if (*msgvec == 0) {
507 if (inhook) {
508 f = FAL0;
509 goto jleave;
511 printf("No applicable messages.\n");
512 goto jleave;
515 sn = nalloc(name, GTO | GSKIN);
516 to = usermap(sn, FAL0);
517 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
518 ++ip)
519 if (resend_msg(message + *ip - 1, to, add_resent) != OKAY)
520 goto jleave;
521 f = FAL0;
522 jleave:
523 NYD_LEAVE;
524 return (f != FAL0);
527 static int
528 _c_file(void *v, enum fedit_mode fm)
530 char **argv = v;
531 int i;
532 NYD2_ENTER;
534 if (*argv == NULL) {
535 newfileinfo();
536 i = 0;
537 goto jleave;
540 if (inhook) {
541 fprintf(stderr, _("Cannot change folder from within a hook.\n"));
542 i = 1;
543 goto jleave;
546 save_mbox_for_possible_quitstuff();
548 i = setfile(*argv, fm);
549 if (i < 0) {
550 i = 1;
551 goto jleave;
553 callhook(mailname, 0);
554 if (i > 0 && !ok_blook(emptystart)) {
555 i = 1;
556 goto jleave;
558 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
559 i = 0;
560 jleave:
561 NYD2_LEAVE;
562 return i;
565 static void
566 list_shortcuts(void)
568 struct shortcut *s;
569 NYD_ENTER;
571 for (s = shortcuts; s != NULL; s = s->sh_next)
572 printf("%s=%s\n", s->sh_short, s->sh_long);
573 NYD_LEAVE;
576 static enum okay
577 delete_shortcut(char const *str)
579 struct shortcut *sp, *sq;
580 enum okay rv = STOP;
581 NYD_ENTER;
583 for (sp = shortcuts, sq = NULL; sp != NULL; sq = sp, sp = sp->sh_next) {
584 if (!strcmp(sp->sh_short, str)) {
585 free(sp->sh_short);
586 free(sp->sh_long);
587 if (sq != NULL)
588 sq->sh_next = sp->sh_next;
589 if (sp == shortcuts)
590 shortcuts = sp->sh_next;
591 free(sp);
592 rv = OKAY;
593 break;
596 NYD_LEAVE;
597 return rv;
600 FL int
601 c_shell(void *v)
603 char const *sh = NULL;
604 char *str = v, *cmd;
605 size_t cmdsize;
606 sigset_t mask;
607 sighandler_type sigint;
608 NYD_ENTER;
610 cmd = smalloc(cmdsize = strlen(str) +1);
611 memcpy(cmd, str, cmdsize);
612 _bangexp(&cmd, &cmdsize);
613 if ((sh = ok_vlook(SHELL)) == NULL)
614 sh = XSHELL;
616 sigint = safe_signal(SIGINT, SIG_IGN);
617 sigemptyset(&mask);
618 run_command(sh, &mask, -1, -1, "-c", cmd, NULL);
619 safe_signal(SIGINT, sigint);
620 printf("!\n");
622 free(cmd);
623 NYD_LEAVE;
624 return 0;
627 FL int
628 c_dosh(void *v)
630 sighandler_type sigint;
631 char const *sh;
632 NYD_ENTER;
633 UNUSED(v);
635 if ((sh = ok_vlook(SHELL)) == NULL)
636 sh = XSHELL;
638 sigint = safe_signal(SIGINT, SIG_IGN);
639 run_command(sh, 0, -1, -1, NULL, NULL, NULL);
640 safe_signal(SIGINT, sigint);
641 putchar('\n');
642 NYD_LEAVE;
643 return 0;
646 FL int
647 c_help(void *v)
649 int ret = 0;
650 char *arg;
651 NYD_ENTER;
653 arg = *(char**)v;
655 if (arg != NULL) {
656 #ifdef HAVE_DOCSTRINGS
657 ret = !print_comm_docstr(arg);
658 if (ret)
659 fprintf(stderr, _("Unknown command: `%s'\n"), arg);
660 #else
661 ret = c_cmdnotsupp(NULL);
662 #endif
663 goto jleave;
666 /* Very ugly, but take care for compiler supported string lengths :( */
667 printf(_("%s commands:\n"), progname);
668 puts(_(
669 "type <message list> type messages\n"
670 "next goto and type next message\n"
671 "from <message list> give head lines of messages\n"
672 "headers print out active message headers\n"
673 "delete <message list> delete messages\n"
674 "undelete <message list> undelete messages\n"));
675 puts(_(
676 "save <message list> folder append messages to folder and mark as saved\n"
677 "copy <message list> folder append messages to folder without marking them\n"
678 "write <message list> file append message texts to file, save attachments\n"
679 "preserve <message list> keep incoming messages in mailbox even if saved\n"
680 "Reply <message list> reply to message senders\n"
681 "reply <message list> reply to message senders and all recipients\n"));
682 puts(_(
683 "mail addresses mail to specific recipients\n"
684 "file folder change to another folder\n"
685 "quit quit and apply changes to folder\n"
686 "xit quit and discard changes made to folder\n"
687 "! shell escape\n"
688 "cd <directory> chdir to directory or home if none given\n"
689 "list list names of all available commands\n"));
690 printf(_(
691 "A <message list> consists of integers, ranges of same, or other criteria\n"
692 "separated by spaces. If omitted, %s uses the last message typed.\n"),
693 progname);
695 jleave:
696 NYD_LEAVE;
697 return ret;
700 FL int
701 c_cwd(void *v)
703 char buf[PATH_MAX]; /* TODO getcwd(3) may return a larger value */
704 NYD_ENTER;
706 if (getcwd(buf, sizeof buf) != NULL) {
707 puts(buf);
708 v = (void*)0x1;
709 } else {
710 perror("getcwd");
711 v = NULL;
713 NYD_LEAVE;
714 return (v == NULL);
717 FL int
718 c_chdir(void *v)
720 char **arglist = v;
721 char const *cp;
722 NYD_ENTER;
724 if (*arglist == NULL)
725 cp = homedir;
726 else if ((cp = file_expand(*arglist)) == NULL)
727 goto jleave;
728 if (chdir(cp) == -1) {
729 perror(cp);
730 cp = NULL;
732 jleave:
733 NYD_LEAVE;
734 return (cp == NULL);
737 FL int
738 c_respond(void *v)
740 int rv;
741 NYD_ENTER;
743 rv = (*respond_or_Respond('r'))(v, 0);
744 NYD_LEAVE;
745 return rv;
748 FL int
749 c_respondall(void *v)
751 int rv;
752 NYD_ENTER;
754 rv = respond_internal(v, 0);
755 NYD_LEAVE;
756 return rv;
759 FL int
760 c_respondsender(void *v)
762 int rv;
763 NYD_ENTER;
765 rv = Respond_internal(v, 0);
766 NYD_LEAVE;
767 return rv;
770 FL int
771 c_Respond(void *v)
773 int rv;
774 NYD_ENTER;
776 rv = (*respond_or_Respond('R'))(v, 0);
777 NYD_LEAVE;
778 return rv;
781 FL int
782 c_followup(void *v)
784 int rv;
785 NYD_ENTER;
787 rv = (*respond_or_Respond('r'))(v, 1);
788 NYD_LEAVE;
789 return rv;
792 FL int
793 c_followupall(void *v)
795 int rv;
796 NYD_ENTER;
798 rv = respond_internal(v, 1);
799 NYD_LEAVE;
800 return rv;
803 FL int
804 c_followupsender(void *v)
806 int rv;
807 NYD_ENTER;
809 rv = Respond_internal(v, 1);
810 NYD_LEAVE;
811 return rv;
814 FL int
815 c_Followup(void *v)
817 int rv;
818 NYD_ENTER;
820 rv = (*respond_or_Respond('R'))(v, 1);
821 NYD_LEAVE;
822 return rv;
825 FL int
826 c_forward(void *v)
828 int rv;
829 NYD_ENTER;
831 rv = _fwd(v, 0);
832 NYD_LEAVE;
833 return rv;
836 FL int
837 c_Forward(void *v)
839 int rv;
840 NYD_ENTER;
842 rv = _fwd(v, 1);
843 NYD_LEAVE;
844 return rv;
847 FL int
848 c_resend(void *v)
850 int rv;
851 NYD_ENTER;
853 rv = _resend1(v, TRU1);
854 NYD_LEAVE;
855 return rv;
858 FL int
859 c_Resend(void *v)
861 int rv;
862 NYD_ENTER;
864 rv = _resend1(v, FAL0);
865 NYD_LEAVE;
866 return rv;
869 FL int
870 c_preserve(void *v)
872 int *msgvec = v, *ip, mesg, rv = 1;
873 struct message *mp;
874 NYD_ENTER;
876 if (edit) {
877 printf(_("Cannot \"preserve\" in edit mode\n"));
878 goto jleave;
881 for (ip = msgvec; *ip != 0; ++ip) {
882 mesg = *ip;
883 mp = message + mesg - 1;
884 mp->m_flag |= MPRESERVE;
885 mp->m_flag &= ~MBOX;
886 setdot(mp);
887 did_print_dot = TRU1;
889 rv = 0;
890 jleave:
891 NYD_LEAVE;
892 return rv;
895 FL int
896 c_unread(void *v)
898 int *msgvec = v, *ip;
899 NYD_ENTER;
901 for (ip = msgvec; *ip != 0; ++ip) {
902 setdot(message + *ip - 1);
903 dot->m_flag &= ~(MREAD | MTOUCH);
904 dot->m_flag |= MSTATUS;
905 #ifdef HAVE_IMAP
906 if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
907 imap_unread(message + *ip - 1, *ip); /* TODO return? */
908 #endif
909 did_print_dot = TRU1;
911 NYD_LEAVE;
912 return 0;
915 FL int
916 c_seen(void *v)
918 int *msgvec = v, *ip;
919 NYD_ENTER;
921 for (ip = msgvec; *ip != 0; ++ip) {
922 struct message *mp = message + *ip - 1;
923 setdot(mp);
924 touch(mp);
926 NYD_LEAVE;
927 return 0;
930 FL int
931 c_messize(void *v)
933 int *msgvec = v, *ip, mesg;
934 struct message *mp;
935 NYD_ENTER;
937 for (ip = msgvec; *ip != 0; ++ip) {
938 mesg = *ip;
939 mp = message + mesg - 1;
940 printf("%d: ", mesg);
941 if (mp->m_xlines > 0)
942 printf("%ld", mp->m_xlines);
943 else
944 putchar(' ');
945 printf("/%lu\n", (ul_it)mp->m_xsize);
947 NYD_LEAVE;
948 return 0;
951 FL int
952 c_rexit(void *v)
954 UNUSED(v);
955 NYD_ENTER;
957 if (!sourcing)
958 exit(0);
959 NYD_LEAVE;
960 return 1;
963 FL int
964 c_group(void *v)
966 char **argv = v, **ap, *gname, **p;
967 struct grouphead *gh;
968 struct group *gp;
969 int h, s;
970 NYD_ENTER;
972 if (*argv == NULL) {
973 for (h = 0, s = 1; h < HSHSIZE; ++h)
974 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
975 ++s;
976 ap = salloc(s * sizeof *ap);
978 for (h = 0, p = ap; h < HSHSIZE; ++h)
979 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
980 *p++ = gh->g_name;
981 *p = NULL;
983 asort(ap);
985 for (p = ap; *p != NULL; ++p)
986 printgroup(*p);
987 goto jleave;
990 if (argv[1] == NULL) {
991 printgroup(*argv);
992 goto jleave;
995 gname = *argv;
996 h = hash(gname);
997 if ((gh = findgroup(gname)) == NULL) {
998 gh = scalloc(1, sizeof *gh);
999 gh->g_name = sstrdup(gname);
1000 gh->g_list = NULL;
1001 gh->g_link = groups[h];
1002 groups[h] = gh;
1005 /* Insert names from the command list into the group. Who cares if there
1006 * are duplicates? They get tossed later anyway */
1007 for (ap = argv + 1; *ap != NULL; ++ap) {
1008 gp = scalloc(1, sizeof *gp);
1009 gp->ge_name = sstrdup(*ap);
1010 gp->ge_link = gh->g_list;
1011 gh->g_list = gp;
1013 jleave:
1014 NYD_LEAVE;
1015 return 0;
1018 FL int
1019 c_ungroup(void *v)
1021 char **argv = v;
1022 int rv = 1;
1023 NYD_ENTER;
1025 if (*argv == NULL) {
1026 fprintf(stderr, _("Must specify alias to remove\n"));
1027 goto jleave;
1031 remove_group(*argv);
1032 while (*++argv != NULL);
1033 rv = 0;
1034 jleave:
1035 NYD_LEAVE;
1036 return rv;
1039 FL int
1040 c_file(void *v)
1042 int rv;
1043 NYD_ENTER;
1045 rv = _c_file(v, FEDIT_NONE);
1046 NYD_LEAVE;
1047 return rv;
1050 FL int
1051 c_File(void *v)
1053 int rv;
1054 NYD_ENTER;
1056 rv = _c_file(v, FEDIT_RDONLY);
1057 NYD_LEAVE;
1058 return rv;
1061 FL int
1062 c_echo(void *v)
1064 char const **argv = v, **ap, *cp;
1065 int c;
1066 NYD_ENTER;
1068 for (ap = argv; *ap != NULL; ++ap) {
1069 cp = *ap;
1070 if ((cp = fexpand(cp, FEXP_NSHORTCUT)) != NULL) {
1071 if (ap != argv)
1072 putchar(' ');
1073 c = 0;
1074 while (*cp != '\0' && (c = expand_shell_escape(&cp, FAL0)) > 0)
1075 putchar(c);
1076 /* \c ends overall processing */
1077 if (c < 0)
1078 goto jleave;
1081 putchar('\n');
1082 jleave:
1083 NYD_LEAVE;
1084 return 0;
1087 FL int
1088 c_if(void *v)
1090 struct cond_stack *csp;
1091 int rv = 1;
1092 char **argv = v, *cp, *op;
1093 NYD_ENTER;
1095 csp = smalloc(sizeof *csp);
1096 csp->c_outer = _cond_stack;
1097 csp->c_noop = condstack_isskip();
1098 csp->c_go = TRU1;
1099 csp->c_else = FAL0;
1100 _cond_stack = csp;
1102 cp = argv[0];
1103 if (*cp != '$' && argv[1] != NULL) {
1104 jesyn:
1105 fprintf(stderr, _("Invalid conditional expression \"%s %s %s\"\n"),
1106 argv[0], (argv[1] != NULL ? argv[1] : ""),
1107 (argv[2] != NULL ? argv[2] : ""));
1108 goto jleave;
1111 switch (*cp) {
1112 case '0':
1113 csp->c_go = FAL0;
1114 break;
1115 case 'R': case 'r':
1116 csp->c_go = !(options & OPT_SENDMODE);
1117 break;
1118 case 'S': case 's':
1119 csp->c_go = ((options & OPT_SENDMODE) != 0);
1120 break;
1121 case 'T': case 't':
1122 csp->c_go = ((options & OPT_TTYIN) != 0);
1123 break;
1124 case '$':
1125 /* Look up the value in question, we need it anyway */
1126 v = vok_vlook(++cp);
1128 /* Single argument, "implicit boolean" form? */
1129 if ((op = argv[1]) == NULL) {
1130 csp->c_go = (v != NULL);
1131 break;
1134 /* Three argument comparison form? */
1135 if (argv[2] == NULL || op[0] == '\0' ||
1136 #ifdef HAVE_REGEX
1137 (op[1] != '=' && op[1] != '~') ||
1138 #else
1139 op[1] != '=' ||
1140 #endif
1141 op[2] != '\0')
1142 goto jesyn;
1144 /* A null value is treated as the empty string */
1145 if (v == NULL)
1146 v = UNCONST("");
1147 #ifdef HAVE_REGEX
1148 if (op[1] == '~') {
1149 regex_t re;
1151 if (regcomp(&re, argv[2], REG_EXTENDED | REG_ICASE | REG_NOSUB))
1152 goto jesyn;
1153 if (regexec(&re, v, 0,NULL, 0) == REG_NOMATCH)
1154 v = NULL;
1155 regfree(&re);
1156 } else
1157 #endif
1158 if (strcmp(v, argv[2]))
1159 v = NULL;
1160 switch (op[0]) {
1161 case '!':
1162 case '=':
1163 csp->c_go = ((op[0] == '=') ^ (v == NULL));
1164 break;
1165 default:
1166 goto jesyn;
1168 break;
1169 default:
1170 fprintf(stderr, _("Unrecognized if-keyword: \"%s\"\n"), cp);
1171 case '1':
1172 csp->c_go = TRU1;
1173 goto jleave;
1175 rv = 0;
1176 jleave:
1177 NYD_LEAVE;
1178 return rv;
1181 FL int
1182 c_elif(void *v)
1184 struct cond_stack *csp;
1185 int rv;
1186 NYD_ENTER;
1188 if ((csp = _cond_stack) == NULL || csp->c_else) {
1189 fprintf(stderr, _("`elif' without matching `if'\n"));
1190 rv = 1;
1191 } else {
1192 csp->c_go = !csp->c_go;
1193 rv = c_if(v);
1194 _cond_stack->c_outer = csp->c_outer;
1195 free(csp);
1197 NYD_LEAVE;
1198 return rv;
1201 FL int
1202 c_else(void *v)
1204 int rv;
1205 NYD_ENTER;
1206 UNUSED(v);
1208 if (_cond_stack == NULL || _cond_stack->c_else) {
1209 fprintf(stderr, _("`else' without matching `if'\n"));
1210 rv = 1;
1211 } else {
1212 _cond_stack->c_go = !_cond_stack->c_go;
1213 _cond_stack->c_else = TRU1;
1214 rv = 0;
1216 NYD_LEAVE;
1217 return rv;
1220 FL int
1221 c_endif(void *v)
1223 struct cond_stack *csp;
1224 int rv;
1225 NYD_ENTER;
1226 UNUSED(v);
1228 if ((csp = _cond_stack) == NULL) {
1229 fprintf(stderr, _("`endif' without matching `if'\n"));
1230 rv = 1;
1231 } else {
1232 _cond_stack = csp->c_outer;
1233 free(csp);
1234 rv = 0;
1236 NYD_LEAVE;
1237 return rv;
1240 FL bool_t
1241 condstack_isskip(void)
1243 bool_t rv;
1244 NYD_ENTER;
1246 rv = (_cond_stack != NULL && (_cond_stack->c_noop || !_cond_stack->c_go));
1247 NYD_LEAVE;
1248 return rv;
1251 FL void *
1252 condstack_release(void)
1254 void *rv;
1255 NYD_ENTER;
1257 rv = _cond_stack;
1258 _cond_stack = NULL;
1259 NYD_LEAVE;
1260 return rv;
1263 FL bool_t
1264 condstack_take(void *self)
1266 struct cond_stack *csp;
1267 bool_t rv;
1268 NYD_ENTER;
1270 if (!(rv = ((csp = _cond_stack) == NULL)))
1271 do {
1272 _cond_stack = csp->c_outer;
1273 free(csp);
1274 } while ((csp = _cond_stack) != NULL);
1276 _cond_stack = self;
1277 NYD_LEAVE;
1278 return rv;
1281 FL int
1282 c_alternates(void *v)
1284 size_t l;
1285 char **namelist = v, **ap, **ap2, *cp;
1286 NYD_ENTER;
1288 l = argcount(namelist) + 1;
1289 if (l == 1) {
1290 if (altnames == NULL)
1291 goto jleave;
1292 for (ap = altnames; *ap != NULL; ++ap)
1293 printf("%s ", *ap);
1294 printf("\n");
1295 goto jleave;
1298 if (altnames != NULL) {
1299 for (ap = altnames; *ap != NULL; ++ap)
1300 free(*ap);
1301 free(altnames);
1303 altnames = smalloc(l * sizeof(char*));
1304 for (ap = namelist, ap2 = altnames; *ap != NULL; ++ap, ++ap2) {
1305 l = strlen(*ap) + 1;
1306 cp = smalloc(l);
1307 memcpy(cp, *ap, l);
1308 *ap2 = cp;
1310 *ap2 = NULL;
1311 jleave:
1312 NYD_LEAVE;
1313 return 0;
1316 FL int
1317 c_newmail(void *v)
1319 int val = 1, mdot;
1320 NYD_ENTER;
1321 UNUSED(v);
1323 if (
1324 #ifdef HAVE_IMAP
1325 (mb.mb_type != MB_IMAP || imap_newmail(1)) &&
1326 #endif
1327 (val = setfile(mailname, FEDIT_NEWMAIL)) == 0) {
1328 mdot = getmdot(1);
1329 setdot(message + mdot - 1);
1331 NYD_LEAVE;
1332 return val;
1335 FL int
1336 c_shortcut(void *v)
1338 char **args = v;
1339 struct shortcut *s;
1340 int rv;
1341 NYD_ENTER;
1343 if (args[0] == NULL) {
1344 list_shortcuts();
1345 rv = 0;
1346 goto jleave;
1349 rv = 1;
1350 if (args[1] == NULL) {
1351 fprintf(stderr, _("expansion name for shortcut missing\n"));
1352 goto jleave;
1354 if (args[2] != NULL) {
1355 fprintf(stderr, _("too many arguments\n"));
1356 goto jleave;
1359 if ((s = get_shortcut(args[0])) != NULL) {
1360 free(s->sh_long);
1361 s->sh_long = sstrdup(args[1]);
1362 } else {
1363 s = scalloc(1, sizeof *s);
1364 s->sh_short = sstrdup(args[0]);
1365 s->sh_long = sstrdup(args[1]);
1366 s->sh_next = shortcuts;
1367 shortcuts = s;
1369 rv = 0;
1370 jleave:
1371 NYD_LEAVE;
1372 return rv;
1375 FL struct shortcut *
1376 get_shortcut(char const *str)
1378 struct shortcut *s;
1379 NYD_ENTER;
1381 for (s = shortcuts; s != NULL; s = s->sh_next)
1382 if (!strcmp(str, s->sh_short))
1383 break;
1384 NYD_LEAVE;
1385 return s;
1388 FL int
1389 c_unshortcut(void *v)
1391 char **args = v;
1392 bool_t errs = FAL0;
1393 NYD_ENTER;
1395 if (args[0] == NULL) {
1396 fprintf(stderr, _("need shortcut names to remove\n"));
1397 errs = TRU1;
1398 goto jleave;
1401 while (*args != NULL) {
1402 if (delete_shortcut(*args) != OKAY) {
1403 errs = TRU1;
1404 fprintf(stderr, _("%s: no such shortcut\n"), *args);
1406 ++args;
1408 jleave:
1409 NYD_LEAVE;
1410 return errs;
1413 FL int
1414 c_flag(void *v)
1416 struct message *m;
1417 int *msgvec = v, *ip;
1418 NYD_ENTER;
1420 for (ip = msgvec; *ip != 0; ++ip) {
1421 m = message + *ip - 1;
1422 setdot(m);
1423 if (!(m->m_flag & (MFLAG | MFLAGGED)))
1424 m->m_flag |= MFLAG | MFLAGGED;
1426 NYD_LEAVE;
1427 return 0;
1430 FL int
1431 c_unflag(void *v)
1433 struct message *m;
1434 int *msgvec = v, *ip;
1435 NYD_ENTER;
1437 for (ip = msgvec; *ip != 0; ++ip) {
1438 m = message + *ip - 1;
1439 setdot(m);
1440 if (m->m_flag & (MFLAG | MFLAGGED)) {
1441 m->m_flag &= ~(MFLAG | MFLAGGED);
1442 m->m_flag |= MUNFLAG;
1445 NYD_LEAVE;
1446 return 0;
1449 FL int
1450 c_answered(void *v)
1452 struct message *m;
1453 int *msgvec = v, *ip;
1454 NYD_ENTER;
1456 for (ip = msgvec; *ip != 0; ++ip) {
1457 m = message + *ip - 1;
1458 setdot(m);
1459 if (!(m->m_flag & (MANSWER | MANSWERED)))
1460 m->m_flag |= MANSWER | MANSWERED;
1462 NYD_LEAVE;
1463 return 0;
1466 FL int
1467 c_unanswered(void *v)
1469 struct message *m;
1470 int *msgvec = v, *ip;
1471 NYD_ENTER;
1473 for (ip = msgvec; *ip != 0; ++ip) {
1474 m = message + *ip - 1;
1475 setdot(m);
1476 if (m->m_flag & (MANSWER | MANSWERED)) {
1477 m->m_flag &= ~(MANSWER | MANSWERED);
1478 m->m_flag |= MUNANSWER;
1481 NYD_LEAVE;
1482 return 0;
1485 FL int
1486 c_draft(void *v)
1488 struct message *m;
1489 int *msgvec = v, *ip;
1490 NYD_ENTER;
1492 for (ip = msgvec; *ip != 0; ++ip) {
1493 m = message + *ip - 1;
1494 setdot(m);
1495 if (!(m->m_flag & (MDRAFT | MDRAFTED)))
1496 m->m_flag |= MDRAFT | MDRAFTED;
1498 NYD_LEAVE;
1499 return 0;
1502 FL int
1503 c_undraft(void *v)
1505 struct message *m;
1506 int *msgvec = v, *ip;
1507 NYD_ENTER;
1509 for (ip = msgvec; *ip != 0; ++ip) {
1510 m = message + *ip - 1;
1511 setdot(m);
1512 if (m->m_flag & (MDRAFT | MDRAFTED)) {
1513 m->m_flag &= ~(MDRAFT | MDRAFTED);
1514 m->m_flag |= MUNDRAFT;
1517 NYD_LEAVE;
1518 return 0;
1521 FL int
1522 c_noop(void *v)
1524 int rv = 0;
1525 NYD_ENTER;
1526 UNUSED(v);
1528 switch (mb.mb_type) {
1529 case MB_IMAP:
1530 #ifdef HAVE_IMAP
1531 imap_noop();
1532 #else
1533 rv = c_cmdnotsupp(NULL);
1534 #endif
1535 break;
1536 case MB_POP3:
1537 #ifdef HAVE_POP3
1538 pop3_noop();
1539 #else
1540 rv = c_cmdnotsupp(NULL);
1541 #endif
1542 break;
1543 default:
1544 break;
1546 NYD_LEAVE;
1547 return rv;
1550 FL int
1551 c_remove(void *v)
1553 char const *fmt;
1554 size_t fmt_len;
1555 char **args = v, *name;
1556 int ec = 0;
1557 NYD_ENTER;
1559 if (*args == NULL) {
1560 fprintf(stderr, _("Syntax is: remove mailbox ...\n"));
1561 ec = 1;
1562 goto jleave;
1565 fmt = _("Remove \"%s\" (y/n) ? ");
1566 fmt_len = strlen(fmt);
1567 do {
1568 if ((name = expand(*args)) == NULL)
1569 continue;
1571 if (!strcmp(name, mailname)) {
1572 fprintf(stderr, _("Cannot remove current mailbox \"%s\".\n"),
1573 name);
1574 ec |= 1;
1575 continue;
1578 size_t vl = strlen(name) + fmt_len +1;
1579 char *vb = ac_alloc(vl);
1580 bool_t asw;
1581 snprintf(vb, vl, fmt, name);
1582 asw = getapproval(vb, TRU1);
1583 ac_free(vb);
1584 if (!asw)
1585 continue;
1588 switch (which_protocol(name)) {
1589 case PROTO_FILE:
1590 if (unlink(name) == -1) { /* TODO do not handle .gz .bz2 .xz.. */
1591 perror(name);
1592 ec |= 1;
1594 break;
1595 case PROTO_POP3:
1596 fprintf(stderr, _("Cannot remove POP3 mailbox \"%s\".\n"),name);
1597 ec |= 1;
1598 break;
1599 case PROTO_IMAP:
1600 #ifdef HAVE_IMAP
1601 if (imap_remove(name) != OKAY)
1602 #endif
1603 ec |= 1;
1604 break;
1605 case PROTO_MAILDIR:
1606 if (maildir_remove(name) != OKAY)
1607 ec |= 1;
1608 break;
1609 case PROTO_UNKNOWN:
1610 fprintf(stderr, _("Unknown protocol in \"%s\". Not removed.\n"),
1611 name);
1612 ec |= 1;
1613 break;
1615 } while (*++args != NULL);
1616 jleave:
1617 NYD_LEAVE;
1618 return ec;
1621 FL int
1622 c_rename(void *v)
1624 char **args = v, *old, *new;
1625 enum protocol oldp, newp;
1626 int ec;
1627 NYD_ENTER;
1629 ec = 1;
1631 if (args[0] == NULL || args[1] == NULL || args[2] != NULL) {
1632 fprintf(stderr, "Syntax: rename old new\n");
1633 goto jleave;
1636 if ((old = expand(args[0])) == NULL)
1637 goto jleave;
1638 oldp = which_protocol(old);
1639 if ((new = expand(args[1])) == NULL)
1640 goto jleave;
1641 newp = which_protocol(new);
1643 if (!strcmp(old, mailname) || !strcmp(new, mailname)) {
1644 fprintf(stderr, _("Cannot rename current mailbox \"%s\".\n"), old);
1645 goto jleave;
1647 if ((oldp == PROTO_IMAP || newp == PROTO_IMAP) && oldp != newp) {
1648 fprintf(stderr, _("Can only rename folders of same type.\n"));
1649 goto jleave;
1652 ec = 0;
1654 if (newp == PROTO_POP3)
1655 goto jnopop3;
1656 switch (oldp) {
1657 case PROTO_FILE:
1658 if (link(old, new) == -1) {
1659 switch (errno) {
1660 case EACCES:
1661 case EEXIST:
1662 case ENAMETOOLONG:
1663 case ENOENT:
1664 case ENOSPC:
1665 case EXDEV:
1666 perror(new);
1667 break;
1668 default:
1669 perror(old);
1671 ec |= 1;
1672 } else if (unlink(old) == -1) {
1673 perror(old);
1674 ec |= 1;
1676 break;
1677 case PROTO_MAILDIR:
1678 if (rename(old, new) == -1) {
1679 perror(old);
1680 ec |= 1;
1682 break;
1683 case PROTO_POP3:
1684 jnopop3:
1685 fprintf(stderr, _("Cannot rename POP3 mailboxes.\n"));
1686 ec |= 1;
1687 break;
1688 #ifdef HAVE_IMAP
1689 case PROTO_IMAP:
1690 if (imap_rename(old, new) != OKAY)
1691 ec |= 1;
1692 break;
1693 #endif
1694 case PROTO_UNKNOWN:
1695 default:
1696 fprintf(stderr, _(
1697 "Unknown protocol in \"%s\" and \"%s\". Not renamed.\n"), old, new);
1698 ec |= 1;
1699 break;
1701 jleave:
1702 NYD_LEAVE;
1703 return ec;
1706 FL int
1707 c_urlencode(void *v) /* XXX IDNA?? */
1709 char **ap;
1710 NYD_ENTER;
1712 for (ap = v; *ap != NULL; ++ap) {
1713 char *in = *ap, *out = urlxenc(in, FAL0);
1715 printf(" in: <%s> (%" ZFMT " bytes)\nout: <%s> (%" ZFMT " bytes)\n",
1716 in, strlen(in), out, strlen(out));
1718 NYD_LEAVE;
1719 return 0;
1722 FL int
1723 c_urldecode(void *v) /* XXX IDNA?? */
1725 char **ap;
1726 NYD_ENTER;
1728 for (ap = v; *ap != NULL; ++ap) {
1729 char *in = *ap, *out = urlxdec(in);
1731 printf(" in: <%s> (%" ZFMT " bytes)\nout: <%s> (%" ZFMT " bytes)\n",
1732 in, strlen(in), out, strlen(out));
1734 NYD_LEAVE;
1735 return 0;
1738 /* s-it-mode */