Offer a simple coloured display..
[s-mailx.git] / cmd3.c
blob2b20b750569ae4731b8544a1de75e4fbc9cce52f
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 = ok_vlook(SHELL)) == NULL)
103 sh = XSHELL;
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 = ok_vlook(SHELL)) == NULL)
123 sh = XSHELL;
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 = ok_blook(bang);
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 (ok_blook(reply_in_same_charset) &&
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 += ok_blook(Replyall);
328 opt += ok_blook(flipr);
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 = ok_blook(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 (!ok_blook(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 (ok_blook(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 (ok_blook(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 ok_blook(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 = ok_blook(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 | (ok_blook(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 += _var_vokclear(&varbuf[2]);
670 else
671 errs += _var_vokset(varbuf, (uintptr_t)cp);
672 jnext:
673 ac_free(varbuf);
675 jleave:
676 return (errs);
680 * Unset a bunch of variable values.
682 FL int
683 unset(void *v)
685 int errs;
686 char **ap;
688 errs = 0;
689 for (ap = (char**)v; *ap != NULL; ap++)
690 errs += _var_vokclear(*ap);
691 return errs;
695 * Put add users to a group.
697 FL int
698 group(void *v)
700 char **argv = v;
701 struct grouphead *gh;
702 struct group *gp;
703 int h;
704 int s;
705 char **ap, *gname, **p;
707 if (*argv == NULL) {
708 for (h = 0, s = 1; h < HSHSIZE; h++)
709 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
710 s++;
711 /*LINTED*/
712 ap = (char **)salloc(s * sizeof *ap);
713 for (h = 0, p = ap; h < HSHSIZE; h++)
714 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
715 *p++ = gh->g_name;
716 *p = NULL;
717 asort(ap);
718 for (p = ap; *p != NULL; p++)
719 printgroup(*p);
720 return(0);
722 if (argv[1] == NULL) {
723 printgroup(*argv);
724 return(0);
726 gname = *argv;
727 h = hash(gname);
728 if ((gh = findgroup(gname)) == NULL) {
729 gh = (struct grouphead *)scalloc(1, sizeof *gh);
730 gh->g_name = sstrdup(gname);
731 gh->g_list = NULL;
732 gh->g_link = groups[h];
733 groups[h] = gh;
737 * Insert names from the command list into the group.
738 * Who cares if there are duplicates? They get tossed
739 * later anyway.
742 for (ap = argv+1; *ap != NULL; ap++) {
743 gp = (struct group *)scalloc(1, sizeof *gp);
744 gp->ge_name = sstrdup(*ap);
745 gp->ge_link = gh->g_list;
746 gh->g_list = gp;
748 return(0);
752 * Delete the passed groups.
754 FL int
755 ungroup(void *v)
757 char **argv = v;
759 if (*argv == NULL) {
760 fprintf(stderr, tr(209, "Must specify alias to remove\n"));
761 return 1;
764 remove_group(*argv);
765 while (*++argv != NULL);
766 return 0;
770 * Sort the passed string vecotor into ascending dictionary
771 * order.
773 static void
774 asort(char **list)
776 char **ap;
778 for (ap = list; *ap != NULL; ap++)
780 if (ap-list < 2)
781 return;
782 qsort(list, ap-list, sizeof(*list), diction);
786 * Do a dictionary order comparison of the arguments from
787 * qsort.
789 static int
790 diction(const void *a, const void *b)
792 return(strcmp(*(char**)UNCONST(a), *(char**)UNCONST(b)));
796 * Change to another file. With no argument, print information about
797 * the current file.
799 FL int
800 cfile(void *v)
802 char **argv = v;
803 int i;
805 if (*argv == NULL) {
806 newfileinfo();
807 return 0;
810 if (inhook) {
811 fprintf(stderr, tr(516,
812 "Cannot change folder from within a hook.\n"));
813 return 1;
816 save_mbox_for_possible_quitstuff();
818 i = setfile(*argv, 0);
819 if (i < 0)
820 return 1;
821 callhook(mailname, 0);
822 if (i > 0 && !ok_blook(emptystart))
823 return 1;
824 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
825 return 0;
829 * Expand file names like echo
831 FL int
832 echo(void *v)
834 char const **argv = v, **ap, *cp;
835 int c;
837 for (ap = argv; *ap != NULL; ++ap) {
838 cp = *ap;
839 if ((cp = fexpand(cp, FEXP_NSHORTCUT)) != NULL) {
840 if (ap != argv)
841 putchar(' ');
842 c = 0;
843 while (*cp != '\0' &&
844 (c = expand_shell_escape(&cp, FAL0))
845 > 0)
846 putchar(c);
847 /* \c ends overall processing */
848 if (c < 0)
849 goto jleave;
852 putchar('\n');
853 jleave:
854 return 0;
857 FL int
858 Respond(void *v)
860 return (respond_or_Respond('R'))((int *)v, 0);
863 FL int
864 Followup(void *v)
866 return (respond_or_Respond('R'))((int *)v, 1);
870 * Reply to a series of messages by simply mailing to the senders
871 * and not messing around with the To: and Cc: lists as in normal
872 * reply.
874 static int
875 Respond_internal(int *msgvec, int recipient_record)
877 struct header head;
878 struct message *mp;
879 int *ap;
880 char *cp;
881 enum gfield gf = ok_blook(fullnames) ? GFULL : GSKIN;
883 memset(&head, 0, sizeof head);
885 for (ap = msgvec; *ap != 0; ap++) {
886 mp = &message[*ap - 1];
887 touch(mp);
888 setdot(mp);
889 if ((cp = hfield1("reply-to", mp)) == NULL)
890 if ((cp = hfield1("from", mp)) == NULL)
891 cp = nameof(mp, 2);
892 head.h_to = cat(head.h_to, lextract(cp, GTO | gf));
894 if (head.h_to == NULL)
895 return 0;
897 mp = &message[msgvec[0] - 1];
898 head.h_subject = hfield1("subject", mp);
899 head.h_subject = _reedit(head.h_subject);
900 make_ref_and_cs(mp, &head);
902 if (ok_blook(quote_as_attachment)) {
903 head.h_attach = csalloc(1, sizeof *head.h_attach);
904 head.h_attach->a_msgno = *msgvec;
905 head.h_attach->a_content_description = tr(512,
906 "Original message content");
909 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
910 ok_blook(markanswered) && (mp->m_flag & MANSWERED) == 0)
911 mp->m_flag |= MANSWER | MANSWERED;
912 return 0;
916 * Conditional commands. These allow one to parameterize one's
917 * .mailrc and do some things if sending, others if receiving.
919 FL int
920 ifcmd(void *v)
922 char **argv = v;
923 char *cp;
925 if (cond != CANY) {
926 printf(tr(42, "Illegal nested \"if\"\n"));
927 return(1);
929 cond = CANY;
930 cp = argv[0];
931 switch (*cp) {
932 case 'r': case 'R':
933 cond = CRCV;
934 break;
936 case 's': case 'S':
937 cond = CSEND;
938 break;
940 case 't': case 'T':
941 cond = CTERM;
942 break;
944 default:
945 printf(tr(43, "Unrecognized if-keyword: \"%s\"\n"), cp);
946 return(1);
948 return(0);
952 * Implement 'else'. This is pretty simple -- we just
953 * flip over the conditional flag.
955 /*ARGSUSED*/
956 FL int
957 elsecmd(void *v)
959 (void)v;
961 switch (cond) {
962 case CANY:
963 printf(tr(44, "\"Else\" without matching \"if\"\n"));
964 return(1);
966 case CSEND:
967 cond = CRCV;
968 break;
970 case CRCV:
971 cond = CSEND;
972 break;
974 case CTERM:
975 cond = CNONTERM;
976 break;
978 default:
979 printf(tr(45, "Mail's idea of conditions is screwed up\n"));
980 cond = CANY;
981 break;
983 return(0);
987 * End of if statement. Just set cond back to anything.
989 /*ARGSUSED*/
990 FL int
991 endifcmd(void *v)
993 (void)v;
995 if (cond == CANY) {
996 printf(tr(46, "\"Endif\" without matching \"if\"\n"));
997 return(1);
999 cond = CANY;
1000 return(0);
1004 * Set the list of alternate names.
1006 FL int
1007 alternates(void *v)
1009 size_t l;
1010 char **namelist = v, **ap, **ap2, *cp;
1012 l = argcount(namelist) + 1;
1014 if (l == 1) {
1015 if (altnames == NULL)
1016 goto jleave;
1017 for (ap = altnames; *ap != NULL; ++ap)
1018 printf("%s ", *ap);
1019 printf("\n");
1020 goto jleave;
1023 if (altnames != NULL) {
1024 for (ap = altnames; *ap != NULL; ++ap)
1025 free(*ap);
1026 free(altnames);
1028 altnames = smalloc(l * sizeof(char*));
1029 for (ap = namelist, ap2 = altnames; *ap; ++ap, ++ap2) {
1030 l = strlen(*ap) + 1;
1031 cp = smalloc(l);
1032 memcpy(cp, *ap, l);
1033 *ap2 = cp;
1035 *ap2 = NULL;
1036 jleave:
1037 return (0);
1041 * Do the real work of resending.
1043 static int
1044 resend1(void *v, int add_resent)
1046 char *name, *str;
1047 struct name *to;
1048 struct name *sn;
1049 int *ip, *msgvec;
1050 bool_t f;
1052 str = (char *)v;
1053 /*LINTED*/
1054 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
1055 name = laststring(str, &f, 1);
1056 if (name == NULL) {
1057 puts(tr(47, "No recipient specified."));
1058 return 1;
1060 if (!f) {
1061 *msgvec = first(0, MMNORM);
1062 if (*msgvec == 0) {
1063 if (inhook)
1064 return 0;
1065 puts(tr(48, "No applicable messages."));
1066 return 1;
1068 msgvec[1] = 0;
1069 } else if (getmsglist(str, msgvec, 0) < 0)
1070 return 1;
1071 if (*msgvec == 0) {
1072 if (inhook)
1073 return 0;
1074 printf("No applicable messages.\n");
1075 return 1;
1077 sn = nalloc(name, GTO);
1078 to = usermap(sn, FAL0);
1079 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
1080 if (resend_msg(&message[*ip - 1], to, add_resent) != OKAY)
1081 return 1;
1083 return 0;
1087 * Resend a message list to a third person.
1089 FL int
1090 resendcmd(void *v)
1092 return resend1(v, 1);
1096 * Resend a message list to a third person without adding headers.
1098 FL int
1099 Resendcmd(void *v)
1101 return resend1(v, 0);
1105 * 'newmail' or 'inc' command: Check for new mail without writing old
1106 * mail back.
1108 /*ARGSUSED*/
1109 FL int
1110 newmail(void *v)
1112 int val = 1, mdot;
1113 (void)v;
1115 if (
1116 #ifdef HAVE_IMAP
1117 (mb.mb_type != MB_IMAP || imap_newmail(1)) &&
1118 #endif
1119 (val = setfile(mailname, 1)) == 0) {
1120 mdot = getmdot(1);
1121 setdot(&message[mdot - 1]);
1123 return val;
1126 static void
1127 list_shortcuts(void)
1129 struct shortcut *s;
1131 for (s = shortcuts; s; s = s->sh_next)
1132 printf("%s=%s\n", s->sh_short, s->sh_long);
1135 FL int
1136 shortcut(void *v)
1138 char **args = (char **)v;
1139 struct shortcut *s;
1141 if (args[0] == NULL) {
1142 list_shortcuts();
1143 return 0;
1145 if (args[1] == NULL) {
1146 fprintf(stderr, tr(220,
1147 "expansion name for shortcut missing\n"));
1148 return 1;
1150 if (args[2] != NULL) {
1151 fprintf(stderr, tr(221, "too many arguments\n"));
1152 return 1;
1154 if ((s = get_shortcut(args[0])) != NULL) {
1155 free(s->sh_long);
1156 s->sh_long = sstrdup(args[1]);
1157 } else {
1158 s = scalloc(1, sizeof *s);
1159 s->sh_short = sstrdup(args[0]);
1160 s->sh_long = sstrdup(args[1]);
1161 s->sh_next = shortcuts;
1162 shortcuts = s;
1164 return 0;
1167 FL struct shortcut *
1168 get_shortcut(const char *str)
1170 struct shortcut *s;
1172 for (s = shortcuts; s; s = s->sh_next)
1173 if (strcmp(str, s->sh_short) == 0)
1174 break;
1175 return s;
1178 static enum okay
1179 delete_shortcut(const char *str)
1181 struct shortcut *sp, *sq;
1183 for (sp = shortcuts, sq = NULL; sp; sq = sp, sp = sp->sh_next) {
1184 if (strcmp(sp->sh_short, str) == 0) {
1185 free(sp->sh_short);
1186 free(sp->sh_long);
1187 if (sq)
1188 sq->sh_next = sp->sh_next;
1189 if (sp == shortcuts)
1190 shortcuts = sp->sh_next;
1191 free(sp);
1192 return OKAY;
1195 return STOP;
1198 FL int
1199 unshortcut(void *v)
1201 char **args = (char **)v;
1202 int errs = 0;
1204 if (args[0] == NULL) {
1205 fprintf(stderr, tr(222, "need shortcut names to remove\n"));
1206 return 1;
1208 while (*args != NULL) {
1209 if (delete_shortcut(*args) != OKAY) {
1210 errs = 1;
1211 fprintf(stderr, tr(223, "%s: no such shortcut\n"),
1212 *args);
1214 args++;
1216 return errs;
1219 FL int
1220 cflag(void *v)
1222 struct message *m;
1223 int *msgvec = v;
1224 int *ip;
1226 for (ip = msgvec; *ip != 0; ip++) {
1227 m = &message[*ip-1];
1228 setdot(m);
1229 if ((m->m_flag & (MFLAG|MFLAGGED)) == 0)
1230 m->m_flag |= MFLAG|MFLAGGED;
1232 return 0;
1235 FL int
1236 cunflag(void *v)
1238 struct message *m;
1239 int *msgvec = v;
1240 int *ip;
1242 for (ip = msgvec; *ip != 0; ip++) {
1243 m = &message[*ip-1];
1244 setdot(m);
1245 if (m->m_flag & (MFLAG|MFLAGGED)) {
1246 m->m_flag &= ~(MFLAG|MFLAGGED);
1247 m->m_flag |= MUNFLAG;
1250 return 0;
1253 FL int
1254 canswered(void *v)
1256 struct message *m;
1257 int *msgvec = v;
1258 int *ip;
1260 for (ip = msgvec; *ip != 0; ip++) {
1261 m = &message[*ip-1];
1262 setdot(m);
1263 if ((m->m_flag & (MANSWER|MANSWERED)) == 0)
1264 m->m_flag |= MANSWER|MANSWERED;
1266 return 0;
1269 FL int
1270 cunanswered(void *v)
1272 struct message *m;
1273 int *msgvec = v;
1274 int *ip;
1276 for (ip = msgvec; *ip != 0; ip++) {
1277 m = &message[*ip-1];
1278 setdot(m);
1279 if (m->m_flag & (MANSWER|MANSWERED)) {
1280 m->m_flag &= ~(MANSWER|MANSWERED);
1281 m->m_flag |= MUNANSWER;
1284 return 0;
1287 FL int
1288 cdraft(void *v)
1290 struct message *m;
1291 int *msgvec = v;
1292 int *ip;
1294 for (ip = msgvec; *ip != 0; ip++) {
1295 m = &message[*ip-1];
1296 setdot(m);
1297 if ((m->m_flag & (MDRAFT|MDRAFTED)) == 0)
1298 m->m_flag |= MDRAFT|MDRAFTED;
1300 return 0;
1303 FL int
1304 cundraft(void *v)
1306 struct message *m;
1307 int *msgvec = v;
1308 int *ip;
1310 for (ip = msgvec; *ip != 0; ip++) {
1311 m = &message[*ip-1];
1312 setdot(m);
1313 if (m->m_flag & (MDRAFT|MDRAFTED)) {
1314 m->m_flag &= ~(MDRAFT|MDRAFTED);
1315 m->m_flag |= MUNDRAFT;
1318 return 0;
1321 /*ARGSUSED*/
1322 FL int
1323 cnoop(void *v)
1325 (void)v;
1327 switch (mb.mb_type) {
1328 case MB_IMAP:
1329 #ifdef HAVE_IMAP
1330 imap_noop();
1331 break;
1332 #else
1333 return (ccmdnotsupp(NULL));
1334 #endif
1335 case MB_POP3:
1336 #ifdef HAVE_POP3
1337 pop3_noop();
1338 break;
1339 #else
1340 return (ccmdnotsupp(NULL));
1341 #endif
1342 default:
1343 break;
1345 return 0;
1348 FL int
1349 cremove(void *v)
1351 char vb[LINESIZE];
1352 char **args = v;
1353 char *name;
1354 int ec = 0;
1356 if (*args == NULL) {
1357 fprintf(stderr, tr(290, "Syntax is: remove mailbox ...\n"));
1358 return (1);
1360 do {
1361 if ((name = expand(*args)) == NULL)
1362 continue;
1363 if (strcmp(name, mailname) == 0) {
1364 fprintf(stderr, tr(286,
1365 "Cannot remove current mailbox \"%s\".\n"),
1366 name);
1367 ec |= 1;
1368 continue;
1370 snprintf(vb, sizeof vb, tr(287, "Remove \"%s\" (y/n) ? "),
1371 name);
1372 if (yorn(vb) == 0)
1373 continue;
1374 switch (which_protocol(name)) {
1375 case PROTO_FILE:
1376 if (unlink(name) < 0) { /* do not handle .gz .bz2 */
1377 perror(name);
1378 ec |= 1;
1380 break;
1381 case PROTO_POP3:
1382 fprintf(stderr, tr(288,
1383 "Cannot remove POP3 mailbox \"%s\".\n"),
1384 name);
1385 ec |= 1;
1386 break;
1387 case PROTO_IMAP:
1388 #ifdef HAVE_IMAP
1389 if (imap_remove(name) != OKAY)
1390 #endif
1391 ec |= 1;
1392 break;
1393 case PROTO_MAILDIR:
1394 if (maildir_remove(name) != OKAY)
1395 ec |= 1;
1396 break;
1397 case PROTO_UNKNOWN:
1398 fprintf(stderr, tr(289,
1399 "Unknown protocol in \"%s\". Not removed.\n"),
1400 name);
1401 ec |= 1;
1403 } while (*++args);
1404 return ec;
1407 FL int
1408 crename(void *v)
1410 char **args = v, *old, *new;
1411 enum protocol oldp, newp;
1412 int ec = 0;
1414 if (args[0] == NULL || args[1] == NULL || args[2] != NULL) {
1415 fprintf(stderr, "Syntax: rename old new\n");
1416 return (1);
1419 if ((old = expand(args[0])) == NULL)
1420 return (1);
1421 oldp = which_protocol(old);
1422 if ((new = expand(args[1])) == NULL)
1423 return (1);
1424 newp = which_protocol(new);
1426 if (strcmp(old, mailname) == 0 || strcmp(new, mailname) == 0) {
1427 fprintf(stderr, tr(291,
1428 "Cannot rename current mailbox \"%s\".\n"), old);
1429 return 1;
1431 if ((oldp == PROTO_IMAP || newp == PROTO_IMAP) && oldp != newp) {
1432 fprintf(stderr, tr(292,
1433 "Can only rename folders of same type.\n"));
1434 return 1;
1436 if (newp == PROTO_POP3)
1437 goto nopop3;
1438 switch (oldp) {
1439 case PROTO_FILE:
1440 if (link(old, new) < 0) {
1441 switch (errno) {
1442 case EACCES:
1443 case EEXIST:
1444 case ENAMETOOLONG:
1445 case ENOENT:
1446 case ENOSPC:
1447 case EXDEV:
1448 perror(new);
1449 break;
1450 default:
1451 perror(old);
1453 ec |= 1;
1454 } else if (unlink(old) < 0) {
1455 perror(old);
1456 ec |= 1;
1458 break;
1459 case PROTO_MAILDIR:
1460 if (rename(old, new) < 0) {
1461 perror(old);
1462 ec |= 1;
1464 break;
1465 case PROTO_POP3:
1466 nopop3: fprintf(stderr, tr(293, "Cannot rename POP3 mailboxes.\n"));
1467 ec |= 1;
1468 break;
1469 #ifdef HAVE_IMAP
1470 case PROTO_IMAP:
1471 if (imap_rename(old, new) != OKAY)
1472 ec |= 1;
1473 break;
1474 #endif
1475 case PROTO_UNKNOWN:
1476 default:
1477 fprintf(stderr, tr(294,
1478 "Unknown protocol in \"%s\" and \"%s\". "
1479 "Not renamed.\n"), old, new);
1480 ec |= 1;
1482 return ec;