Simplify ccred_lookup()..
[s-mailx.git] / cmd3.c
blob2eac0a38e018b4e2c50879d4349d83623dd8d675
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, 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 j_leave;
110 in.s = subj;
111 in.l = strlen(subj);
112 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
114 /* TODO _reedit: should be localizable (see cmd1.c:__subject_trim()!) */
115 if ((out.s[0] == 'r' || out.s[0] == 'R') &&
116 (out.s[1] == 'e' || out.s[1] == 'E') && out.s[2] == ':') {
117 newsubj = savestr(out.s);
118 goto jleave;
120 newsubj = salloc(out.l + 4 +1);
121 sstpcpy(sstpcpy(newsubj, "Re: "), out.s);
122 jleave:
123 free(out.s);
124 j_leave:
125 NYD_LEAVE;
126 return newsubj;
129 static void
130 _bangexp(char **str, size_t *size)
132 char *bangbuf;
133 int changed = 0;
134 bool_t dobang;
135 size_t sz, i, j, bangbufsize;
136 NYD_ENTER;
138 dobang = ok_blook(bang);
140 bangbuf = smalloc(bangbufsize = *size);
141 i = j = 0;
142 while ((*str)[i]) {
143 if (dobang) {
144 if ((*str)[i] == '!') {
145 sz = strlen(_bang_buf);
146 bangbuf = srealloc(bangbuf, bangbufsize += sz);
147 ++changed;
148 memcpy(bangbuf + j, _bang_buf, sz + 1);
149 j += sz;
150 i++;
151 continue;
154 if ((*str)[i] == '\\' && (*str)[i + 1] == '!') {
155 bangbuf[j++] = '!';
156 i += 2;
157 ++changed;
159 bangbuf[j++] = (*str)[i++];
161 bangbuf[j] = '\0';
162 if (changed) {
163 printf("!%s\n", bangbuf);
164 fflush(stdout);
166 sz = j + 1;
167 if (sz > *size)
168 *str = srealloc(*str, *size = sz);
169 memcpy(*str, bangbuf, sz);
170 if (sz > _bang_size)
171 _bang_buf = srealloc(_bang_buf, _bang_size = sz);
172 memcpy(_bang_buf, bangbuf, sz);
173 free(bangbuf);
174 NYD_LEAVE;
177 static void
178 make_ref_and_cs(struct message *mp, struct header *head)
180 char *oldref, *oldmsgid, *newref, *cp;
181 size_t oldreflen = 0, oldmsgidlen = 0, reflen;
182 unsigned i;
183 struct name *n;
184 NYD_ENTER;
186 oldref = hfield1("references", mp);
187 oldmsgid = hfield1("message-id", mp);
188 if (oldmsgid == NULL || *oldmsgid == '\0') {
189 head->h_ref = NULL;
190 goto jleave;
193 reflen = 1;
194 if (oldref) {
195 oldreflen = strlen(oldref);
196 reflen += oldreflen + 2;
198 if (oldmsgid) {
199 oldmsgidlen = strlen(oldmsgid);
200 reflen += oldmsgidlen;
203 newref = ac_alloc(reflen);
204 if (oldref != NULL) {
205 memcpy(newref, oldref, oldreflen +1);
206 if (oldmsgid != NULL) {
207 newref[oldreflen++] = ',';
208 newref[oldreflen++] = ' ';
209 memcpy(newref + oldreflen, oldmsgid, oldmsgidlen +1);
211 } else if (oldmsgid)
212 memcpy(newref, oldmsgid, oldmsgidlen +1);
213 n = extract(newref, GREF);
214 ac_free(newref);
216 /* Limit number of references */
217 while (n->n_flink != NULL)
218 n = n->n_flink;
219 for (i = 1; i < 21; ++i) { /* XXX no magics */
220 if (n->n_blink != NULL)
221 n = n->n_blink;
222 else
223 break;
225 n->n_blink = NULL;
226 head->h_ref = n;
227 if (ok_blook(reply_in_same_charset) &&
228 (cp = hfield1("content-type", mp)) != NULL)
229 head->h_charset = mime_getparam("charset", cp);
230 jleave:
231 NYD_LEAVE;
234 static int
235 (*respond_or_Respond(int c))(int *, int)
237 int opt;
238 int (*rv)(int*, int);
239 NYD_ENTER;
241 opt = ok_blook(Replyall);
242 opt += ok_blook(flipr);
243 rv = ((opt == 1) ^ (c == 'R')) ? &Respond_internal : &respond_internal;
244 NYD_LEAVE;
245 return rv;
248 static int
249 respond_internal(int *msgvec, int recipient_record)
251 struct header head;
252 struct message *mp;
253 char *cp, *rcv;
254 struct name *np = NULL;
255 enum gfield gf;
256 int rv = 1;
257 NYD_ENTER;
259 gf = ok_blook(fullnames) ? GFULL : GSKIN;
261 if (msgvec[1] != 0) {
262 fprintf(stderr, _(
263 "Sorry, can't reply to multiple messages at once\n"));
264 goto jleave;
267 mp = message + msgvec[0] - 1;
268 touch(mp);
269 setdot(mp);
271 if ((rcv = hfield1("reply-to", mp)) == NULL)
272 if ((rcv = hfield1("from", mp)) == NULL)
273 rcv = nameof(mp, 1);
274 if (rcv != NULL)
275 np = lextract(rcv, GTO | gf);
276 if (!ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
277 np = cat(np, lextract(cp, GTO | gf));
278 /* Delete my name from reply list, and with it, all my alternate names */
279 np = elide(delete_alternates(np));
280 if (np == NULL)
281 np = lextract(rcv, GTO | gf);
283 memset(&head, 0, sizeof head);
284 head.h_to = np;
285 head.h_subject = hfield1("subject", mp);
286 head.h_subject = _reedit(head.h_subject);
288 /* Cc: */
289 np = NULL;
290 if (ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
291 np = lextract(cp, GCC | gf);
292 if ((cp = hfield1("cc", mp)) != NULL)
293 np = cat(np, lextract(cp, GCC | gf));
294 if (np != NULL)
295 head.h_cc = elide(delete_alternates(np));
296 make_ref_and_cs(mp, &head);
298 if (ok_blook(quote_as_attachment)) {
299 head.h_attach = csalloc(1, sizeof *head.h_attach);
300 head.h_attach->a_msgno = *msgvec;
301 head.h_attach->a_content_description = _(
302 "Original message content");
305 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
306 ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
307 mp->m_flag |= MANSWER | MANSWERED;
308 rv = 0;
309 jleave:
310 NYD_LEAVE;
311 return rv;
314 static int
315 Respond_internal(int *msgvec, int recipient_record)
317 struct header head;
318 struct message *mp;
319 int *ap;
320 char *cp;
321 enum gfield gf;
322 NYD_ENTER;
324 memset(&head, 0, sizeof head);
325 gf = ok_blook(fullnames) ? GFULL : GSKIN;
327 for (ap = msgvec; *ap != 0; ++ap) {
328 mp = message + *ap - 1;
329 touch(mp);
330 setdot(mp);
331 if ((cp = hfield1("reply-to", mp)) == NULL)
332 if ((cp = hfield1("from", mp)) == NULL)
333 cp = nameof(mp, 2);
334 head.h_to = cat(head.h_to, lextract(cp, GTO | gf));
336 if (head.h_to == NULL)
337 goto jleave;
339 mp = message + msgvec[0] - 1;
340 head.h_subject = hfield1("subject", mp);
341 head.h_subject = _reedit(head.h_subject);
342 make_ref_and_cs(mp, &head);
344 if (ok_blook(quote_as_attachment)) {
345 head.h_attach = csalloc(1, sizeof *head.h_attach);
346 head.h_attach->a_msgno = *msgvec;
347 head.h_attach->a_content_description = _(
348 "Original message content");
351 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
352 ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
353 mp->m_flag |= MANSWER | MANSWERED;
354 jleave:
355 NYD_LEAVE;
356 return 0;
359 static int
360 forward1(char *str, int recipient_record)
362 struct header head;
363 int *msgvec, rv = 1;
364 char *recipient;
365 struct message *mp;
366 bool_t f, forward_as_attachment;
367 NYD_ENTER;
369 if ((recipient = laststring(str, &f, 0)) == NULL) {
370 puts(_("No recipient specified."));
371 goto jleave;
374 forward_as_attachment = ok_blook(forward_as_attachment);
375 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
377 if (!f) {
378 *msgvec = first(0, MMNORM);
379 if (*msgvec == 0) {
380 if (inhook) {
381 rv = 0;
382 goto jleave;
384 printf("No messages to forward.\n");
385 goto jleave;
387 msgvec[1] = 0;
388 } else if (getmsglist(str, msgvec, 0) < 0)
389 goto jleave;
391 if (*msgvec == 0) {
392 if (inhook) {
393 rv = 0;
394 goto jleave;
396 printf("No applicable messages.\n");
397 goto jleave;
399 if (msgvec[1] != 0) {
400 printf("Cannot forward multiple messages at once\n");
401 goto jleave;
404 memset(&head, 0, sizeof head);
405 if ((head.h_to = lextract(recipient,
406 (GTO | (ok_blook(fullnames) ? GFULL : GSKIN)))) == NULL)
407 goto jleave;
409 mp = message + *msgvec - 1;
411 if (forward_as_attachment) {
412 head.h_attach = csalloc(1, sizeof *head.h_attach);
413 head.h_attach->a_msgno = *msgvec;
414 head.h_attach->a_content_description = "Forwarded message";
415 } else {
416 touch(mp);
417 setdot(mp);
419 head.h_subject = hfield1("subject", mp);
420 head.h_subject = fwdedit(head.h_subject);
421 mail1(&head, 1, (forward_as_attachment ? NULL : mp), NULL, recipient_record,
423 rv = 0;
424 jleave:
425 NYD_LEAVE;
426 return rv;
429 static char *
430 fwdedit(char *subj)
432 struct str in, out;
433 char *newsubj = NULL;
434 NYD_ENTER;
436 if (subj == NULL || *subj == '\0')
437 goto jleave;
439 in.s = subj;
440 in.l = strlen(subj);
441 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
443 newsubj = salloc(out.l + 6);
444 memcpy(newsubj, "Fwd: ", 5); /* XXX localizable */
445 memcpy(newsubj + 5, out.s, out.l + 1);
446 free(out.s);
447 jleave:
448 NYD_LEAVE;
449 return newsubj;
452 static void
453 asort(char **list)
455 char **ap;
456 size_t i;
457 NYD_ENTER;
459 for (ap = list; *ap != NULL; ++ap)
461 if ((i = PTR2SIZE(ap - list)) >= 2)
462 qsort(list, i, sizeof *list, diction);
463 NYD_LEAVE;
466 static int
467 diction(void const *a, void const *b)
469 int rv;
470 NYD_ENTER;
472 rv = strcmp(*(char**)UNCONST(a), *(char**)UNCONST(b));
473 NYD_LEAVE;
474 return rv;
477 static int
478 _resend1(void *v, bool_t add_resent)
480 char *name, *str;
481 struct name *to, *sn;
482 int *ip, *msgvec;
483 bool_t f = TRU1;
484 NYD_ENTER;
486 str = v;
487 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
488 name = laststring(str, &f, 1);
489 if (name == NULL) {
490 puts(_("No recipient specified."));
491 goto jleave;
494 if (!f) {
495 *msgvec = first(0, MMNORM);
496 if (*msgvec == 0) {
497 if (inhook) {
498 f = FAL0;
499 goto jleave;
501 puts(_("No applicable messages."));
502 goto jleave;
504 msgvec[1] = 0;
505 } else if (getmsglist(str, msgvec, 0) < 0)
506 goto jleave;
508 if (*msgvec == 0) {
509 if (inhook) {
510 f = FAL0;
511 goto jleave;
513 printf("No applicable messages.\n");
514 goto jleave;
517 sn = nalloc(name, GTO | GSKIN);
518 to = usermap(sn, FAL0);
519 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
520 ++ip)
521 if (resend_msg(message + *ip - 1, to, add_resent) != OKAY)
522 goto jleave;
523 f = FAL0;
524 jleave:
525 NYD_LEAVE;
526 return (f != FAL0);
529 static int
530 _c_file(void *v, enum fedit_mode fm)
532 char **argv = v;
533 int i;
534 NYD2_ENTER;
536 if (*argv == NULL) {
537 newfileinfo();
538 i = 0;
539 goto jleave;
542 if (inhook) {
543 fprintf(stderr, _("Cannot change folder from within a hook.\n"));
544 i = 1;
545 goto jleave;
548 save_mbox_for_possible_quitstuff();
550 i = setfile(*argv, fm);
551 if (i < 0) {
552 i = 1;
553 goto jleave;
555 callhook(mailname, 0);
556 if (i > 0 && !ok_blook(emptystart)) {
557 i = 1;
558 goto jleave;
560 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
561 i = 0;
562 jleave:
563 NYD2_LEAVE;
564 return i;
567 static void
568 list_shortcuts(void)
570 struct shortcut *s;
571 NYD_ENTER;
573 for (s = shortcuts; s != NULL; s = s->sh_next)
574 printf("%s=%s\n", s->sh_short, s->sh_long);
575 NYD_LEAVE;
578 static enum okay
579 delete_shortcut(char const *str)
581 struct shortcut *sp, *sq;
582 enum okay rv = STOP;
583 NYD_ENTER;
585 for (sp = shortcuts, sq = NULL; sp != NULL; sq = sp, sp = sp->sh_next) {
586 if (!strcmp(sp->sh_short, str)) {
587 free(sp->sh_short);
588 free(sp->sh_long);
589 if (sq != NULL)
590 sq->sh_next = sp->sh_next;
591 if (sp == shortcuts)
592 shortcuts = sp->sh_next;
593 free(sp);
594 rv = OKAY;
595 break;
598 NYD_LEAVE;
599 return rv;
602 FL int
603 c_shell(void *v)
605 char const *sh = NULL;
606 char *str = v, *cmd;
607 size_t cmdsize;
608 sigset_t mask;
609 sighandler_type sigint;
610 NYD_ENTER;
612 cmd = smalloc(cmdsize = strlen(str) +1);
613 memcpy(cmd, str, cmdsize);
614 _bangexp(&cmd, &cmdsize);
615 if ((sh = ok_vlook(SHELL)) == NULL)
616 sh = XSHELL;
618 sigint = safe_signal(SIGINT, SIG_IGN);
619 sigemptyset(&mask);
620 run_command(sh, &mask, -1, -1, "-c", cmd, NULL);
621 safe_signal(SIGINT, sigint);
622 printf("!\n");
624 free(cmd);
625 NYD_LEAVE;
626 return 0;
629 FL int
630 c_dosh(void *v)
632 sighandler_type sigint;
633 char const *sh;
634 NYD_ENTER;
635 UNUSED(v);
637 if ((sh = ok_vlook(SHELL)) == NULL)
638 sh = XSHELL;
640 sigint = safe_signal(SIGINT, SIG_IGN);
641 run_command(sh, 0, -1, -1, NULL, NULL, NULL);
642 safe_signal(SIGINT, sigint);
643 putchar('\n');
644 NYD_LEAVE;
645 return 0;
648 FL int
649 c_help(void *v)
651 int ret = 0;
652 char *arg;
653 NYD_ENTER;
655 arg = *(char**)v;
657 if (arg != NULL) {
658 #ifdef HAVE_DOCSTRINGS
659 ret = !print_comm_docstr(arg);
660 if (ret)
661 fprintf(stderr, _("Unknown command: `%s'\n"), arg);
662 #else
663 ret = c_cmdnotsupp(NULL);
664 #endif
665 goto jleave;
668 /* Very ugly, but take care for compiler supported string lengths :( */
669 printf(_("%s commands:\n"), progname);
670 puts(_(
671 "type <message list> type messages\n"
672 "next goto and type next message\n"
673 "from <message list> give head lines of messages\n"
674 "headers print out active message headers\n"
675 "delete <message list> delete messages\n"
676 "undelete <message list> undelete messages\n"));
677 puts(_(
678 "save <message list> folder append messages to folder and mark as saved\n"
679 "copy <message list> folder append messages to folder without marking them\n"
680 "write <message list> file append message texts to file, save attachments\n"
681 "preserve <message list> keep incoming messages in mailbox even if saved\n"
682 "Reply <message list> reply to message senders\n"
683 "reply <message list> reply to message senders and all recipients\n"));
684 puts(_(
685 "mail addresses mail to specific recipients\n"
686 "file folder change to another folder\n"
687 "quit quit and apply changes to folder\n"
688 "xit quit and discard changes made to folder\n"
689 "! shell escape\n"
690 "cd <directory> chdir to directory or home if none given\n"
691 "list list names of all available commands\n"));
692 printf(_(
693 "\nA <message list> consists of integers, ranges of same, or other criteria\n"
694 "separated by spaces. If omitted, %s uses the last message typed.\n"),
695 progname);
697 jleave:
698 NYD_LEAVE;
699 return ret;
702 FL int
703 c_cwd(void *v)
705 char buf[PATH_MAX]; /* TODO getcwd(3) may return a larger value */
706 NYD_ENTER;
708 if (getcwd(buf, sizeof buf) != NULL) {
709 puts(buf);
710 v = (void*)0x1;
711 } else {
712 perror("getcwd");
713 v = NULL;
715 NYD_LEAVE;
716 return (v == NULL);
719 FL int
720 c_chdir(void *v)
722 char **arglist = v;
723 char const *cp;
724 NYD_ENTER;
726 if (*arglist == NULL)
727 cp = homedir;
728 else if ((cp = file_expand(*arglist)) == NULL)
729 goto jleave;
730 if (chdir(cp) == -1) {
731 perror(cp);
732 cp = NULL;
734 jleave:
735 NYD_LEAVE;
736 return (cp == NULL);
739 FL int
740 c_respond(void *v)
742 int rv;
743 NYD_ENTER;
745 rv = (*respond_or_Respond('r'))(v, 0);
746 NYD_LEAVE;
747 return rv;
750 FL int
751 c_respondall(void *v)
753 int rv;
754 NYD_ENTER;
756 rv = respond_internal(v, 0);
757 NYD_LEAVE;
758 return rv;
761 FL int
762 c_respondsender(void *v)
764 int rv;
765 NYD_ENTER;
767 rv = Respond_internal(v, 0);
768 NYD_LEAVE;
769 return rv;
772 FL int
773 c_Respond(void *v)
775 int rv;
776 NYD_ENTER;
778 rv = (*respond_or_Respond('R'))(v, 0);
779 NYD_LEAVE;
780 return rv;
783 FL int
784 c_followup(void *v)
786 int rv;
787 NYD_ENTER;
789 rv = (*respond_or_Respond('r'))(v, 1);
790 NYD_LEAVE;
791 return rv;
794 FL int
795 c_followupall(void *v)
797 int rv;
798 NYD_ENTER;
800 rv = respond_internal(v, 1);
801 NYD_LEAVE;
802 return rv;
805 FL int
806 c_followupsender(void *v)
808 int rv;
809 NYD_ENTER;
811 rv = Respond_internal(v, 1);
812 NYD_LEAVE;
813 return rv;
816 FL int
817 c_Followup(void *v)
819 int rv;
820 NYD_ENTER;
822 rv = (*respond_or_Respond('R'))(v, 1);
823 NYD_LEAVE;
824 return rv;
827 FL int
828 c_forward(void *v)
830 int rv;
831 NYD_ENTER;
833 rv = forward1(v, 0);
834 NYD_LEAVE;
835 return rv;
838 FL int
839 c_Forward(void *v)
841 int rv;
842 NYD_ENTER;
844 rv = forward1(v, 1);
845 NYD_LEAVE;
846 return rv;
849 FL int
850 c_resend(void *v)
852 int rv;
853 NYD_ENTER;
855 rv = _resend1(v, TRU1);
856 NYD_LEAVE;
857 return rv;
860 FL int
861 c_Resend(void *v)
863 int rv;
864 NYD_ENTER;
866 rv = _resend1(v, FAL0);
867 NYD_LEAVE;
868 return rv;
871 FL int
872 c_preserve(void *v)
874 int *msgvec = v, *ip, mesg, rv = 1;
875 struct message *mp;
876 NYD_ENTER;
878 if (edit) {
879 printf(_("Cannot \"preserve\" in edit mode\n"));
880 goto jleave;
883 for (ip = msgvec; *ip != 0; ++ip) {
884 mesg = *ip;
885 mp = message + mesg - 1;
886 mp->m_flag |= MPRESERVE;
887 mp->m_flag &= ~MBOX;
888 setdot(mp);
889 did_print_dot = TRU1;
891 rv = 0;
892 jleave:
893 NYD_LEAVE;
894 return rv;
897 FL int
898 c_unread(void *v)
900 int *msgvec = v, *ip;
901 NYD_ENTER;
903 for (ip = msgvec; *ip != 0; ++ip) {
904 setdot(message + *ip - 1);
905 dot->m_flag &= ~(MREAD | MTOUCH);
906 dot->m_flag |= MSTATUS;
907 #ifdef HAVE_IMAP
908 if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
909 imap_unread(message + *ip - 1, *ip); /* TODO return? */
910 #endif
911 did_print_dot = TRU1;
913 NYD_LEAVE;
914 return 0;
917 FL int
918 c_seen(void *v)
920 int *msgvec = v, *ip;
921 NYD_ENTER;
923 for (ip = msgvec; *ip != 0; ++ip) {
924 struct message *mp = message + *ip - 1;
925 setdot(mp);
926 touch(mp);
928 NYD_LEAVE;
929 return 0;
932 FL int
933 c_messize(void *v)
935 int *msgvec = v, *ip, mesg;
936 struct message *mp;
937 NYD_ENTER;
939 for (ip = msgvec; *ip != 0; ++ip) {
940 mesg = *ip;
941 mp = message + mesg - 1;
942 printf("%d: ", mesg);
943 if (mp->m_xlines > 0)
944 printf("%ld", mp->m_xlines);
945 else
946 putchar(' ');
947 printf("/%lu\n", (ul_it)mp->m_xsize);
949 NYD_LEAVE;
950 return 0;
953 FL int
954 c_rexit(void *v)
956 UNUSED(v);
957 NYD_ENTER;
959 if (!sourcing)
960 exit(0);
961 NYD_LEAVE;
962 return 1;
965 FL int
966 c_group(void *v)
968 char **argv = v, **ap, *gname, **p;
969 struct grouphead *gh;
970 struct group *gp;
971 int h, s;
972 NYD_ENTER;
974 if (*argv == NULL) {
975 for (h = 0, s = 1; h < HSHSIZE; ++h)
976 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
977 ++s;
978 ap = salloc(s * sizeof *ap);
980 for (h = 0, p = ap; h < HSHSIZE; ++h)
981 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
982 *p++ = gh->g_name;
983 *p = NULL;
985 asort(ap);
987 for (p = ap; *p != NULL; ++p)
988 printgroup(*p);
989 goto jleave;
992 if (argv[1] == NULL) {
993 printgroup(*argv);
994 goto jleave;
997 gname = *argv;
998 h = hash(gname);
999 if ((gh = findgroup(gname)) == NULL) {
1000 gh = scalloc(1, sizeof *gh);
1001 gh->g_name = sstrdup(gname);
1002 gh->g_list = NULL;
1003 gh->g_link = groups[h];
1004 groups[h] = gh;
1007 /* Insert names from the command list into the group. Who cares if there
1008 * are duplicates? They get tossed later anyway */
1009 for (ap = argv + 1; *ap != NULL; ++ap) {
1010 gp = scalloc(1, sizeof *gp);
1011 gp->ge_name = sstrdup(*ap);
1012 gp->ge_link = gh->g_list;
1013 gh->g_list = gp;
1015 jleave:
1016 NYD_LEAVE;
1017 return 0;
1020 FL int
1021 c_ungroup(void *v)
1023 char **argv = v;
1024 int rv = 1;
1025 NYD_ENTER;
1027 if (*argv == NULL) {
1028 fprintf(stderr, _("Must specify alias to remove\n"));
1029 goto jleave;
1033 remove_group(*argv);
1034 while (*++argv != NULL);
1035 rv = 0;
1036 jleave:
1037 NYD_LEAVE;
1038 return rv;
1041 FL int
1042 c_file(void *v)
1044 int rv;
1045 NYD_ENTER;
1047 rv = _c_file(v, FEDIT_NONE);
1048 NYD_LEAVE;
1049 return rv;
1052 FL int
1053 c_File(void *v)
1055 int rv;
1056 NYD_ENTER;
1058 rv = _c_file(v, FEDIT_RDONLY);
1059 NYD_LEAVE;
1060 return rv;
1063 FL int
1064 c_echo(void *v)
1066 char const **argv = v, **ap, *cp;
1067 int c;
1068 NYD_ENTER;
1070 for (ap = argv; *ap != NULL; ++ap) {
1071 cp = *ap;
1072 if ((cp = fexpand(cp, FEXP_NSHORTCUT)) != NULL) {
1073 if (ap != argv)
1074 putchar(' ');
1075 c = 0;
1076 while (*cp != '\0' && (c = expand_shell_escape(&cp, FAL0)) > 0)
1077 putchar(c);
1078 /* \c ends overall processing */
1079 if (c < 0)
1080 goto jleave;
1083 putchar('\n');
1084 jleave:
1085 NYD_LEAVE;
1086 return 0;
1089 FL int
1090 c_if(void *v)
1092 struct cond_stack *csp;
1093 int rv = 1;
1094 char **argv = v, *cp, *op;
1095 NYD_ENTER;
1097 csp = smalloc(sizeof *csp);
1098 csp->c_outer = _cond_stack;
1099 csp->c_noop = condstack_isskip();
1100 csp->c_go = TRU1;
1101 csp->c_else = FAL0;
1102 _cond_stack = csp;
1104 cp = argv[0];
1105 if (*cp != '$' && argv[1] != NULL) {
1106 jesyn:
1107 fprintf(stderr, _("Invalid conditional expression \"%s %s %s\"\n"),
1108 argv[0], (argv[1] != NULL ? argv[1] : ""),
1109 (argv[2] != NULL ? argv[2] : ""));
1110 goto jleave;
1113 switch (*cp) {
1114 case '0':
1115 csp->c_go = FAL0;
1116 break;
1117 case 'R': case 'r':
1118 csp->c_go = !(options & OPT_SENDMODE);
1119 break;
1120 case 'S': case 's':
1121 csp->c_go = ((options & OPT_SENDMODE) != 0);
1122 break;
1123 case 'T': case 't':
1124 csp->c_go = ((options & OPT_TTYIN) != 0);
1125 break;
1126 case '$':
1127 /* Look up the value in question, we need it anyway */
1128 v = vok_vlook(++cp);
1130 /* Single argument, "implicit boolean" form? */
1131 if ((op = argv[1]) == NULL) {
1132 csp->c_go = (v != NULL);
1133 break;
1136 /* Three argument comparison form? */
1137 if (argv[2] == NULL || op[0] == '\0' ||
1138 #ifdef HAVE_REGEX
1139 (op[1] != '=' && op[1] != '~') ||
1140 #else
1141 op[1] != '=' ||
1142 #endif
1143 op[2] != '\0')
1144 goto jesyn;
1146 /* A null value is treated as the empty string */
1147 if (v == NULL)
1148 v = UNCONST("");
1149 #ifdef HAVE_REGEX
1150 if (op[1] == '~') {
1151 regex_t re;
1153 if (regcomp(&re, argv[2], REG_EXTENDED | REG_ICASE | REG_NOSUB))
1154 goto jesyn;
1155 if (regexec(&re, v, 0,NULL, 0) == REG_NOMATCH)
1156 v = NULL;
1157 regfree(&re);
1158 } else
1159 #endif
1160 if (strcmp(v, argv[2]))
1161 v = NULL;
1162 switch (op[0]) {
1163 case '!':
1164 case '=':
1165 csp->c_go = ((op[0] == '=') ^ (v == NULL));
1166 break;
1167 default:
1168 goto jesyn;
1170 break;
1171 default:
1172 fprintf(stderr, _("Unrecognized if-keyword: \"%s\"\n"), cp);
1173 case '1':
1174 csp->c_go = TRU1;
1175 goto jleave;
1177 rv = 0;
1178 jleave:
1179 NYD_LEAVE;
1180 return rv;
1183 FL int
1184 c_elif(void *v)
1186 struct cond_stack *csp;
1187 int rv;
1188 NYD_ENTER;
1190 if ((csp = _cond_stack) == NULL || csp->c_else) {
1191 fprintf(stderr, _("`elif' without matching `if'\n"));
1192 rv = 1;
1193 } else {
1194 csp->c_go = !csp->c_go;
1195 rv = c_if(v);
1196 _cond_stack->c_outer = csp->c_outer;
1197 free(csp);
1199 NYD_LEAVE;
1200 return rv;
1203 FL int
1204 c_else(void *v)
1206 int rv;
1207 NYD_ENTER;
1208 UNUSED(v);
1210 if (_cond_stack == NULL || _cond_stack->c_else) {
1211 fprintf(stderr, _("`else' without matching `if'\n"));
1212 rv = 1;
1213 } else {
1214 _cond_stack->c_go = !_cond_stack->c_go;
1215 _cond_stack->c_else = TRU1;
1216 rv = 0;
1218 NYD_LEAVE;
1219 return rv;
1222 FL int
1223 c_endif(void *v)
1225 struct cond_stack *csp;
1226 int rv;
1227 NYD_ENTER;
1228 UNUSED(v);
1230 if ((csp = _cond_stack) == NULL) {
1231 fprintf(stderr, _("`endif' without matching `if'\n"));
1232 rv = 1;
1233 } else {
1234 _cond_stack = csp->c_outer;
1235 free(csp);
1236 rv = 0;
1238 NYD_LEAVE;
1239 return rv;
1242 FL bool_t
1243 condstack_isskip(void)
1245 bool_t rv;
1246 NYD_ENTER;
1248 rv = (_cond_stack != NULL && (_cond_stack->c_noop || !_cond_stack->c_go));
1249 NYD_LEAVE;
1250 return rv;
1253 FL void *
1254 condstack_release(void)
1256 void *rv;
1257 NYD_ENTER;
1259 rv = _cond_stack;
1260 _cond_stack = NULL;
1261 NYD_LEAVE;
1262 return rv;
1265 FL bool_t
1266 condstack_take(void *self)
1268 struct cond_stack *csp;
1269 bool_t rv;
1270 NYD_ENTER;
1272 if (!(rv = ((csp = _cond_stack) == NULL)))
1273 do {
1274 _cond_stack = csp->c_outer;
1275 free(csp);
1276 } while ((csp = _cond_stack) != NULL);
1278 _cond_stack = self;
1279 NYD_LEAVE;
1280 return rv;
1283 FL int
1284 c_alternates(void *v)
1286 size_t l;
1287 char **namelist = v, **ap, **ap2, *cp;
1288 NYD_ENTER;
1290 l = argcount(namelist) + 1;
1291 if (l == 1) {
1292 if (altnames == NULL)
1293 goto jleave;
1294 for (ap = altnames; *ap != NULL; ++ap)
1295 printf("%s ", *ap);
1296 printf("\n");
1297 goto jleave;
1300 if (altnames != NULL) {
1301 for (ap = altnames; *ap != NULL; ++ap)
1302 free(*ap);
1303 free(altnames);
1305 altnames = smalloc(l * sizeof(char*));
1306 for (ap = namelist, ap2 = altnames; *ap != NULL; ++ap, ++ap2) {
1307 l = strlen(*ap) + 1;
1308 cp = smalloc(l);
1309 memcpy(cp, *ap, l);
1310 *ap2 = cp;
1312 *ap2 = NULL;
1313 jleave:
1314 NYD_LEAVE;
1315 return 0;
1318 FL int
1319 c_newmail(void *v)
1321 int val = 1, mdot;
1322 NYD_ENTER;
1323 UNUSED(v);
1325 if (
1326 #ifdef HAVE_IMAP
1327 (mb.mb_type != MB_IMAP || imap_newmail(1)) &&
1328 #endif
1329 (val = setfile(mailname, FEDIT_NEWMAIL)) == 0) {
1330 mdot = getmdot(1);
1331 setdot(message + mdot - 1);
1333 NYD_LEAVE;
1334 return val;
1337 FL int
1338 c_shortcut(void *v)
1340 char **args = v;
1341 struct shortcut *s;
1342 int rv;
1343 NYD_ENTER;
1345 if (args[0] == NULL) {
1346 list_shortcuts();
1347 rv = 0;
1348 goto jleave;
1351 rv = 1;
1352 if (args[1] == NULL) {
1353 fprintf(stderr, _("expansion name for shortcut missing\n"));
1354 goto jleave;
1356 if (args[2] != NULL) {
1357 fprintf(stderr, _("too many arguments\n"));
1358 goto jleave;
1361 if ((s = get_shortcut(args[0])) != NULL) {
1362 free(s->sh_long);
1363 s->sh_long = sstrdup(args[1]);
1364 } else {
1365 s = scalloc(1, sizeof *s);
1366 s->sh_short = sstrdup(args[0]);
1367 s->sh_long = sstrdup(args[1]);
1368 s->sh_next = shortcuts;
1369 shortcuts = s;
1371 rv = 0;
1372 jleave:
1373 NYD_LEAVE;
1374 return rv;
1377 FL struct shortcut *
1378 get_shortcut(char const *str)
1380 struct shortcut *s;
1381 NYD_ENTER;
1383 for (s = shortcuts; s != NULL; s = s->sh_next)
1384 if (!strcmp(str, s->sh_short))
1385 break;
1386 NYD_LEAVE;
1387 return s;
1390 FL int
1391 c_unshortcut(void *v)
1393 char **args = v;
1394 bool_t errs = FAL0;
1395 NYD_ENTER;
1397 if (args[0] == NULL) {
1398 fprintf(stderr, _("need shortcut names to remove\n"));
1399 errs = TRU1;
1400 goto jleave;
1403 while (*args != NULL) {
1404 if (delete_shortcut(*args) != OKAY) {
1405 errs = TRU1;
1406 fprintf(stderr, _("%s: no such shortcut\n"), *args);
1408 ++args;
1410 jleave:
1411 NYD_LEAVE;
1412 return errs;
1415 FL int
1416 c_flag(void *v)
1418 struct message *m;
1419 int *msgvec = v, *ip;
1420 NYD_ENTER;
1422 for (ip = msgvec; *ip != 0; ++ip) {
1423 m = message + *ip - 1;
1424 setdot(m);
1425 if (!(m->m_flag & (MFLAG | MFLAGGED)))
1426 m->m_flag |= MFLAG | MFLAGGED;
1428 NYD_LEAVE;
1429 return 0;
1432 FL int
1433 c_unflag(void *v)
1435 struct message *m;
1436 int *msgvec = v, *ip;
1437 NYD_ENTER;
1439 for (ip = msgvec; *ip != 0; ++ip) {
1440 m = message + *ip - 1;
1441 setdot(m);
1442 if (m->m_flag & (MFLAG | MFLAGGED)) {
1443 m->m_flag &= ~(MFLAG | MFLAGGED);
1444 m->m_flag |= MUNFLAG;
1447 NYD_LEAVE;
1448 return 0;
1451 FL int
1452 c_answered(void *v)
1454 struct message *m;
1455 int *msgvec = v, *ip;
1456 NYD_ENTER;
1458 for (ip = msgvec; *ip != 0; ++ip) {
1459 m = message + *ip - 1;
1460 setdot(m);
1461 if (!(m->m_flag & (MANSWER | MANSWERED)))
1462 m->m_flag |= MANSWER | MANSWERED;
1464 NYD_LEAVE;
1465 return 0;
1468 FL int
1469 c_unanswered(void *v)
1471 struct message *m;
1472 int *msgvec = v, *ip;
1473 NYD_ENTER;
1475 for (ip = msgvec; *ip != 0; ++ip) {
1476 m = message + *ip - 1;
1477 setdot(m);
1478 if (m->m_flag & (MANSWER | MANSWERED)) {
1479 m->m_flag &= ~(MANSWER | MANSWERED);
1480 m->m_flag |= MUNANSWER;
1483 NYD_LEAVE;
1484 return 0;
1487 FL int
1488 c_draft(void *v)
1490 struct message *m;
1491 int *msgvec = v, *ip;
1492 NYD_ENTER;
1494 for (ip = msgvec; *ip != 0; ++ip) {
1495 m = message + *ip - 1;
1496 setdot(m);
1497 if (!(m->m_flag & (MDRAFT | MDRAFTED)))
1498 m->m_flag |= MDRAFT | MDRAFTED;
1500 NYD_LEAVE;
1501 return 0;
1504 FL int
1505 c_undraft(void *v)
1507 struct message *m;
1508 int *msgvec = v, *ip;
1509 NYD_ENTER;
1511 for (ip = msgvec; *ip != 0; ++ip) {
1512 m = message + *ip - 1;
1513 setdot(m);
1514 if (m->m_flag & (MDRAFT | MDRAFTED)) {
1515 m->m_flag &= ~(MDRAFT | MDRAFTED);
1516 m->m_flag |= MUNDRAFT;
1519 NYD_LEAVE;
1520 return 0;
1523 FL int
1524 c_noop(void *v)
1526 int rv = 0;
1527 NYD_ENTER;
1528 UNUSED(v);
1530 switch (mb.mb_type) {
1531 case MB_IMAP:
1532 #ifdef HAVE_IMAP
1533 imap_noop();
1534 #else
1535 rv = c_cmdnotsupp(NULL);
1536 #endif
1537 break;
1538 case MB_POP3:
1539 #ifdef HAVE_POP3
1540 pop3_noop();
1541 #else
1542 rv = c_cmdnotsupp(NULL);
1543 #endif
1544 break;
1545 default:
1546 break;
1548 NYD_LEAVE;
1549 return rv;
1552 FL int
1553 c_remove(void *v)
1555 char const *fmt;
1556 size_t fmt_len;
1557 char **args = v, *name;
1558 int ec = 0;
1559 NYD_ENTER;
1561 if (*args == NULL) {
1562 fprintf(stderr, _("Syntax is: remove mailbox ...\n"));
1563 ec = 1;
1564 goto jleave;
1567 fmt = _("Remove \"%s\" (y/n) ? ");
1568 fmt_len = strlen(fmt);
1569 do {
1570 if ((name = expand(*args)) == NULL)
1571 continue;
1573 if (!strcmp(name, mailname)) {
1574 fprintf(stderr, _("Cannot remove current mailbox \"%s\".\n"),
1575 name);
1576 ec |= 1;
1577 continue;
1580 size_t vl = strlen(name) + fmt_len +1;
1581 char *vb = ac_alloc(vl);
1582 bool_t asw;
1583 snprintf(vb, vl, fmt, name);
1584 asw = getapproval(vb, TRU1);
1585 ac_free(vb);
1586 if (!asw)
1587 continue;
1590 switch (which_protocol(name)) {
1591 case PROTO_FILE:
1592 if (unlink(name) == -1) { /* TODO do not handle .gz .bz2 .xz.. */
1593 perror(name);
1594 ec |= 1;
1596 break;
1597 case PROTO_POP3:
1598 fprintf(stderr, _("Cannot remove POP3 mailbox \"%s\".\n"),name);
1599 ec |= 1;
1600 break;
1601 case PROTO_IMAP:
1602 #ifdef HAVE_IMAP
1603 if (imap_remove(name) != OKAY)
1604 #endif
1605 ec |= 1;
1606 break;
1607 case PROTO_MAILDIR:
1608 if (maildir_remove(name) != OKAY)
1609 ec |= 1;
1610 break;
1611 case PROTO_UNKNOWN:
1612 fprintf(stderr, _("Unknown protocol in \"%s\". Not removed.\n"),
1613 name);
1614 ec |= 1;
1615 break;
1617 } while (*++args != NULL);
1618 jleave:
1619 NYD_LEAVE;
1620 return ec;
1623 FL int
1624 c_rename(void *v)
1626 char **args = v, *old, *new;
1627 enum protocol oldp, newp;
1628 int ec;
1629 NYD_ENTER;
1631 ec = 1;
1633 if (args[0] == NULL || args[1] == NULL || args[2] != NULL) {
1634 fprintf(stderr, "Syntax: rename old new\n");
1635 goto jleave;
1638 if ((old = expand(args[0])) == NULL)
1639 goto jleave;
1640 oldp = which_protocol(old);
1641 if ((new = expand(args[1])) == NULL)
1642 goto jleave;
1643 newp = which_protocol(new);
1645 if (!strcmp(old, mailname) || !strcmp(new, mailname)) {
1646 fprintf(stderr, _("Cannot rename current mailbox \"%s\".\n"), old);
1647 goto jleave;
1649 if ((oldp == PROTO_IMAP || newp == PROTO_IMAP) && oldp != newp) {
1650 fprintf(stderr, _("Can only rename folders of same type.\n"));
1651 goto jleave;
1654 ec = 0;
1656 if (newp == PROTO_POP3)
1657 goto jnopop3;
1658 switch (oldp) {
1659 case PROTO_FILE:
1660 if (link(old, new) == -1) {
1661 switch (errno) {
1662 case EACCES:
1663 case EEXIST:
1664 case ENAMETOOLONG:
1665 case ENOENT:
1666 case ENOSPC:
1667 case EXDEV:
1668 perror(new);
1669 break;
1670 default:
1671 perror(old);
1673 ec |= 1;
1674 } else if (unlink(old) == -1) {
1675 perror(old);
1676 ec |= 1;
1678 break;
1679 case PROTO_MAILDIR:
1680 if (rename(old, new) == -1) {
1681 perror(old);
1682 ec |= 1;
1684 break;
1685 case PROTO_POP3:
1686 jnopop3:
1687 fprintf(stderr, _("Cannot rename POP3 mailboxes.\n"));
1688 ec |= 1;
1689 break;
1690 #ifdef HAVE_IMAP
1691 case PROTO_IMAP:
1692 if (imap_rename(old, new) != OKAY)
1693 ec |= 1;
1694 break;
1695 #endif
1696 case PROTO_UNKNOWN:
1697 default:
1698 fprintf(stderr, _(
1699 "Unknown protocol in \"%s\" and \"%s\". Not renamed.\n"), old, new);
1700 ec |= 1;
1701 break;
1703 jleave:
1704 NYD_LEAVE;
1705 return ec;
1708 FL int
1709 c_urlencode(void *v) /* XXX IDNA?? */
1711 char **ap;
1712 NYD_ENTER;
1714 for (ap = v; *ap != NULL; ++ap) {
1715 char *in = *ap, *out = urlxenc(in, FAL0);
1717 printf(" in: <%s> (%" ZFMT " bytes)\nout: <%s> (%" ZFMT " bytes)\n",
1718 in, strlen(in), out, strlen(out));
1720 NYD_LEAVE;
1721 return 0;
1724 FL int
1725 c_urldecode(void *v) /* XXX IDNA?? */
1727 char **ap;
1728 NYD_ENTER;
1730 for (ap = v; *ap != NULL; ++ap) {
1731 char *in = *ap, *out = urlxdec(in);
1733 printf(" in: <%s> (%" ZFMT " bytes)\nout: <%s> (%" ZFMT " bytes)\n",
1734 in, strlen(in), out, strlen(out));
1736 NYD_LEAVE;
1737 return 0;
1740 /* s-it-mode */