Rename occurrences of USE_ to HAVE_ (again)
[s-mailx.git] / cmd3.c
blob0547e97c2703ca4828d6fdb043dbe82a0106b516
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 #include "rcv.h"
42 #include <errno.h>
43 #include <math.h>
44 #include <unistd.h>
46 #include "extern.h"
48 /* Modify subject we reply to to begin with Re: if it does not already */
49 static char * _reedit(char *subj);
51 /* "set" command: show all option settings */
52 static int _set_show_all(void);
54 static int bangexp(char **str, size_t *size);
55 static void make_ref_and_cs(struct message *mp, struct header *head);
56 static int (* respond_or_Respond(int c))(int *, int);
57 static int respond_internal(int *msgvec, int recipient_record);
58 static char * fwdedit(char *subj);
59 static void asort(char **list);
60 static int diction(const void *a, const void *b);
61 static int file1(char const *name);
62 static int Respond_internal(int *msgvec, int recipient_record);
63 static int resend1(void *v, int add_resent);
64 static void list_shortcuts(void);
65 static enum okay delete_shortcut(const char *str);
67 static char *
68 _reedit(char *subj)
70 struct str in, out;
71 char *newsubj = NULL;
73 if (subj == NULL || *subj == '\0')
74 goto j_leave;
76 in.s = subj;
77 in.l = strlen(subj);
78 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
80 if ((out.s[0] == 'r' || out.s[0] == 'R') &&
81 (out.s[1] == 'e' || out.s[1] == 'E') &&
82 out.s[2] == ':') {
83 newsubj = savestr(out.s);
84 goto jleave;
86 newsubj = salloc(out.l + 5);
87 sstpcpy(sstpcpy(newsubj, "Re: "), out.s);
88 jleave:
89 free(out.s);
90 j_leave:
91 return (newsubj);
94 static int
95 _set_show_all(void)
97 int ret = 1;
98 FILE *fp;
99 char *cp, **vacp, **p;
100 struct var *vp;
101 size_t i;
102 union {size_t j; char const *fmt;} u;
104 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
105 perror("tmpfile");
106 goto jleave;
108 rm(cp);
109 Ftfree(&cp);
111 for (u.j = 1, i = 0; i < HSHSIZE; ++i)
112 for (vp = variables[i]; vp != NULL; vp = vp->v_link)
113 ++u.j;
114 vacp = (char**)salloc(u.j * sizeof(*vacp));
115 for (p = vacp, i = 0; i < HSHSIZE; ++i)
116 for (vp = variables[i]; vp != NULL; vp = vp->v_link)
117 *p++ = vp->v_name;
118 *p = NULL;
120 asort(vacp);
122 i = (value("bsdcompat") != NULL || value("bsdset") != NULL);
123 u.fmt = i ? "%s\t%s\n" : "%s=\"%s\"\n";
124 for (p = vacp; *p != NULL; ++p) {
125 char const *x = value(*p);
126 if (x == NULL)
127 x = "";
128 if (i || *x)
129 fprintf(fp, u.fmt, *p, x);
130 else
131 fprintf(fp, "%s\n", *p);
134 page_or_print(fp, (size_t)(p - vacp));
135 Fclose(fp);
136 ret = 0;
137 jleave:
138 return (ret);
142 * Process a shell escape by saving signals, ignoring signals,
143 * and forking a sh -c
145 int
146 shell(void *v)
148 char *str = v, *cmd;
149 char const *shell;
150 size_t cmdsize;
151 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
153 cmd = smalloc(cmdsize = strlen(str) + 1);
154 memcpy(cmd, str, cmdsize);
155 if (bangexp(&cmd, &cmdsize) < 0)
156 return 1;
157 if ((shell = value("SHELL")) == NULL)
158 shell = SHELL;
159 run_command(shell, 0, -1, -1, "-c", cmd, NULL);
160 safe_signal(SIGINT, sigint);
161 printf("!\n");
162 free(cmd);
163 return 0;
167 * Fork an interactive shell.
169 /*ARGSUSED*/
170 int
171 dosh(void *v)
173 sighandler_type sigint = safe_signal(SIGINT, SIG_IGN);
174 char const *shell;
175 (void)v;
177 if ((shell = value("SHELL")) == NULL)
178 shell = SHELL;
179 run_command(shell, 0, -1, -1, NULL, NULL, NULL);
180 safe_signal(SIGINT, sigint);
181 putchar('\n');
182 return 0;
186 * Expand the shell escape by expanding unescaped !'s into the
187 * last issued command where possible.
190 static char *lastbang;
191 static size_t lastbangsize;
193 static int
194 bangexp(char **str, size_t *size)
196 char *bangbuf;
197 int changed = 0;
198 int dobang = value("bang") != NULL;
199 size_t sz, i, j, bangbufsize;
201 bangbuf = smalloc(bangbufsize = *size);
202 i = j = 0;
203 while ((*str)[i]) {
204 if (dobang) {
205 if ((*str)[i] == '!') {
206 sz = strlen(lastbang);
207 bangbuf = srealloc(bangbuf, bangbufsize += sz);
208 changed++;
209 memcpy(bangbuf + j, lastbang, sz + 1);
210 j += sz;
211 i++;
212 continue;
215 if ((*str)[i] == '\\' && (*str)[i + 1] == '!') {
216 bangbuf[j++] = '!';
217 i += 2;
218 changed++;
220 bangbuf[j++] = (*str)[i++];
222 bangbuf[j] = '\0';
223 if (changed) {
224 printf("!%s\n", bangbuf);
225 fflush(stdout);
227 sz = j + 1;
228 if (sz > *size)
229 *str = srealloc(*str, *size = sz);
230 memcpy(*str, bangbuf, sz);
231 if (sz > lastbangsize)
232 lastbang = srealloc(lastbang, lastbangsize = sz);
233 memcpy(lastbang, bangbuf, sz);
234 free(bangbuf);
235 return 0;
238 /*ARGSUSED*/
239 int
240 help(void *v)
242 int ret = 0;
243 char *arg = *(char**)v;
245 if (arg != NULL) {
246 #ifdef HAVE_DOCSTRINGS
247 extern struct cmd const cmdtab[];
248 struct cmd const *cp;
249 for (cp = cmdtab; cp->c_name != NULL; ++cp) {
250 if (cp->c_func == &ccmdnotsupp)
251 continue;
252 if (strcmp(arg, cp->c_name) == 0)
253 printf("%s: %s\n", arg,
254 tr(cp->c_docid, cp->c_doc));
255 else if (is_prefix(arg, cp->c_name))
256 printf("%s (%s): %s\n", arg, cp->c_name,
257 tr(cp->c_docid, cp->c_doc));
258 else
259 continue;
260 goto jleave;
262 fprintf(stderr, tr(91, "Unknown command: \"%s\"\n"), arg);
263 ret = 1;
264 #else
265 ret = ccmdnotsupp(NULL);
266 #endif
267 goto jleave;
270 /* Very ugly, but take care for compiler supported string lengths :( */
271 printf(tr(295, "%s commands:\n"), progname);
272 puts(tr(296,
273 "type <message list> type messages\n"
274 "next goto and type next message\n"
275 "from <message list> give head lines of messages\n"
276 "headers print out active message headers\n"
277 "delete <message list> delete messages\n"
278 "undelete <message list> undelete messages\n"));
279 puts(tr(297,
280 "save <message list> folder append messages to folder and mark as saved\n"
281 "copy <message list> folder append messages to folder without marking them\n"
282 "write <message list> file append message texts to file, save attachments\n"
283 "preserve <message list> keep incoming messages in mailbox even if saved\n"
284 "Reply <message list> reply to message senders\n"
285 "reply <message list> reply to message senders and all recipients\n"));
286 puts(tr(298,
287 "mail addresses mail to specific recipients\n"
288 "file folder change to another folder\n"
289 "quit quit and apply changes to folder\n"
290 "xit quit and discard changes made to folder\n"
291 "! shell escape\n"
292 "cd <directory> chdir to directory or home if none given\n"
293 "list list names of all available commands\n"));
294 fprintf(stdout, tr(299,
295 "\nA <message list> consists of integers, ranges of same, or other criteria\n"
296 "separated by spaces. If omitted, %s uses the last message typed.\n"),
297 progname);
299 jleave:
300 return (ret);
304 * Change user's working directory.
306 int
307 schdir(void *v)
309 char **arglist = v;
310 char const *cp;
312 if (*arglist == NULL)
313 cp = homedir;
314 else if ((cp = file_expand(*arglist)) == NULL)
315 goto jleave;
316 if (chdir(cp) < 0) {
317 perror(cp);
318 cp = NULL;
320 jleave:
321 return (cp != NULL);
324 static void
325 make_ref_and_cs(struct message *mp, struct header *head)
327 char *oldref, *oldmsgid, *newref, *cp;
328 size_t oldreflen = 0, oldmsgidlen = 0, reflen;
329 unsigned i;
330 struct name *n;
332 oldref = hfield1("references", mp);
333 oldmsgid = hfield1("message-id", mp);
334 if (oldmsgid == NULL || *oldmsgid == '\0') {
335 head->h_ref = NULL;
336 return;
338 reflen = 1;
339 if (oldref) {
340 oldreflen = strlen(oldref);
341 reflen += oldreflen + 2;
343 if (oldmsgid) {
344 oldmsgidlen = strlen(oldmsgid);
345 reflen += oldmsgidlen;
348 newref = ac_alloc(reflen);
349 if (oldref != NULL) {
350 memcpy(newref, oldref, oldreflen + 1);
351 if (oldmsgid != NULL) {
352 newref[oldreflen++] = ',';
353 newref[oldreflen++] = ' ';
354 memcpy(newref + oldreflen, oldmsgid, oldmsgidlen + 1);
356 } else if (oldmsgid)
357 memcpy(newref, oldmsgid, oldmsgidlen + 1);
358 n = extract(newref, GREF);
359 ac_free(newref);
362 * Limit the references to 21 entries.
364 while (n->n_flink != NULL)
365 n = n->n_flink;
366 for (i = 1; i < 21; i++) {
367 if (n->n_blink != NULL)
368 n = n->n_blink;
369 else
370 break;
372 n->n_blink = NULL;
373 head->h_ref = n;
374 if (value("reply-in-same-charset") != NULL &&
375 (cp = hfield1("content-type", mp)) != NULL)
376 head->h_charset = mime_getparam("charset", cp);
379 static int
380 (*respond_or_Respond(int c))(int *, int)
382 int opt = 0;
384 opt += (value("Replyall") != NULL);
385 opt += (value("flipr") != NULL);
386 return ((opt == 1) ^ (c == 'R')) ? Respond_internal : respond_internal;
389 int
390 respond(void *v)
392 return (respond_or_Respond('r'))((int *)v, 0);
395 int
396 respondall(void *v)
398 return respond_internal((int *)v, 0);
401 int
402 respondsender(void *v)
404 return Respond_internal((int *)v, 0);
407 int
408 followup(void *v)
410 return (respond_or_Respond('r'))((int *)v, 1);
413 int
414 followupall(void *v)
416 return respond_internal((int *)v, 1);
419 int
420 followupsender(void *v)
422 return Respond_internal((int *)v, 1);
426 * Reply to a single message. Extract each name from the
427 * message header and send them off to mail1()
429 static int
430 respond_internal(int *msgvec, int recipient_record)
432 struct header head;
433 struct message *mp;
434 char *cp, *rcv;
435 struct name *np = NULL;
436 enum gfield gf = boption("fullnames") ? GFULL : GSKIN;
438 if (msgvec[1] != 0) {
439 fprintf(stderr, tr(37,
440 "Sorry, can't reply to multiple messages at once\n"));
441 return 1;
443 mp = &message[msgvec[0] - 1];
444 touch(mp);
445 setdot(mp);
447 if ((rcv = hfield1("reply-to", mp)) == NULL)
448 if ((rcv = hfield1("from", mp)) == NULL)
449 rcv = nameof(mp, 1);
450 if (rcv != NULL)
451 np = lextract(rcv, GTO|gf);
452 if (! boption("recipients-in-cc") && (cp = hfield1("to", mp)) != NULL)
453 np = cat(np, lextract(cp, GTO | gf));
455 * Delete my name from the reply list,
456 * and with it, all my alternate names.
458 np = elide(delete_alternates(np));
459 if (np == NULL)
460 np = lextract(rcv, GTO | gf);
462 memset(&head, 0, sizeof head);
463 head.h_to = np;
464 head.h_subject = hfield1("subject", mp);
465 head.h_subject = _reedit(head.h_subject);
466 /* Cc: */
467 np = NULL;
468 if (boption("recipients-in-cc") && (cp = hfield1("to", mp)) != NULL)
469 np = lextract(cp, GCC | gf);
470 if ((cp = hfield1("cc", mp)) != NULL)
471 np = cat(np, lextract(cp, GCC | gf));
472 if (np != NULL)
473 head.h_cc = elide(delete_alternates(np));
474 make_ref_and_cs(mp, &head);
476 if (boption("quote-as-attachment")) {
477 head.h_attach = csalloc(1, sizeof *head.h_attach);
478 head.h_attach->a_msgno = *msgvec;
479 head.h_attach->a_content_description = tr(512,
480 "Original message content");
483 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
484 boption("markanswered") &&
485 (mp->m_flag & MANSWERED) == 0)
486 mp->m_flag |= MANSWER | MANSWERED;
487 return 0;
491 * Forward a message to a new recipient, in the sense of RFC 2822.
493 static int
494 forward1(char *str, int recipient_record)
496 int *msgvec, f;
497 char *recipient;
498 struct message *mp;
499 struct header head;
500 bool_t forward_as_attachment;
502 forward_as_attachment = boption("forward-as-attachment");
503 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
504 if ((recipient = laststring(str, &f, 0)) == NULL) {
505 puts(catgets(catd, CATSET, 47, "No recipient specified."));
506 return 1;
508 if (!f) {
509 *msgvec = first(0, MMNORM);
510 if (*msgvec == 0) {
511 if (inhook)
512 return 0;
513 printf("No messages to forward.\n");
514 return 1;
516 msgvec[1] = 0;
518 if (f && getmsglist(str, msgvec, 0) < 0)
519 return 1;
520 if (*msgvec == 0) {
521 if (inhook)
522 return 0;
523 printf("No applicable messages.\n");
524 return 1;
526 if (msgvec[1] != 0) {
527 printf("Cannot forward multiple messages at once\n");
528 return 1;
530 memset(&head, 0, sizeof head);
531 if ((head.h_to = lextract(recipient,
532 GTO | (value("fullnames") ? GFULL : GSKIN))) == NULL)
533 return 1;
534 mp = &message[*msgvec - 1];
535 if (forward_as_attachment) {
536 head.h_attach = csalloc(1, sizeof *head.h_attach);
537 head.h_attach->a_msgno = *msgvec;
538 head.h_attach->a_content_description = "Forwarded message";
539 } else {
540 touch(mp);
541 setdot(mp);
543 head.h_subject = hfield1("subject", mp);
544 head.h_subject = fwdedit(head.h_subject);
545 mail1(&head, 1, (forward_as_attachment ? NULL : mp),
546 NULL, recipient_record, 1);
547 return 0;
551 * Modify the subject we are replying to to begin with Fwd:.
553 static char *
554 fwdedit(char *subj)
556 struct str in, out;
557 char *newsubj;
559 if (subj == NULL || *subj == '\0')
560 return NULL;
561 in.s = subj;
562 in.l = strlen(subj);
563 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
565 newsubj = salloc(out.l + 6);
566 memcpy(newsubj, "Fwd: ", 5);
567 memcpy(newsubj + 5, out.s, out.l + 1);
568 free(out.s);
569 return newsubj;
573 * The 'forward' command.
576 forwardcmd(void *v)
578 return forward1(v, 0);
582 * Similar to forward, saving the message in a file named after the
583 * first recipient.
586 Forwardcmd(void *v)
588 return forward1(v, 1);
592 * Preserve the named messages, so that they will be sent
593 * back to the system mailbox.
595 int
596 preserve(void *v)
598 int *msgvec = v;
599 struct message *mp;
600 int *ip, mesg;
602 if (edit) {
603 printf(catgets(catd, CATSET, 39,
604 "Cannot \"preserve\" in edit mode\n"));
605 return(1);
607 for (ip = msgvec; *ip != 0; ip++) {
608 mesg = *ip;
609 mp = &message[mesg-1];
610 mp->m_flag |= MPRESERVE;
611 mp->m_flag &= ~MBOX;
612 setdot(mp);
614 * This is now Austin Group Request XCU #20.
616 did_print_dot = TRU1;
618 return(0);
622 * Mark all given messages as unread.
624 int
625 unread(void *v)
627 int *msgvec = v;
628 int *ip;
630 for (ip = msgvec; *ip != 0; ip++) {
631 setdot(&message[*ip-1]);
632 dot->m_flag &= ~(MREAD|MTOUCH);
633 dot->m_flag |= MSTATUS;
634 #ifdef HAVE_IMAP
635 if (mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE)
636 imap_unread(&message[*ip-1], *ip); /* TODO return? */
637 #endif
639 * The "unread" command is not part of POSIX mailx.
641 did_print_dot = TRU1;
643 return(0);
647 * Mark all given messages as read.
650 seen(void *v)
652 int *msgvec = v;
653 int *ip;
655 for (ip = msgvec; *ip; ip++) {
656 setdot(&message[*ip-1]);
657 touch(&message[*ip-1]);
659 return 0;
663 * Print the size of each message.
665 int
666 messize(void *v)
668 int *msgvec = v;
669 struct message *mp;
670 int *ip, mesg;
672 for (ip = msgvec; *ip != 0; ip++) {
673 mesg = *ip;
674 mp = &message[mesg-1];
675 printf("%d: ", mesg);
676 if (mp->m_xlines > 0)
677 printf("%ld", mp->m_xlines);
678 else
679 putchar(' ');
680 printf("/%lu\n", (unsigned long)mp->m_xsize);
682 return(0);
686 * Quit quickly. If we are sourcing, just pop the input level
687 * by returning an error.
689 /*ARGSUSED*/
690 int
691 rexit(void *v)
693 (void)v;
694 if (sourcing)
695 return(1);
696 exit(0);
697 /*NOTREACHED*/
700 int
701 set(void *v)
703 char **ap = v, *cp, *cp2, *varbuf, c;
704 int errs = 0;
706 if (*ap == NULL) {
707 _set_show_all();
708 goto jleave;
711 for (; *ap != NULL; ++ap) {
712 cp = *ap;
713 cp2 = varbuf = ac_alloc(strlen(cp) + 1);
714 for (; (c = *cp) != '=' && c != '\0'; ++cp)
715 *cp2++ = c;
716 *cp2 = '\0';
717 if (c == '\0')
718 cp = UNCONST("");
719 else
720 ++cp;
721 if (varbuf == cp2) {
722 fprintf(stderr,
723 tr(41, "Non-null variable name required\n"));
724 ++errs;
725 goto jnext;
727 if (varbuf[0] == 'n' && varbuf[1] == 'o')
728 errs += unset_internal(&varbuf[2]);
729 else
730 assign(varbuf, cp);
731 jnext: ac_free(varbuf);
733 jleave:
734 return (errs);
738 * Unset a bunch of variable values.
740 int
741 unset(void *v)
743 int errs;
744 char **ap;
746 errs = 0;
747 for (ap = (char **)v; *ap != NULL; ap++)
748 errs += unset_internal(*ap);
749 return(errs);
753 * Put add users to a group.
755 int
756 group(void *v)
758 char **argv = v;
759 struct grouphead *gh;
760 struct group *gp;
761 int h;
762 int s;
763 char **ap, *gname, **p;
765 if (*argv == NULL) {
766 for (h = 0, s = 1; h < HSHSIZE; h++)
767 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
768 s++;
769 /*LINTED*/
770 ap = (char **)salloc(s * sizeof *ap);
771 for (h = 0, p = ap; h < HSHSIZE; h++)
772 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
773 *p++ = gh->g_name;
774 *p = NULL;
775 asort(ap);
776 for (p = ap; *p != NULL; p++)
777 printgroup(*p);
778 return(0);
780 if (argv[1] == NULL) {
781 printgroup(*argv);
782 return(0);
784 gname = *argv;
785 h = hash(gname);
786 if ((gh = findgroup(gname)) == NULL) {
787 gh = (struct grouphead *)scalloc(1, sizeof *gh);
788 gh->g_name = sstrdup(gname);
789 gh->g_list = NULL;
790 gh->g_link = groups[h];
791 groups[h] = gh;
795 * Insert names from the command list into the group.
796 * Who cares if there are duplicates? They get tossed
797 * later anyway.
800 for (ap = argv+1; *ap != NULL; ap++) {
801 gp = (struct group *)scalloc(1, sizeof *gp);
802 gp->ge_name = sstrdup(*ap);
803 gp->ge_link = gh->g_list;
804 gh->g_list = gp;
806 return(0);
810 * Delete the passed groups.
812 int
813 ungroup(void *v)
815 char **argv = v;
817 if (*argv == NULL) {
818 fprintf(stderr, tr(209, "Must specify alias to remove\n"));
819 return 1;
822 remove_group(*argv);
823 while (*++argv != NULL);
824 return 0;
828 * Sort the passed string vecotor into ascending dictionary
829 * order.
831 static void
832 asort(char **list)
834 char **ap;
836 for (ap = list; *ap != NULL; ap++)
838 if (ap-list < 2)
839 return;
840 qsort(list, ap-list, sizeof(*list), diction);
844 * Do a dictionary order comparison of the arguments from
845 * qsort.
847 static int
848 diction(const void *a, const void *b)
850 return(strcmp(*(char**)UNCONST(a), *(char**)UNCONST(b)));
854 * Change to another file. With no argument, print information about
855 * the current file.
857 int
858 cfile(void *v)
860 char **argv = v;
861 #if 1 /* TODO this & expansion is completely redundant! */
862 char *exp;
863 #endif
864 if (argv[0] == NULL) {
865 newfileinfo();
866 return 0;
868 #if 1
869 if ((exp = expand("&")) == NULL)
870 return 0;
871 strncpy(mboxname, exp, sizeof mboxname)[sizeof mboxname - 1] = '\0';
872 #endif
873 return file1(*argv);
876 static int
877 file1(char const *name)
879 int i;
881 if (inhook) {
882 fprintf(stderr, "Cannot change folder from within a hook.\n");
883 return 1;
885 i = setfile(name, 0);
886 if (i < 0)
887 return 1;
888 callhook(mailname, 0);
889 if (i > 0 && value("emptystart") == NULL)
890 return 1;
891 announce(value("bsdcompat") != NULL || value("bsdannounce") != NULL);
892 return 0;
897 * Expand file names like echo
899 int
900 echo(void *v)
902 char const **argv = v, **ap, *cp;
903 int c;
905 for (ap = argv; *ap != NULL; ++ap) {
906 cp = *ap;
907 if ((cp = fexpand(cp, FEXP_NSHORTCUT)) != NULL) {
908 if (ap != argv)
909 putchar(' ');
910 while (*cp != '\0' &&
911 (c = expand_shell_escape(&cp)) > 0)
912 putchar(c);
913 /* \c ends overall processing */
914 if (c < 0)
915 goto jleave;
918 putchar('\n');
919 jleave:
920 return 0;
923 int
924 Respond(void *v)
926 return (respond_or_Respond('R'))((int *)v, 0);
929 int
930 Followup(void *v)
932 return (respond_or_Respond('R'))((int *)v, 1);
936 * Reply to a series of messages by simply mailing to the senders
937 * and not messing around with the To: and Cc: lists as in normal
938 * reply.
940 static int
941 Respond_internal(int *msgvec, int recipient_record)
943 struct header head;
944 struct message *mp;
945 int *ap;
946 char *cp;
947 enum gfield gf = boption("fullnames") ? GFULL : GSKIN;
949 memset(&head, 0, sizeof head);
951 for (ap = msgvec; *ap != 0; ap++) {
952 mp = &message[*ap - 1];
953 touch(mp);
954 setdot(mp);
955 if ((cp = hfield1("reply-to", mp)) == NULL)
956 if ((cp = hfield1("from", mp)) == NULL)
957 cp = nameof(mp, 2);
958 head.h_to = cat(head.h_to, lextract(cp, GTO | gf));
960 if (head.h_to == NULL)
961 return 0;
963 mp = &message[msgvec[0] - 1];
964 head.h_subject = hfield1("subject", mp);
965 head.h_subject = _reedit(head.h_subject);
966 make_ref_and_cs(mp, &head);
968 if (boption("quote-as-attachment")) {
969 head.h_attach = csalloc(1, sizeof *head.h_attach);
970 head.h_attach->a_msgno = *msgvec;
971 head.h_attach->a_content_description = tr(512,
972 "Original message content");
975 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
976 value("markanswered") && (mp->m_flag & MANSWERED) == 0)
977 mp->m_flag |= MANSWER | MANSWERED;
978 return 0;
982 * Conditional commands. These allow one to parameterize one's
983 * .mailrc and do some things if sending, others if receiving.
985 int
986 ifcmd(void *v)
988 char **argv = v;
989 char *cp;
991 if (cond != CANY) {
992 printf(catgets(catd, CATSET, 42, "Illegal nested \"if\"\n"));
993 return(1);
995 cond = CANY;
996 cp = argv[0];
997 switch (*cp) {
998 case 'r': case 'R':
999 cond = CRCV;
1000 break;
1002 case 's': case 'S':
1003 cond = CSEND;
1004 break;
1006 case 't': case 'T':
1007 cond = CTERM;
1008 break;
1010 default:
1011 printf(catgets(catd, CATSET, 43,
1012 "Unrecognized if-keyword: \"%s\"\n"), cp);
1013 return(1);
1015 return(0);
1019 * Implement 'else'. This is pretty simple -- we just
1020 * flip over the conditional flag.
1022 /*ARGSUSED*/
1023 int
1024 elsecmd(void *v)
1026 (void)v;
1028 switch (cond) {
1029 case CANY:
1030 printf(catgets(catd, CATSET, 44,
1031 "\"Else\" without matching \"if\"\n"));
1032 return(1);
1034 case CSEND:
1035 cond = CRCV;
1036 break;
1038 case CRCV:
1039 cond = CSEND;
1040 break;
1042 case CTERM:
1043 cond = CNONTERM;
1044 break;
1046 default:
1047 printf(catgets(catd, CATSET, 45,
1048 "Mail's idea of conditions is screwed up\n"));
1049 cond = CANY;
1050 break;
1052 return(0);
1056 * End of if statement. Just set cond back to anything.
1058 /*ARGSUSED*/
1059 int
1060 endifcmd(void *v)
1062 (void)v;
1064 if (cond == CANY) {
1065 printf(catgets(catd, CATSET, 46,
1066 "\"Endif\" without matching \"if\"\n"));
1067 return(1);
1069 cond = CANY;
1070 return(0);
1074 * Set the list of alternate names.
1076 int
1077 alternates(void *v)
1079 size_t l;
1080 char **namelist = v, **ap, **ap2, *cp;
1082 l = argcount(namelist) + 1;
1084 if (l == 1) {
1085 if (altnames == NULL)
1086 goto jleave;
1087 for (ap = altnames; *ap != NULL; ++ap)
1088 printf("%s ", *ap);
1089 printf("\n");
1090 goto jleave;
1093 if (altnames != NULL) {
1094 for (ap = altnames; *ap != NULL; ++ap)
1095 free(*ap);
1096 free(altnames);
1098 altnames = smalloc(l * sizeof(char*));
1099 for (ap = namelist, ap2 = altnames; *ap; ++ap, ++ap2) {
1100 l = strlen(*ap) + 1;
1101 cp = smalloc(l);
1102 memcpy(cp, *ap, l);
1103 *ap2 = cp;
1105 *ap2 = NULL;
1106 jleave:
1107 return (0);
1111 * Do the real work of resending.
1113 static int
1114 resend1(void *v, int add_resent)
1116 char *name, *str;
1117 struct name *to;
1118 struct name *sn;
1119 int f, *ip, *msgvec;
1121 str = (char *)v;
1122 /*LINTED*/
1123 msgvec = (int *)salloc((msgCount + 2) * sizeof *msgvec);
1124 name = laststring(str, &f, 1);
1125 if (name == NULL) {
1126 puts(catgets(catd, CATSET, 47, "No recipient specified."));
1127 return 1;
1129 if (!f) {
1130 *msgvec = first(0, MMNORM);
1131 if (*msgvec == 0) {
1132 if (inhook)
1133 return 0;
1134 puts(catgets(catd, CATSET, 48,
1135 "No applicable messages."));
1136 return 1;
1138 msgvec[1] = 0;
1139 } else if (getmsglist(str, msgvec, 0) < 0)
1140 return 1;
1141 if (*msgvec == 0) {
1142 if (inhook)
1143 return 0;
1144 printf("No applicable messages.\n");
1145 return 1;
1147 sn = nalloc(name, GTO);
1148 to = usermap(sn, FAL0);
1149 for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
1150 if (resend_msg(&message[*ip - 1], to, add_resent) != OKAY)
1151 return 1;
1153 return 0;
1157 * Resend a message list to a third person.
1159 int
1160 resendcmd(void *v)
1162 return resend1(v, 1);
1166 * Resend a message list to a third person without adding headers.
1168 int
1169 Resendcmd(void *v)
1171 return resend1(v, 0);
1175 * 'newmail' or 'inc' command: Check for new mail without writing old
1176 * mail back.
1178 /*ARGSUSED*/
1179 int
1180 newmail(void *v)
1182 int val = 1, mdot;
1183 (void)v;
1185 if (
1186 #ifdef HAVE_IMAP
1187 (mb.mb_type != MB_IMAP || imap_newmail(1)) &&
1188 #endif
1189 (val = setfile(mailname, 1)) == 0) {
1190 mdot = getmdot(1);
1191 setdot(&message[mdot - 1]);
1193 return val;
1196 static void
1197 list_shortcuts(void)
1199 struct shortcut *s;
1201 for (s = shortcuts; s; s = s->sh_next)
1202 printf("%s=%s\n", s->sh_short, s->sh_long);
1205 int
1206 shortcut(void *v)
1208 char **args = (char **)v;
1209 struct shortcut *s;
1211 if (args[0] == NULL) {
1212 list_shortcuts();
1213 return 0;
1215 if (args[1] == NULL) {
1216 fprintf(stderr, catgets(catd, CATSET, 220,
1217 "expansion name for shortcut missing\n"));
1218 return 1;
1220 if (args[2] != NULL) {
1221 fprintf(stderr, catgets(catd, CATSET, 221,
1222 "too many arguments\n"));
1223 return 1;
1225 if ((s = get_shortcut(args[0])) != NULL) {
1226 free(s->sh_long);
1227 s->sh_long = sstrdup(args[1]);
1228 } else {
1229 s = scalloc(1, sizeof *s);
1230 s->sh_short = sstrdup(args[0]);
1231 s->sh_long = sstrdup(args[1]);
1232 s->sh_next = shortcuts;
1233 shortcuts = s;
1235 return 0;
1238 struct shortcut *
1239 get_shortcut(const char *str)
1241 struct shortcut *s;
1243 for (s = shortcuts; s; s = s->sh_next)
1244 if (strcmp(str, s->sh_short) == 0)
1245 break;
1246 return s;
1249 static enum okay
1250 delete_shortcut(const char *str)
1252 struct shortcut *sp, *sq;
1254 for (sp = shortcuts, sq = NULL; sp; sq = sp, sp = sp->sh_next) {
1255 if (strcmp(sp->sh_short, str) == 0) {
1256 free(sp->sh_short);
1257 free(sp->sh_long);
1258 if (sq)
1259 sq->sh_next = sp->sh_next;
1260 if (sp == shortcuts)
1261 shortcuts = sp->sh_next;
1262 free(sp);
1263 return OKAY;
1266 return STOP;
1269 int
1270 unshortcut(void *v)
1272 char **args = (char **)v;
1273 int errs = 0;
1275 if (args[0] == NULL) {
1276 fprintf(stderr, catgets(catd, CATSET, 222,
1277 "need shortcut names to remove\n"));
1278 return 1;
1280 while (*args != NULL) {
1281 if (delete_shortcut(*args) != OKAY) {
1282 errs = 1;
1283 fprintf(stderr, catgets(catd, CATSET, 223,
1284 "%s: no such shortcut\n"), *args);
1286 args++;
1288 return errs;
1291 struct oldaccount {
1292 struct oldaccount *ac_next; /* next account in list */
1293 char *ac_name; /* name of account */
1294 char **ac_vars; /* variables to set */
1297 static struct oldaccount *oldaccounts;
1299 struct oldaccount *
1300 get_oldaccount(const char *name)
1302 struct oldaccount *a;
1304 for (a = oldaccounts; a; a = a->ac_next)
1305 if (a->ac_name && strcmp(name, a->ac_name) == 0)
1306 break;
1307 return a;
1310 int
1311 account(void *v)
1313 char **args = (char **)v;
1314 struct oldaccount *a;
1315 char *cp;
1316 int i, mc, oqf, nqf;
1318 if (args[0] == NULL) {
1319 FILE *fp;
1320 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
1321 perror("tmpfile");
1322 return 1;
1324 rm(cp);
1325 Ftfree(&cp);
1326 mc = listaccounts(fp);
1327 for (a = oldaccounts; a; a = a->ac_next)
1328 if (a->ac_name) {
1329 if (mc++)
1330 fputc('\n', fp);
1331 fprintf(fp, "%s:\n", a->ac_name);
1332 for (i = 0; a->ac_vars[i]; i++)
1333 fprintf(fp, "\t%s\n", a->ac_vars[i]);
1335 if (mc)
1336 try_pager(fp);
1337 Fclose(fp);
1338 return 0;
1340 if (args[1] && args[1][0] == '{' && args[1][1] == '\0') {
1341 if (args[2] != NULL) {
1342 fprintf(stderr, "Syntax is: account <name> {\n");
1343 return 1;
1345 if ((a = get_oldaccount(args[0])) != NULL)
1346 a->ac_name = NULL;
1347 return define1(args[0], 1);
1350 if ((cp = expand("&")) == NULL)
1351 return (1);
1352 strncpy(mboxname, cp, sizeof mboxname)[sizeof mboxname - 1] = '\0';
1354 oqf = savequitflags();
1355 if ((a = get_oldaccount(args[0])) == NULL) {
1356 if (args[1]) {
1357 a = scalloc(1, sizeof *a);
1358 a->ac_next = oldaccounts;
1359 oldaccounts = a;
1360 } else {
1361 if ((i = callaccount(args[0])) != CBAD)
1362 goto setf;
1363 printf("Account %s does not exist.\n", args[0]);
1364 return 1;
1367 if (args[1]) {
1368 delaccount(args[0]);
1369 a->ac_name = sstrdup(args[0]);
1370 for (i = 1; args[i]; i++);
1371 a->ac_vars = scalloc(i, sizeof *a->ac_vars);
1372 for (i = 0; args[i+1]; i++)
1373 a->ac_vars[i] = sstrdup(args[i+1]);
1374 } else {
1375 unset_allow_undefined = TRU1;
1376 set(a->ac_vars);
1377 unset_allow_undefined = FAL0;
1378 setf: if (!starting) {
1379 nqf = savequitflags();
1380 restorequitflags(oqf);
1381 i = file1("%");
1382 restorequitflags(nqf);
1383 return i;
1386 return 0;
1389 int
1390 cflag(void *v)
1392 struct message *m;
1393 int *msgvec = v;
1394 int *ip;
1396 for (ip = msgvec; *ip != 0; ip++) {
1397 m = &message[*ip-1];
1398 setdot(m);
1399 if ((m->m_flag & (MFLAG|MFLAGGED)) == 0)
1400 m->m_flag |= MFLAG|MFLAGGED;
1402 return 0;
1405 int
1406 cunflag(void *v)
1408 struct message *m;
1409 int *msgvec = v;
1410 int *ip;
1412 for (ip = msgvec; *ip != 0; ip++) {
1413 m = &message[*ip-1];
1414 setdot(m);
1415 if (m->m_flag & (MFLAG|MFLAGGED)) {
1416 m->m_flag &= ~(MFLAG|MFLAGGED);
1417 m->m_flag |= MUNFLAG;
1420 return 0;
1423 int
1424 canswered(void *v)
1426 struct message *m;
1427 int *msgvec = v;
1428 int *ip;
1430 for (ip = msgvec; *ip != 0; ip++) {
1431 m = &message[*ip-1];
1432 setdot(m);
1433 if ((m->m_flag & (MANSWER|MANSWERED)) == 0)
1434 m->m_flag |= MANSWER|MANSWERED;
1436 return 0;
1439 int
1440 cunanswered(void *v)
1442 struct message *m;
1443 int *msgvec = v;
1444 int *ip;
1446 for (ip = msgvec; *ip != 0; ip++) {
1447 m = &message[*ip-1];
1448 setdot(m);
1449 if (m->m_flag & (MANSWER|MANSWERED)) {
1450 m->m_flag &= ~(MANSWER|MANSWERED);
1451 m->m_flag |= MUNANSWER;
1454 return 0;
1457 int
1458 cdraft(void *v)
1460 struct message *m;
1461 int *msgvec = v;
1462 int *ip;
1464 for (ip = msgvec; *ip != 0; ip++) {
1465 m = &message[*ip-1];
1466 setdot(m);
1467 if ((m->m_flag & (MDRAFT|MDRAFTED)) == 0)
1468 m->m_flag |= MDRAFT|MDRAFTED;
1470 return 0;
1473 int
1474 cundraft(void *v)
1476 struct message *m;
1477 int *msgvec = v;
1478 int *ip;
1480 for (ip = msgvec; *ip != 0; ip++) {
1481 m = &message[*ip-1];
1482 setdot(m);
1483 if (m->m_flag & (MDRAFT|MDRAFTED)) {
1484 m->m_flag &= ~(MDRAFT|MDRAFTED);
1485 m->m_flag |= MUNDRAFT;
1488 return 0;
1491 /*ARGSUSED*/
1492 int
1493 cnoop(void *v)
1495 (void)v;
1497 switch (mb.mb_type) {
1498 case MB_IMAP:
1499 #ifdef HAVE_IMAP
1500 imap_noop();
1501 break;
1502 #else
1503 return (ccmdnotsupp(NULL));
1504 #endif
1505 case MB_POP3:
1506 #ifdef HAVE_POP3
1507 pop3_noop();
1508 break;
1509 #else
1510 return (ccmdnotsupp(NULL));
1511 #endif
1512 default:
1513 break;
1515 return 0;
1518 int
1519 cremove(void *v)
1521 char vb[LINESIZE];
1522 char **args = v;
1523 char *name;
1524 int ec = 0;
1526 if (*args == NULL) {
1527 fprintf(stderr, tr(290, "Syntax is: remove mailbox ...\n"));
1528 return (1);
1530 do {
1531 if ((name = expand(*args)) == NULL)
1532 continue;
1533 if (strcmp(name, mailname) == 0) {
1534 fprintf(stderr, tr(286,
1535 "Cannot remove current mailbox \"%s\".\n"),
1536 name);
1537 ec |= 1;
1538 continue;
1540 snprintf(vb, sizeof vb, tr(287, "Remove \"%s\" (y/n) ? "),
1541 name);
1542 if (yorn(vb) == 0)
1543 continue;
1544 switch (which_protocol(name)) {
1545 case PROTO_FILE:
1546 if (unlink(name) < 0) { /* do not handle .gz .bz2 */
1547 perror(name);
1548 ec |= 1;
1550 break;
1551 case PROTO_POP3:
1552 fprintf(stderr, tr(288,
1553 "Cannot remove POP3 mailbox \"%s\".\n"),
1554 name);
1555 ec |= 1;
1556 break;
1557 case PROTO_IMAP:
1558 #ifdef HAVE_IMAP
1559 if (imap_remove(name) != OKAY)
1560 #endif
1561 ec |= 1;
1562 break;
1563 case PROTO_MAILDIR:
1564 if (maildir_remove(name) != OKAY)
1565 ec |= 1;
1566 break;
1567 case PROTO_UNKNOWN:
1568 fprintf(stderr, tr(289,
1569 "Unknown protocol in \"%s\". Not removed.\n"),
1570 name);
1571 ec |= 1;
1573 } while (*++args);
1574 return ec;
1577 int
1578 crename(void *v)
1580 char **args = v, *old, *new;
1581 enum protocol oldp, newp;
1582 int ec = 0;
1584 if (args[0] == NULL || args[1] == NULL || args[2] != NULL) {
1585 fprintf(stderr, "Syntax: rename old new\n");
1586 return (1);
1589 if ((old = expand(args[0])) == NULL)
1590 return (1);
1591 oldp = which_protocol(old);
1592 if ((new = expand(args[1])) == NULL)
1593 return (1);
1594 newp = which_protocol(new);
1596 if (strcmp(old, mailname) == 0 || strcmp(new, mailname) == 0) {
1597 fprintf(stderr, tr(291,
1598 "Cannot rename current mailbox \"%s\".\n"), old);
1599 return 1;
1601 if ((oldp == PROTO_IMAP || newp == PROTO_IMAP) && oldp != newp) {
1602 fprintf(stderr, tr(292,
1603 "Can only rename folders of same type.\n"));
1604 return 1;
1606 if (newp == PROTO_POP3)
1607 goto nopop3;
1608 switch (oldp) {
1609 case PROTO_FILE:
1610 if (link(old, new) < 0) {
1611 switch (errno) {
1612 case EACCES:
1613 case EEXIST:
1614 case ENAMETOOLONG:
1615 case ENOENT:
1616 case ENOSPC:
1617 case EXDEV:
1618 perror(new);
1619 break;
1620 default:
1621 perror(old);
1623 ec |= 1;
1624 } else if (unlink(old) < 0) {
1625 perror(old);
1626 ec |= 1;
1628 break;
1629 case PROTO_MAILDIR:
1630 if (rename(old, new) < 0) {
1631 perror(old);
1632 ec |= 1;
1634 break;
1635 case PROTO_POP3:
1636 nopop3: fprintf(stderr, tr(293, "Cannot rename POP3 mailboxes.\n"));
1637 ec |= 1;
1638 break;
1639 #ifdef HAVE_IMAP
1640 case PROTO_IMAP:
1641 if (imap_rename(old, new) != OKAY)
1642 ec |= 1;
1643 break;
1644 #endif
1645 case PROTO_UNKNOWN:
1646 default:
1647 fprintf(stderr, tr(294,
1648 "Unknown protocol in \"%s\" and \"%s\". "
1649 "Not renamed.\n"), old, new);
1650 ec |= 1;
1652 return ec;