Many: unite many HAVE_ to HAVE_C90AMEND1, plus
[s-mailx.git] / cmd3.c
blob4771c51736cb37522bf7666e85276bc34835f4be
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 - 2013 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 /* Modify subject we reply to to begin with Re: if it does not already */
45 static char * _reedit(char *subj);
47 static int bangexp(char **str, size_t *size);
48 static void make_ref_and_cs(struct message *mp, struct header *head);
49 static int (* respond_or_Respond(int c))(int *, int);
50 static int respond_internal(int *msgvec, int recipient_record);
51 static char * fwdedit(char *subj);
52 static void asort(char **list);
53 static int diction(const void *a, const void *b);
54 static int Respond_internal(int *msgvec, int recipient_record);
55 static int resend1(void *v, int add_resent);
56 static void list_shortcuts(void);
57 static enum okay delete_shortcut(const char *str);
59 static char *
60 _reedit(char *subj)
62 struct str in, out;
63 char *newsubj = NULL;
65 if (subj == NULL || *subj == '\0')
66 goto j_leave;
68 in.s = subj;
69 in.l = strlen(subj);
70 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
72 if ((out.s[0] == 'r' || out.s[0] == 'R') &&
73 (out.s[1] == 'e' || out.s[1] == 'E') &&
74 out.s[2] == ':') {
75 newsubj = savestr(out.s);
76 goto jleave;
78 newsubj = salloc(out.l + 5);
79 sstpcpy(sstpcpy(newsubj, "Re: "), out.s);
80 jleave:
81 free(out.s);
82 j_leave:
83 return (newsubj);
87 * Process a shell escape by saving signals, ignoring signals,
88 * and forking a sh -c
90 FL int
91 shell(void *v)
93 char *str = v, *cmd;
94 char const *sh;
95 size_t cmdsize;
96 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
98 cmd = smalloc(cmdsize = strlen(str) + 1);
99 memcpy(cmd, str, cmdsize);
100 if (bangexp(&cmd, &cmdsize) < 0)
101 return 1;
102 if ((sh = value("SHELL")) == NULL)
103 sh = SHELL;
104 run_command(sh, 0, -1, -1, "-c", cmd, NULL);
105 safe_signal(SIGINT, sigint);
106 printf("!\n");
107 free(cmd);
108 return 0;
112 * Fork an interactive shell.
114 /*ARGSUSED*/
115 FL int
116 dosh(void *v)
118 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
119 char const *sh;
120 (void)v;
122 if ((sh = value("SHELL")) == NULL)
123 sh = SHELL;
124 run_command(sh, 0, -1, -1, NULL, NULL, NULL);
125 safe_signal(SIGINT, sigint);
126 putchar('\n');
127 return 0;
131 * Expand the shell escape by expanding unescaped !'s into the
132 * last issued command where possible.
135 static char *lastbang;
136 static size_t lastbangsize;
138 static int
139 bangexp(char **str, size_t *size)
141 char *bangbuf;
142 int changed = 0;
143 int dobang = value("bang") != NULL;
144 size_t sz, i, j, bangbufsize;
146 bangbuf = smalloc(bangbufsize = *size);
147 i = j = 0;
148 while ((*str)[i]) {
149 if (dobang) {
150 if ((*str)[i] == '!') {
151 sz = strlen(lastbang);
152 bangbuf = srealloc(bangbuf, bangbufsize += sz);
153 changed++;
154 memcpy(bangbuf + j, lastbang, sz + 1);
155 j += sz;
156 i++;
157 continue;
160 if ((*str)[i] == '\\' && (*str)[i + 1] == '!') {
161 bangbuf[j++] = '!';
162 i += 2;
163 changed++;
165 bangbuf[j++] = (*str)[i++];
167 bangbuf[j] = '\0';
168 if (changed) {
169 printf("!%s\n", bangbuf);
170 fflush(stdout);
172 sz = j + 1;
173 if (sz > *size)
174 *str = srealloc(*str, *size = sz);
175 memcpy(*str, bangbuf, sz);
176 if (sz > lastbangsize)
177 lastbang = srealloc(lastbang, lastbangsize = sz);
178 memcpy(lastbang, bangbuf, sz);
179 free(bangbuf);
180 return 0;
183 /*ARGSUSED*/
184 FL int
185 help(void *v)
187 int ret = 0;
188 char *arg = *(char**)v;
190 if (arg != NULL) {
191 #ifdef HAVE_DOCSTRINGS
192 ret = ! print_comm_docstr(arg);
193 if (ret)
194 fprintf(stderr, tr(91, "Unknown command: `%s'\n"), arg);
195 #else
196 ret = ccmdnotsupp(NULL);
197 #endif
198 goto jleave;
201 /* Very ugly, but take care for compiler supported string lengths :( */
202 printf(tr(295, "%s commands:\n"), progname);
203 puts(tr(296,
204 "type <message list> type messages\n"
205 "next goto and type next message\n"
206 "from <message list> give head lines of messages\n"
207 "headers print out active message headers\n"
208 "delete <message list> delete messages\n"
209 "undelete <message list> undelete messages\n"));
210 puts(tr(297,
211 "save <message list> folder append messages to folder and mark as saved\n"
212 "copy <message list> folder append messages to folder without marking them\n"
213 "write <message list> file append message texts to file, save attachments\n"
214 "preserve <message list> keep incoming messages in mailbox even if saved\n"
215 "Reply <message list> reply to message senders\n"
216 "reply <message list> reply to message senders and all recipients\n"));
217 puts(tr(298,
218 "mail addresses mail to specific recipients\n"
219 "file folder change to another folder\n"
220 "quit quit and apply changes to folder\n"
221 "xit quit and discard changes made to folder\n"
222 "! shell escape\n"
223 "cd <directory> chdir to directory or home if none given\n"
224 "list list names of all available commands\n"));
225 printf(tr(299,
226 "\nA <message list> consists of integers, ranges of same, or other criteria\n"
227 "separated by spaces. If omitted, %s uses the last message typed.\n"),
228 progname);
230 jleave:
231 return ret;
234 FL int
235 c_cwd(void *v)
237 char buf[MAXPATHLEN]; /* TODO getcwd(3) may return a larger value */
239 if (getcwd(buf, sizeof buf) != NULL) {
240 puts(buf);
241 v = (void*)0x1;
242 } else {
243 perror("getcwd");
244 v = NULL;
246 return (v == NULL);
249 FL int
250 c_chdir(void *v)
252 char **arglist = v;
253 char const *cp;
255 if (*arglist == NULL)
256 cp = homedir;
257 else if ((cp = file_expand(*arglist)) == NULL)
258 goto jleave;
259 if (chdir(cp) < 0) {
260 perror(cp);
261 cp = NULL;
263 jleave:
264 return (cp == NULL);
267 static void
268 make_ref_and_cs(struct message *mp, struct header *head)
270 char *oldref, *oldmsgid, *newref, *cp;
271 size_t oldreflen = 0, oldmsgidlen = 0, reflen;
272 unsigned i;
273 struct name *n;
275 oldref = hfield1("references", mp);
276 oldmsgid = hfield1("message-id", mp);
277 if (oldmsgid == NULL || *oldmsgid == '\0') {
278 head->h_ref = NULL;
279 return;
281 reflen = 1;
282 if (oldref) {
283 oldreflen = strlen(oldref);
284 reflen += oldreflen + 2;
286 if (oldmsgid) {
287 oldmsgidlen = strlen(oldmsgid);
288 reflen += oldmsgidlen;
291 newref = ac_alloc(reflen);
292 if (oldref != NULL) {
293 memcpy(newref, oldref, oldreflen + 1);
294 if (oldmsgid != NULL) {
295 newref[oldreflen++] = ',';
296 newref[oldreflen++] = ' ';
297 memcpy(newref + oldreflen, oldmsgid, oldmsgidlen + 1);
299 } else if (oldmsgid)
300 memcpy(newref, oldmsgid, oldmsgidlen + 1);
301 n = extract(newref, GREF);
302 ac_free(newref);
305 * Limit the references to 21 entries.
307 while (n->n_flink != NULL)
308 n = n->n_flink;
309 for (i = 1; i < 21; i++) {
310 if (n->n_blink != NULL)
311 n = n->n_blink;
312 else
313 break;
315 n->n_blink = NULL;
316 head->h_ref = n;
317 if (value("reply-in-same-charset") != NULL &&
318 (cp = hfield1("content-type", mp)) != NULL)
319 head->h_charset = mime_getparam("charset", cp);
322 static int
323 (*respond_or_Respond(int c))(int *, int)
325 int opt = 0;
327 opt += (value("Replyall") != NULL);
328 opt += (value("flipr") != NULL);
329 return ((opt == 1) ^ (c == 'R')) ? Respond_internal : respond_internal;
332 FL int
333 respond(void *v)
335 return (respond_or_Respond('r'))((int *)v, 0);
338 FL int
339 respondall(void *v)
341 return respond_internal((int *)v, 0);
344 FL int
345 respondsender(void *v)
347 return Respond_internal((int *)v, 0);
350 FL int
351 followup(void *v)
353 return (respond_or_Respond('r'))((int *)v, 1);
356 FL int
357 followupall(void *v)
359 return respond_internal((int *)v, 1);
362 FL int
363 followupsender(void *v)
365 return Respond_internal((int *)v, 1);
369 * Reply to a single message. Extract each name from the
370 * message header and send them off to mail1()
372 static int
373 respond_internal(int *msgvec, int recipient_record)
375 struct header head;
376 struct message *mp;
377 char *cp, *rcv;
378 struct name *np = NULL;
379 enum gfield gf = boption("fullnames") ? GFULL : GSKIN;
381 if (msgvec[1] != 0) {
382 fprintf(stderr, tr(37,
383 "Sorry, can't reply to multiple messages at once\n"));
384 return 1;
386 mp = &message[msgvec[0] - 1];
387 touch(mp);
388 setdot(mp);
390 if ((rcv = hfield1("reply-to", mp)) == NULL)
391 if ((rcv = hfield1("from", mp)) == NULL)
392 rcv = nameof(mp, 1);
393 if (rcv != NULL)
394 np = lextract(rcv, GTO|gf);
395 if (! boption("recipients-in-cc") && (cp = hfield1("to", mp)) != NULL)
396 np = cat(np, lextract(cp, GTO | gf));
398 * Delete my name from the reply list,
399 * and with it, all my alternate names.
401 np = elide(delete_alternates(np));
402 if (np == NULL)
403 np = lextract(rcv, GTO | gf);
405 memset(&head, 0, sizeof head);
406 head.h_to = np;
407 head.h_subject = hfield1("subject", mp);
408 head.h_subject = _reedit(head.h_subject);
409 /* Cc: */
410 np = NULL;
411 if (boption("recipients-in-cc") && (cp = hfield1("to", mp)) != NULL)
412 np = lextract(cp, GCC | gf);
413 if ((cp = hfield1("cc", mp)) != NULL)
414 np = cat(np, lextract(cp, GCC | gf));
415 if (np != NULL)
416 head.h_cc = elide(delete_alternates(np));
417 make_ref_and_cs(mp, &head);
419 if (boption("quote-as-attachment")) {
420 head.h_attach = csalloc(1, sizeof *head.h_attach);
421 head.h_attach->a_msgno = *msgvec;
422 head.h_attach->a_content_description = tr(512,
423 "Original message content");
426 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
427 boption("markanswered") &&
428 (mp->m_flag & MANSWERED) == 0)
429 mp->m_flag |= MANSWER | MANSWERED;
430 return 0;
434 * Forward a message to a new recipient, in the sense of RFC 2822.
436 static int
437 forward1(char *str, int recipient_record)
439 int *msgvec;
440 char *recipient;
441 struct message *mp;
442 struct header head;
443 bool_t f, forward_as_attachment;
445 forward_as_attachment = boption("forward-as-attachment");
446 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
447 if ((recipient = laststring(str, &f, 0)) == NULL) {
448 puts(tr(47, "No recipient specified."));
449 return 1;
451 if (!f) {
452 *msgvec = first(0, MMNORM);
453 if (*msgvec == 0) {
454 if (inhook)
455 return 0;
456 printf("No messages to forward.\n");
457 return 1;
459 msgvec[1] = 0;
460 } else if (getmsglist(str, msgvec, 0) < 0)
461 return 1;
462 if (*msgvec == 0) {
463 if (inhook)
464 return 0;
465 printf("No applicable messages.\n");
466 return 1;
468 if (msgvec[1] != 0) {
469 printf("Cannot forward multiple messages at once\n");
470 return 1;
472 memset(&head, 0, sizeof head);
473 if ((head.h_to = lextract(recipient,
474 GTO | (value("fullnames") ? GFULL : GSKIN))) == NULL)
475 return 1;
476 mp = &message[*msgvec - 1];
477 if (forward_as_attachment) {
478 head.h_attach = csalloc(1, sizeof *head.h_attach);
479 head.h_attach->a_msgno = *msgvec;
480 head.h_attach->a_content_description = "Forwarded message";
481 } else {
482 touch(mp);
483 setdot(mp);
485 head.h_subject = hfield1("subject", mp);
486 head.h_subject = fwdedit(head.h_subject);
487 mail1(&head, 1, (forward_as_attachment ? NULL : mp),
488 NULL, recipient_record, 1);
489 return 0;
493 * Modify the subject we are replying to to begin with Fwd:.
495 static char *
496 fwdedit(char *subj)
498 struct str in, out;
499 char *newsubj;
501 if (subj == NULL || *subj == '\0')
502 return NULL;
503 in.s = subj;
504 in.l = strlen(subj);
505 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
507 newsubj = salloc(out.l + 6);
508 memcpy(newsubj, "Fwd: ", 5);
509 memcpy(newsubj + 5, out.s, out.l + 1);
510 free(out.s);
511 return newsubj;
515 * The 'forward' command.
517 FL int
518 forwardcmd(void *v)
520 return forward1(v, 0);
524 * Similar to forward, saving the message in a file named after the
525 * first recipient.
527 FL int
528 Forwardcmd(void *v)
530 return forward1(v, 1);
534 * Preserve the named messages, so that they will be sent
535 * back to the system mailbox.
537 FL int
538 preserve(void *v)
540 int *msgvec = v;
541 struct message *mp;
542 int *ip, mesg;
544 if (edit) {
545 printf(tr(39, "Cannot \"preserve\" in edit mode\n"));
546 return(1);
548 for (ip = msgvec; *ip != 0; ip++) {
549 mesg = *ip;
550 mp = &message[mesg-1];
551 mp->m_flag |= MPRESERVE;
552 mp->m_flag &= ~MBOX;
553 setdot(mp);
555 * This is now Austin Group Request XCU #20.
557 did_print_dot = TRU1;
559 return(0);
563 * Mark all given messages as unread.
565 FL int
566 unread(void *v)
568 int *msgvec = v;
569 int *ip;
571 for (ip = msgvec; *ip != 0; ip++) {
572 setdot(&message[*ip-1]);
573 dot->m_flag &= ~(MREAD|MTOUCH);
574 dot->m_flag |= MSTATUS;
575 #ifdef HAVE_IMAP
576 if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
577 imap_unread(&message[*ip-1], *ip); /* TODO return? */
578 #endif
580 * The "unread" command is not part of POSIX mailx.
582 did_print_dot = TRU1;
584 return(0);
588 * Mark all given messages as read.
590 FL int
591 seen(void *v)
593 int *msgvec = v;
594 int *ip;
596 for (ip = msgvec; *ip; ip++) {
597 setdot(&message[*ip-1]);
598 touch(&message[*ip-1]);
600 return 0;
604 * Print the size of each message.
606 FL int
607 messize(void *v)
609 int *msgvec = v;
610 struct message *mp;
611 int *ip, mesg;
613 for (ip = msgvec; *ip != 0; ip++) {
614 mesg = *ip;
615 mp = &message[mesg-1];
616 printf("%d: ", mesg);
617 if (mp->m_xlines > 0)
618 printf("%ld", mp->m_xlines);
619 else
620 putchar(' ');
621 printf("/%lu\n", (unsigned long)mp->m_xsize);
623 return(0);
627 * Quit quickly. If we are sourcing, just pop the input level
628 * by returning an error.
630 /*ARGSUSED*/
631 FL int
632 rexit(void *v)
634 (void)v;
635 if (sourcing)
636 return(1);
637 exit(0);
638 /*NOTREACHED*/
641 FL int
642 set(void *v)
644 char **ap = v, *cp, *cp2, *varbuf, c;
645 int errs = 0;
647 if (*ap == NULL) {
648 var_list_all();
649 goto jleave;
652 for (; *ap != NULL; ++ap) {
653 cp = *ap;
654 cp2 = varbuf = ac_alloc(strlen(cp) + 1);
655 for (; (c = *cp) != '=' && c != '\0'; ++cp)
656 *cp2++ = c;
657 *cp2 = '\0';
658 if (c == '\0')
659 cp = UNCONST("");
660 else
661 ++cp;
662 if (varbuf == cp2) {
663 fprintf(stderr,
664 tr(41, "Non-null variable name required\n"));
665 ++errs;
666 goto jnext;
668 if (varbuf[0] == 'n' && varbuf[1] == 'o')
669 errs += unset_internal(&varbuf[2]);
670 else
671 errs += assign(varbuf, cp);
672 jnext: ac_free(varbuf);
674 jleave:
675 return (errs);
679 * Unset a bunch of variable values.
681 FL int
682 unset(void *v)
684 int errs;
685 char **ap;
687 errs = 0;
688 for (ap = (char **)v; *ap != NULL; ap++)
689 errs += unset_internal(*ap);
690 return(errs);
694 * Put add users to a group.
696 FL int
697 group(void *v)
699 char **argv = v;
700 struct grouphead *gh;
701 struct group *gp;
702 int h;
703 int s;
704 char **ap, *gname, **p;
706 if (*argv == NULL) {
707 for (h = 0, s = 1; h < HSHSIZE; h++)
708 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
709 s++;
710 /*LINTED*/
711 ap = (char **)salloc(s * sizeof *ap);
712 for (h = 0, p = ap; h < HSHSIZE; h++)
713 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
714 *p++ = gh->g_name;
715 *p = NULL;
716 asort(ap);
717 for (p = ap; *p != NULL; p++)
718 printgroup(*p);
719 return(0);
721 if (argv[1] == NULL) {
722 printgroup(*argv);
723 return(0);
725 gname = *argv;
726 h = hash(gname);
727 if ((gh = findgroup(gname)) == NULL) {
728 gh = (struct grouphead *)scalloc(1, sizeof *gh);
729 gh->g_name = sstrdup(gname);
730 gh->g_list = NULL;
731 gh->g_link = groups[h];
732 groups[h] = gh;
736 * Insert names from the command list into the group.
737 * Who cares if there are duplicates? They get tossed
738 * later anyway.
741 for (ap = argv+1; *ap != NULL; ap++) {
742 gp = (struct group *)scalloc(1, sizeof *gp);
743 gp->ge_name = sstrdup(*ap);
744 gp->ge_link = gh->g_list;
745 gh->g_list = gp;
747 return(0);
751 * Delete the passed groups.
753 FL int
754 ungroup(void *v)
756 char **argv = v;
758 if (*argv == NULL) {
759 fprintf(stderr, tr(209, "Must specify alias to remove\n"));
760 return 1;
763 remove_group(*argv);
764 while (*++argv != NULL);
765 return 0;
769 * Sort the passed string vecotor into ascending dictionary
770 * order.
772 static void
773 asort(char **list)
775 char **ap;
777 for (ap = list; *ap != NULL; ap++)
779 if (ap-list < 2)
780 return;
781 qsort(list, ap-list, sizeof(*list), diction);
785 * Do a dictionary order comparison of the arguments from
786 * qsort.
788 static int
789 diction(const void *a, const void *b)
791 return(strcmp(*(char**)UNCONST(a), *(char**)UNCONST(b)));
795 * Change to another file. With no argument, print information about
796 * the current file.
798 FL int
799 cfile(void *v)
801 char **argv = v;
802 int i;
804 if (*argv == NULL) {
805 newfileinfo();
806 return 0;
809 if (inhook) {
810 fprintf(stderr, tr(516,
811 "Cannot change folder from within a hook.\n"));
812 return 1;
815 save_mbox_for_possible_quitstuff();
817 i = setfile(*argv, 0);
818 if (i < 0)
819 return 1;
820 callhook(mailname, 0);
821 if (i > 0 && value("emptystart") == NULL)
822 return 1;
823 announce(value("bsdcompat") != NULL || value("bsdannounce") != NULL);
824 return 0;
828 * Expand file names like echo
830 FL int
831 echo(void *v)
833 char const **argv = v, **ap, *cp;
834 int c;
836 for (ap = argv; *ap != NULL; ++ap) {
837 cp = *ap;
838 if ((cp = fexpand(cp, FEXP_NSHORTCUT)) != NULL) {
839 if (ap != argv)
840 putchar(' ');
841 c = 0;
842 while (*cp != '\0' &&
843 (c = expand_shell_escape(&cp, FAL0))
844 > 0)
845 putchar(c);
846 /* \c ends overall processing */
847 if (c < 0)
848 goto jleave;
851 putchar('\n');
852 jleave:
853 return 0;
856 FL int
857 Respond(void *v)
859 return (respond_or_Respond('R'))((int *)v, 0);
862 FL int
863 Followup(void *v)
865 return (respond_or_Respond('R'))((int *)v, 1);
869 * Reply to a series of messages by simply mailing to the senders
870 * and not messing around with the To: and Cc: lists as in normal
871 * reply.
873 static int
874 Respond_internal(int *msgvec, int recipient_record)
876 struct header head;
877 struct message *mp;
878 int *ap;
879 char *cp;
880 enum gfield gf = boption("fullnames") ? GFULL : GSKIN;
882 memset(&head, 0, sizeof head);
884 for (ap = msgvec; *ap != 0; ap++) {
885 mp = &message[*ap - 1];
886 touch(mp);
887 setdot(mp);
888 if ((cp = hfield1("reply-to", mp)) == NULL)
889 if ((cp = hfield1("from", mp)) == NULL)
890 cp = nameof(mp, 2);
891 head.h_to = cat(head.h_to, lextract(cp, GTO | gf));
893 if (head.h_to == NULL)
894 return 0;
896 mp = &message[msgvec[0] - 1];
897 head.h_subject = hfield1("subject", mp);
898 head.h_subject = _reedit(head.h_subject);
899 make_ref_and_cs(mp, &head);
901 if (boption("quote-as-attachment")) {
902 head.h_attach = csalloc(1, sizeof *head.h_attach);
903 head.h_attach->a_msgno = *msgvec;
904 head.h_attach->a_content_description = tr(512,
905 "Original message content");
908 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
909 value("markanswered") && (mp->m_flag & MANSWERED) == 0)
910 mp->m_flag |= MANSWER | MANSWERED;
911 return 0;
915 * Conditional commands. These allow one to parameterize one's
916 * .mailrc and do some things if sending, others if receiving.
918 FL int
919 ifcmd(void *v)
921 char **argv = v;
922 char *cp;
924 if (cond != CANY) {
925 printf(tr(42, "Illegal nested \"if\"\n"));
926 return(1);
928 cond = CANY;
929 cp = argv[0];
930 switch (*cp) {
931 case 'r': case 'R':
932 cond = CRCV;
933 break;
935 case 's': case 'S':
936 cond = CSEND;
937 break;
939 case 't': case 'T':
940 cond = CTERM;
941 break;
943 default:
944 printf(tr(43, "Unrecognized if-keyword: \"%s\"\n"), cp);
945 return(1);
947 return(0);
951 * Implement 'else'. This is pretty simple -- we just
952 * flip over the conditional flag.
954 /*ARGSUSED*/
955 FL int
956 elsecmd(void *v)
958 (void)v;
960 switch (cond) {
961 case CANY:
962 printf(tr(44, "\"Else\" without matching \"if\"\n"));
963 return(1);
965 case CSEND:
966 cond = CRCV;
967 break;
969 case CRCV:
970 cond = CSEND;
971 break;
973 case CTERM:
974 cond = CNONTERM;
975 break;
977 default:
978 printf(tr(45, "Mail's idea of conditions is screwed up\n"));
979 cond = CANY;
980 break;
982 return(0);
986 * End of if statement. Just set cond back to anything.
988 /*ARGSUSED*/
989 FL int
990 endifcmd(void *v)
992 (void)v;
994 if (cond == CANY) {
995 printf(tr(46, "\"Endif\" without matching \"if\"\n"));
996 return(1);
998 cond = CANY;
999 return(0);
1003 * Set the list of alternate names.
1005 FL int
1006 alternates(void *v)
1008 size_t l;
1009 char **namelist = v, **ap, **ap2, *cp;
1011 l = argcount(namelist) + 1;
1013 if (l == 1) {
1014 if (altnames == NULL)
1015 goto jleave;
1016 for (ap = altnames; *ap != NULL; ++ap)
1017 printf("%s ", *ap);
1018 printf("\n");
1019 goto jleave;
1022 if (altnames != NULL) {
1023 for (ap = altnames; *ap != NULL; ++ap)
1024 free(*ap);
1025 free(altnames);
1027 altnames = smalloc(l * sizeof(char*));
1028 for (ap = namelist, ap2 = altnames; *ap; ++ap, ++ap2) {
1029 l = strlen(*ap) + 1;
1030 cp = smalloc(l);
1031 memcpy(cp, *ap, l);
1032 *ap2 = cp;
1034 *ap2 = NULL;
1035 jleave:
1036 return (0);
1040 * Do the real work of resending.
1042 static int
1043 resend1(void *v, int add_resent)
1045 char *name, *str;
1046 struct name *to;
1047 struct name *sn;
1048 int *ip, *msgvec;
1049 bool_t f;
1051 str = (char *)v;
1052 /*LINTED*/
1053 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
1054 name = laststring(str, &f, 1);
1055 if (name == NULL) {
1056 puts(tr(47, "No recipient specified."));
1057 return 1;
1059 if (!f) {
1060 *msgvec = first(0, MMNORM);
1061 if (*msgvec == 0) {
1062 if (inhook)
1063 return 0;
1064 puts(tr(48, "No applicable messages."));
1065 return 1;
1067 msgvec[1] = 0;
1068 } else if (getmsglist(str, msgvec, 0) < 0)
1069 return 1;
1070 if (*msgvec == 0) {
1071 if (inhook)
1072 return 0;
1073 printf("No applicable messages.\n");
1074 return 1;
1076 sn = nalloc(name, GTO);
1077 to = usermap(sn, FAL0);
1078 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
1079 if (resend_msg(&message[*ip - 1], to, add_resent) != OKAY)
1080 return 1;
1082 return 0;
1086 * Resend a message list to a third person.
1088 FL int
1089 resendcmd(void *v)
1091 return resend1(v, 1);
1095 * Resend a message list to a third person without adding headers.
1097 FL int
1098 Resendcmd(void *v)
1100 return resend1(v, 0);
1104 * 'newmail' or 'inc' command: Check for new mail without writing old
1105 * mail back.
1107 /*ARGSUSED*/
1108 FL int
1109 newmail(void *v)
1111 int val = 1, mdot;
1112 (void)v;
1114 if (
1115 #ifdef HAVE_IMAP
1116 (mb.mb_type != MB_IMAP || imap_newmail(1)) &&
1117 #endif
1118 (val = setfile(mailname, 1)) == 0) {
1119 mdot = getmdot(1);
1120 setdot(&message[mdot - 1]);
1122 return val;
1125 static void
1126 list_shortcuts(void)
1128 struct shortcut *s;
1130 for (s = shortcuts; s; s = s->sh_next)
1131 printf("%s=%s\n", s->sh_short, s->sh_long);
1134 FL int
1135 shortcut(void *v)
1137 char **args = (char **)v;
1138 struct shortcut *s;
1140 if (args[0] == NULL) {
1141 list_shortcuts();
1142 return 0;
1144 if (args[1] == NULL) {
1145 fprintf(stderr, tr(220,
1146 "expansion name for shortcut missing\n"));
1147 return 1;
1149 if (args[2] != NULL) {
1150 fprintf(stderr, tr(221, "too many arguments\n"));
1151 return 1;
1153 if ((s = get_shortcut(args[0])) != NULL) {
1154 free(s->sh_long);
1155 s->sh_long = sstrdup(args[1]);
1156 } else {
1157 s = scalloc(1, sizeof *s);
1158 s->sh_short = sstrdup(args[0]);
1159 s->sh_long = sstrdup(args[1]);
1160 s->sh_next = shortcuts;
1161 shortcuts = s;
1163 return 0;
1166 FL struct shortcut *
1167 get_shortcut(const char *str)
1169 struct shortcut *s;
1171 for (s = shortcuts; s; s = s->sh_next)
1172 if (strcmp(str, s->sh_short) == 0)
1173 break;
1174 return s;
1177 static enum okay
1178 delete_shortcut(const char *str)
1180 struct shortcut *sp, *sq;
1182 for (sp = shortcuts, sq = NULL; sp; sq = sp, sp = sp->sh_next) {
1183 if (strcmp(sp->sh_short, str) == 0) {
1184 free(sp->sh_short);
1185 free(sp->sh_long);
1186 if (sq)
1187 sq->sh_next = sp->sh_next;
1188 if (sp == shortcuts)
1189 shortcuts = sp->sh_next;
1190 free(sp);
1191 return OKAY;
1194 return STOP;
1197 FL int
1198 unshortcut(void *v)
1200 char **args = (char **)v;
1201 int errs = 0;
1203 if (args[0] == NULL) {
1204 fprintf(stderr, tr(222, "need shortcut names to remove\n"));
1205 return 1;
1207 while (*args != NULL) {
1208 if (delete_shortcut(*args) != OKAY) {
1209 errs = 1;
1210 fprintf(stderr, tr(223, "%s: no such shortcut\n"),
1211 *args);
1213 args++;
1215 return errs;
1218 FL int
1219 cflag(void *v)
1221 struct message *m;
1222 int *msgvec = v;
1223 int *ip;
1225 for (ip = msgvec; *ip != 0; ip++) {
1226 m = &message[*ip-1];
1227 setdot(m);
1228 if ((m->m_flag & (MFLAG|MFLAGGED)) == 0)
1229 m->m_flag |= MFLAG|MFLAGGED;
1231 return 0;
1234 FL int
1235 cunflag(void *v)
1237 struct message *m;
1238 int *msgvec = v;
1239 int *ip;
1241 for (ip = msgvec; *ip != 0; ip++) {
1242 m = &message[*ip-1];
1243 setdot(m);
1244 if (m->m_flag & (MFLAG|MFLAGGED)) {
1245 m->m_flag &= ~(MFLAG|MFLAGGED);
1246 m->m_flag |= MUNFLAG;
1249 return 0;
1252 FL int
1253 canswered(void *v)
1255 struct message *m;
1256 int *msgvec = v;
1257 int *ip;
1259 for (ip = msgvec; *ip != 0; ip++) {
1260 m = &message[*ip-1];
1261 setdot(m);
1262 if ((m->m_flag & (MANSWER|MANSWERED)) == 0)
1263 m->m_flag |= MANSWER|MANSWERED;
1265 return 0;
1268 FL int
1269 cunanswered(void *v)
1271 struct message *m;
1272 int *msgvec = v;
1273 int *ip;
1275 for (ip = msgvec; *ip != 0; ip++) {
1276 m = &message[*ip-1];
1277 setdot(m);
1278 if (m->m_flag & (MANSWER|MANSWERED)) {
1279 m->m_flag &= ~(MANSWER|MANSWERED);
1280 m->m_flag |= MUNANSWER;
1283 return 0;
1286 FL int
1287 cdraft(void *v)
1289 struct message *m;
1290 int *msgvec = v;
1291 int *ip;
1293 for (ip = msgvec; *ip != 0; ip++) {
1294 m = &message[*ip-1];
1295 setdot(m);
1296 if ((m->m_flag & (MDRAFT|MDRAFTED)) == 0)
1297 m->m_flag |= MDRAFT|MDRAFTED;
1299 return 0;
1302 FL int
1303 cundraft(void *v)
1305 struct message *m;
1306 int *msgvec = v;
1307 int *ip;
1309 for (ip = msgvec; *ip != 0; ip++) {
1310 m = &message[*ip-1];
1311 setdot(m);
1312 if (m->m_flag & (MDRAFT|MDRAFTED)) {
1313 m->m_flag &= ~(MDRAFT|MDRAFTED);
1314 m->m_flag |= MUNDRAFT;
1317 return 0;
1320 /*ARGSUSED*/
1321 FL int
1322 cnoop(void *v)
1324 (void)v;
1326 switch (mb.mb_type) {
1327 case MB_IMAP:
1328 #ifdef HAVE_IMAP
1329 imap_noop();
1330 break;
1331 #else
1332 return (ccmdnotsupp(NULL));
1333 #endif
1334 case MB_POP3:
1335 #ifdef HAVE_POP3
1336 pop3_noop();
1337 break;
1338 #else
1339 return (ccmdnotsupp(NULL));
1340 #endif
1341 default:
1342 break;
1344 return 0;
1347 FL int
1348 cremove(void *v)
1350 char vb[LINESIZE];
1351 char **args = v;
1352 char *name;
1353 int ec = 0;
1355 if (*args == NULL) {
1356 fprintf(stderr, tr(290, "Syntax is: remove mailbox ...\n"));
1357 return (1);
1359 do {
1360 if ((name = expand(*args)) == NULL)
1361 continue;
1362 if (strcmp(name, mailname) == 0) {
1363 fprintf(stderr, tr(286,
1364 "Cannot remove current mailbox \"%s\".\n"),
1365 name);
1366 ec |= 1;
1367 continue;
1369 snprintf(vb, sizeof vb, tr(287, "Remove \"%s\" (y/n) ? "),
1370 name);
1371 if (yorn(vb) == 0)
1372 continue;
1373 switch (which_protocol(name)) {
1374 case PROTO_FILE:
1375 if (unlink(name) < 0) { /* do not handle .gz .bz2 */
1376 perror(name);
1377 ec |= 1;
1379 break;
1380 case PROTO_POP3:
1381 fprintf(stderr, tr(288,
1382 "Cannot remove POP3 mailbox \"%s\".\n"),
1383 name);
1384 ec |= 1;
1385 break;
1386 case PROTO_IMAP:
1387 #ifdef HAVE_IMAP
1388 if (imap_remove(name) != OKAY)
1389 #endif
1390 ec |= 1;
1391 break;
1392 case PROTO_MAILDIR:
1393 if (maildir_remove(name) != OKAY)
1394 ec |= 1;
1395 break;
1396 case PROTO_UNKNOWN:
1397 fprintf(stderr, tr(289,
1398 "Unknown protocol in \"%s\". Not removed.\n"),
1399 name);
1400 ec |= 1;
1402 } while (*++args);
1403 return ec;
1406 FL int
1407 crename(void *v)
1409 char **args = v, *old, *new;
1410 enum protocol oldp, newp;
1411 int ec = 0;
1413 if (args[0] == NULL || args[1] == NULL || args[2] != NULL) {
1414 fprintf(stderr, "Syntax: rename old new\n");
1415 return (1);
1418 if ((old = expand(args[0])) == NULL)
1419 return (1);
1420 oldp = which_protocol(old);
1421 if ((new = expand(args[1])) == NULL)
1422 return (1);
1423 newp = which_protocol(new);
1425 if (strcmp(old, mailname) == 0 || strcmp(new, mailname) == 0) {
1426 fprintf(stderr, tr(291,
1427 "Cannot rename current mailbox \"%s\".\n"), old);
1428 return 1;
1430 if ((oldp == PROTO_IMAP || newp == PROTO_IMAP) && oldp != newp) {
1431 fprintf(stderr, tr(292,
1432 "Can only rename folders of same type.\n"));
1433 return 1;
1435 if (newp == PROTO_POP3)
1436 goto nopop3;
1437 switch (oldp) {
1438 case PROTO_FILE:
1439 if (link(old, new) < 0) {
1440 switch (errno) {
1441 case EACCES:
1442 case EEXIST:
1443 case ENAMETOOLONG:
1444 case ENOENT:
1445 case ENOSPC:
1446 case EXDEV:
1447 perror(new);
1448 break;
1449 default:
1450 perror(old);
1452 ec |= 1;
1453 } else if (unlink(old) < 0) {
1454 perror(old);
1455 ec |= 1;
1457 break;
1458 case PROTO_MAILDIR:
1459 if (rename(old, new) < 0) {
1460 perror(old);
1461 ec |= 1;
1463 break;
1464 case PROTO_POP3:
1465 nopop3: fprintf(stderr, tr(293, "Cannot rename POP3 mailboxes.\n"));
1466 ec |= 1;
1467 break;
1468 #ifdef HAVE_IMAP
1469 case PROTO_IMAP:
1470 if (imap_rename(old, new) != OKAY)
1471 ec |= 1;
1472 break;
1473 #endif
1474 case PROTO_UNKNOWN:
1475 default:
1476 fprintf(stderr, tr(294,
1477 "Unknown protocol in \"%s\" and \"%s\". "
1478 "Not renamed.\n"), old, new);
1479 ec |= 1;
1481 return ec;