No empty .Rs/.Re
[netbsd-mini2440.git] / usr.bin / mail / cmd3.c
blob8d213d0c1994c603fa2dc70f07505a900102b656
1 /* $NetBSD: cmd3.c,v 1.40 2009/04/10 13:08:24 christos Exp $ */
3 /*
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)cmd3.c 8.2 (Berkeley) 4/20/95";
36 #else
37 __RCSID("$NetBSD: cmd3.c,v 1.40 2009/04/10 13:08:24 christos Exp $");
38 #endif
39 #endif /* not lint */
41 #include "rcv.h"
42 #include <assert.h>
43 #include <util.h>
44 #include "extern.h"
45 #include "mime.h"
46 #include "sig.h"
47 #include "thread.h"
50 * Mail -- a mail program
52 * Still more user commands.
57 * Do a dictionary order comparison of the arguments from
58 * qsort.
60 static int
61 diction(const void *a, const void *b)
64 return strcmp(*(const char *const *)a, *(const char *const *)b);
68 * Sort the passed string vector into ascending dictionary
69 * order.
71 PUBLIC void
72 sort(const char **list)
74 const char **ap;
76 for (ap = list; *ap != NULL; ap++)
77 continue;
78 if (ap - list < 2)
79 return;
80 qsort(list, (size_t)(ap - list), sizeof(*list), diction);
84 * Expand the shell escape by expanding unescaped !'s into the
85 * last issued command where possible.
87 static int
88 bangexp(char *str)
90 static char lastbang[128];
91 char bangbuf[LINESIZE];
92 char *cp, *cp2;
93 ssize_t n;
94 int changed;
96 changed = 0;
97 cp = str;
98 cp2 = bangbuf;
99 n = sizeof(bangbuf); /* bytes left in bangbuf */
100 while (*cp) {
101 if (*cp == '!') {
102 if (n < (int)strlen(lastbang)) {
103 overf:
104 (void)printf("Command buffer overflow\n");
105 return -1;
107 changed++;
108 (void)strcpy(cp2, lastbang);
109 cp2 += strlen(lastbang);
110 n -= strlen(lastbang);
111 cp++;
112 continue;
114 if (*cp == '\\' && cp[1] == '!') {
115 if (--n <= 1)
116 goto overf;
117 *cp2++ = '!';
118 cp += 2;
119 changed++;
121 if (--n <= 1)
122 goto overf;
123 *cp2++ = *cp++;
125 *cp2 = 0;
126 if (changed) {
127 (void)printf("!%s\n", bangbuf);
128 (void)fflush(stdout);
130 (void)strcpy(str, bangbuf);
131 (void)strlcpy(lastbang, bangbuf, sizeof(lastbang));
132 return 0;
136 * Process a shell escape by saving signals, ignoring signals,
137 * and forking a sh -c
139 PUBLIC int
140 shell(void *v)
142 struct sigaction osa;
143 sigset_t oset;
144 char *str;
145 const char *shellcmd;
146 char cmd[LINESIZE];
148 str = v;
149 sig_check();
150 (void)sig_ignore(SIGINT, &osa, &oset);
151 (void)strcpy(cmd, str);
152 if (bangexp(cmd) < 0)
153 return 1;
154 if ((shellcmd = value(ENAME_SHELL)) == NULL)
155 shellcmd = _PATH_CSHELL;
156 (void)run_command(shellcmd, NULL, 0, 1, "-c", cmd, NULL);
157 (void)sig_restore(SIGINT, &osa, &oset);
158 (void)printf("!\n");
159 sig_check();
160 return 0;
164 * Fork an interactive shell.
166 /*ARGSUSED*/
167 PUBLIC int
168 dosh(void *v __unused)
170 struct sigaction osa;
171 sigset_t oset;
172 const char *shellcmd;
174 sig_check();
175 (void)sig_ignore(SIGINT, &osa, &oset);
176 if ((shellcmd = value(ENAME_SHELL)) == NULL)
177 shellcmd = _PATH_CSHELL;
178 (void)run_command(shellcmd, NULL, 0, 1, NULL);
179 (void)sig_restore(SIGINT, &osa, &oset);
180 (void)putchar('\n');
181 sig_check();
182 return 0;
186 * Print out a nice help message from some file or another.
189 /*ARGSUSED*/
190 PUBLIC int
191 help(void *v __unused)
194 cathelp(_PATH_HELP);
195 return 0;
199 * Change user's working directory.
201 PUBLIC int
202 schdir(void *v)
204 char **arglist;
205 const char *cp;
207 arglist = v;
208 if (*arglist == NULL)
209 cp = homedir;
210 else
211 if ((cp = expand(*arglist)) == NULL)
212 return 1;
213 if (chdir(cp) < 0) {
214 warn("%s", cp);
215 return 1;
217 return 0;
221 * Return the smopts field if "ReplyAsRecipient" is defined.
223 static struct name *
224 set_smopts(struct message *mp)
226 char *cp;
227 struct name *np;
228 char *reply_as_recipient;
230 np = NULL;
231 reply_as_recipient = value(ENAME_REPLYASRECIPIENT);
232 if (reply_as_recipient &&
233 (cp = skin(hfield("to", mp))) != NULL &&
234 extract(cp, GTO)->n_flink == NULL) { /* check for one recipient */
235 char *p, *q;
236 size_t len = strlen(cp);
238 * XXX - perhaps we always want to ignore
239 * "undisclosed-recipients:;" ?
241 for (p = q = reply_as_recipient; *p; p = q) {
242 while (*q != '\0' && *q != ',' && !is_WSP(*q))
243 q++;
244 if (p + len == q && strncasecmp(cp, p, len) == 0)
245 return np;
246 while (*q == ',' || is_WSP(*q))
247 q++;
249 np = extract(__UNCONST("-f"), GSMOPTS);
250 np = cat(np, extract(cp, GSMOPTS));
253 return np;
257 * Modify the subject we are replying to to begin with Re: if
258 * it does not already.
260 static char *
261 reedit(char *subj, const char *pref)
263 char *newsubj;
264 size_t preflen;
266 assert(pref != NULL);
267 if (subj == NULL)
268 return __UNCONST(pref);
269 preflen = strlen(pref);
270 if (strncasecmp(subj, pref, preflen) == 0)
271 return subj;
272 newsubj = salloc(strlen(subj) + preflen + 1 + 1);
273 (void)sprintf(newsubj, "%s %s", pref, subj);
274 return newsubj;
278 * Set the "In-Reply-To" and "References" header fields appropriately.
279 * Used in replies.
281 static void
282 set_ident_fields(struct header *hp, struct message *mp)
284 char *in_reply_to;
285 char *references;
287 in_reply_to = hfield("message-id", mp);
288 hp->h_in_reply_to = in_reply_to;
290 references = hfield("references", mp);
291 hp->h_references = extract(references, GMISC);
292 hp->h_references = cat(hp->h_references, extract(in_reply_to, GMISC));
296 * Reply to a list of messages. Extract each name from the
297 * message header and send them off to mail1()
299 static int
300 respond_core(int *msgvec)
302 struct message *mp;
303 char *cp, *rcv, *replyto;
304 char **ap;
305 struct name *np;
306 struct header head;
308 /* ensure that all header fields are initially NULL */
309 (void)memset(&head, 0, sizeof(head));
311 if (msgvec[1] != 0) {
312 (void)printf("Sorry, can't reply to multiple messages at once\n");
313 return 1;
315 mp = get_message(msgvec[0]);
316 touch(mp);
317 dot = mp;
318 if ((rcv = skin(hfield("from", mp))) == NULL)
319 rcv = skin(nameof(mp, 1));
320 if ((replyto = skin(hfield("reply-to", mp))) != NULL)
321 np = extract(replyto, GTO);
322 else if ((cp = skin(hfield("to", mp))) != NULL)
323 np = extract(cp, GTO);
324 else
325 np = NULL;
326 np = elide(np);
328 * Delete my name from the reply list,
329 * and with it, all my alternate names.
331 np = delname(np, myname);
332 if (altnames)
333 for (ap = altnames; *ap; ap++)
334 np = delname(np, *ap);
335 if (np != NULL && replyto == NULL)
336 np = cat(np, extract(rcv, GTO));
337 else if (np == NULL) {
338 if (replyto != NULL)
339 (void)printf("Empty reply-to field -- replying to author\n");
340 np = extract(rcv, GTO);
342 head.h_to = np;
343 if ((head.h_subject = hfield("subject", mp)) == NULL)
344 head.h_subject = hfield("subj", mp);
345 head.h_subject = reedit(head.h_subject, "Re:");
346 if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
347 np = elide(extract(cp, GCC));
348 np = delname(np, myname);
349 if (altnames != 0)
350 for (ap = altnames; *ap; ap++)
351 np = delname(np, *ap);
352 head.h_cc = np;
353 } else
354 head.h_cc = NULL;
355 head.h_bcc = NULL;
356 head.h_smopts = set_smopts(mp);
357 #ifdef MIME_SUPPORT
358 head.h_attach = NULL;
359 #endif
360 set_ident_fields(&head, mp);
361 mail1(&head, 1);
362 return 0;
366 * Reply to a series of messages by simply mailing to the senders
367 * and not messing around with the To: and Cc: lists as in normal
368 * reply.
370 static int
371 Respond_core(int msgvec[])
373 struct header head;
374 struct message *mp;
375 int *ap;
376 char *cp;
378 /* ensure that all header fields are initially NULL */
379 (void)memset(&head, 0, sizeof(head));
381 head.h_to = NULL;
382 for (ap = msgvec; *ap != 0; ap++) {
383 mp = get_message(*ap);
384 touch(mp);
385 dot = mp;
386 if ((cp = skin(hfield("from", mp))) == NULL)
387 cp = skin(nameof(mp, 2));
388 head.h_to = cat(head.h_to, extract(cp, GTO));
390 if (head.h_to == NULL)
391 return 0;
392 mp = get_message(msgvec[0]);
393 if ((head.h_subject = hfield("subject", mp)) == NULL)
394 head.h_subject = hfield("subj", mp);
395 head.h_subject = reedit(head.h_subject, "Re:");
396 head.h_cc = NULL;
397 head.h_bcc = NULL;
398 head.h_smopts = set_smopts(mp);
399 #ifdef MIME_SUPPORT
400 head.h_attach = NULL;
401 #endif
402 set_ident_fields(&head, mp);
403 mail1(&head, 1);
404 return 0;
407 PUBLIC int
408 respond(void *v)
410 int *msgvec = v;
411 if (value(ENAME_REPLYALL) == NULL)
412 return respond_core(msgvec);
413 else
414 return Respond_core(msgvec);
417 PUBLIC int
418 Respond(void *v)
420 int *msgvec = v;
421 if (value(ENAME_REPLYALL) == NULL)
422 return Respond_core(msgvec);
423 else
424 return respond_core(msgvec);
427 #ifdef MIME_SUPPORT
428 static int
429 forward_one(int msgno, struct name *h_to)
431 struct attachment attach;
432 struct message *mp;
433 struct header hdr;
435 mp = get_message(msgno);
436 if (mp == NULL) {
437 (void)printf("no such message %d\n", msgno);
438 return 1;
440 (void)printf("message %d\n", msgno);
442 (void)memset(&attach, 0, sizeof(attach));
443 attach.a_type = ATTACH_MSG;
444 attach.a_msg = mp;
445 attach.a_Content = get_mime_content(&attach, 0);
447 (void)memset(&hdr, 0, sizeof(hdr));
448 hdr.h_to = h_to;
449 if ((hdr.h_subject = hfield("subject", mp)) == NULL)
450 hdr.h_subject = hfield("subj", mp);
451 hdr.h_subject = reedit(hdr.h_subject, "Fwd:");
452 hdr.h_attach = &attach;
453 hdr.h_smopts = set_smopts(mp);
455 set_ident_fields(&hdr, mp);
456 mail1(&hdr, 1);
457 return 0;
460 PUBLIC int
461 forward(void *v)
463 int *msgvec = v;
464 int *ip;
465 struct header hdr;
466 int rval;
468 if (forwardtab[0].i_count == 0) {
469 /* setup the forward tab */
470 add_ignore("Status", forwardtab);
472 (void)memset(&hdr, 0, sizeof(hdr));
473 if ((rval = grabh(&hdr, GTO)) != 0)
474 return rval;
476 if (hdr.h_to == NULL) {
477 (void)printf("address missing!\n");
478 return 1;
480 for (ip = msgvec; *ip; ip++) {
481 int e;
482 if ((e = forward_one(*ip, hdr.h_to)) != 0)
483 return e;
485 return 0;
487 #endif /* MIME_SUPPORT */
489 static int
490 bounce_one(int msgno, const char **smargs, struct name *h_to)
492 char mailtempname[PATHSIZE];
493 struct message *mp;
494 int fd;
495 FILE *obuf;
496 int rval;
498 rval = 0;
500 obuf = NULL;
501 (void)snprintf(mailtempname, sizeof(mailtempname),
502 "%s/mail.RsXXXXXXXXXX", tmpdir);
503 if ((fd = mkstemp(mailtempname)) == -1 ||
504 (obuf = Fdopen(fd, "w+")) == NULL) {
505 if (fd != -1)
506 (void)close(fd);
507 warn("%s", mailtempname);
508 rval = 1;
509 goto done;
511 (void)rm(mailtempname);
513 mp = get_message(msgno);
515 if (mp == NULL) {
516 (void)printf("no such message %d\n", msgno);
517 rval = 1;
518 goto done;
520 else {
521 char *cp;
522 char **ap;
523 struct name *np;
524 struct header hdr;
527 * Construct and output a new "To:" field:
528 * Remove our address from anything in the old "To:" field
529 * and append that list to the bounce address(es).
531 np = NULL;
532 if ((cp = skin(hfield("to", mp))) != NULL)
533 np = extract(cp, GTO);
534 np = delname(np, myname);
535 if (altnames)
536 for (ap = altnames; *ap; ap++)
537 np = delname(np, *ap);
538 np = cat(h_to, np);
539 (void)memset(&hdr, 0, sizeof(hdr));
540 hdr.h_to = elide(np);
541 (void)puthead(&hdr, obuf, GTO | GCOMMA);
543 if (sendmessage(mp, obuf, bouncetab, NULL, NULL)) {
544 (void)printf("bounce failed for message %d\n", msgno);
545 rval = 1;
546 goto done;
548 rewind(obuf); /* XXX - here or inside mail2() */
549 mail2(obuf, smargs);
550 done:
551 if (obuf)
552 (void)Fclose(obuf);
553 return rval;
556 PUBLIC int
557 bounce(void *v)
559 int *msgvec;
560 int *ip;
561 const char **smargs;
562 struct header hdr;
563 int rval;
565 msgvec = v;
566 if (bouncetab[0].i_count == 0) {
567 /* setup the bounce tab */
568 add_ignore("Status", bouncetab);
569 add_ignore("Delivered-To", bouncetab);
570 add_ignore("To", bouncetab);
571 add_ignore("X-Original-To", bouncetab);
573 (void)memset(&hdr, 0, sizeof(hdr));
574 if ((rval = grabh(&hdr, GTO)) != 0)
575 return rval;
577 if (hdr.h_to == NULL)
578 return 1;
580 smargs = unpack(hdr.h_to);
581 for (ip = msgvec; *ip; ip++) {
582 int e;
583 if ((e = bounce_one(*ip, smargs, hdr.h_to)) != 0)
584 return e;
586 return 0;
590 * Preserve the named messages, so that they will be sent
591 * back to the system mailbox.
593 PUBLIC int
594 preserve(void *v)
596 int *msgvec;
597 int *ip;
599 msgvec = v;
600 if (edit) {
601 (void)printf("Cannot \"preserve\" in edit mode\n");
602 return 1;
604 for (ip = msgvec; *ip != 0; ip++)
605 dot = set_m_flag(*ip, ~(MBOX | MPRESERVE), MPRESERVE);
607 return 0;
611 * Mark all given messages as unread, preserving the new status.
613 PUBLIC int
614 unread(void *v)
616 int *msgvec;
617 int *ip;
619 msgvec = v;
620 for (ip = msgvec; *ip != 0; ip++)
621 dot = set_m_flag(*ip, ~(MREAD | MTOUCH | MSTATUS), MSTATUS);
623 return 0;
627 * Mark all given messages as read.
629 PUBLIC int
630 markread(void *v)
632 int *msgvec;
633 int *ip;
635 msgvec = v;
636 for (ip = msgvec; *ip != 0; ip++)
637 dot = set_m_flag(*ip,
638 ~(MNEW | MTOUCH | MREAD | MSTATUS), MREAD | MSTATUS);
640 return 0;
644 * Print the size of each message.
646 PUBLIC int
647 messize(void *v)
649 int *msgvec;
650 struct message *mp;
651 int *ip, mesg;
653 msgvec = v;
654 for (ip = msgvec; *ip != 0; ip++) {
655 mesg = *ip;
656 mp = get_message(mesg);
657 (void)printf("%d: %ld/%llu\n", mesg, mp->m_blines,
658 (unsigned long long)mp->m_size);
660 return 0;
664 * Quit quickly. If we are sourcing, just pop the input level
665 * by returning an error.
667 /*ARGSUSED*/
668 PUBLIC int
669 rexit(void *v __unused)
671 if (sourcing)
672 return 1;
673 exit(0);
674 /*NOTREACHED*/
678 * Set or display a variable value. Syntax is similar to that
679 * of csh.
681 PUBLIC int
682 set(void *v)
684 const char **arglist = v;
685 struct var *vp;
686 const char *cp;
687 char varbuf[LINESIZE];
688 const char **ap, **p;
689 int errs, h, s;
690 size_t l;
692 if (*arglist == NULL) {
693 for (h = 0, s = 1; h < HSHSIZE; h++)
694 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
695 s++;
696 ap = salloc(s * sizeof(*ap));
697 for (h = 0, p = ap; h < HSHSIZE; h++)
698 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
699 *p++ = vp->v_name;
700 *p = NULL;
701 sort(ap);
702 for (p = ap; *p != NULL; p++)
703 (void)printf("%s\t%s\n", *p, value(*p));
704 return 0;
706 errs = 0;
707 for (ap = arglist; *ap != NULL; ap++) {
708 cp = *ap;
709 while (*cp != '=' && *cp != '\0')
710 ++cp;
711 l = cp - *ap;
712 if (l >= sizeof(varbuf))
713 l = sizeof(varbuf) - 1;
714 (void)strncpy(varbuf, *ap, l);
715 varbuf[l] = '\0';
716 if (*cp == '\0')
717 cp = "";
718 else
719 cp++;
720 if (equal(varbuf, "")) {
721 (void)printf("Non-null variable name required\n");
722 errs++;
723 continue;
725 assign(varbuf, cp);
727 return errs;
731 * Unset a bunch of variable values.
733 PUBLIC int
734 unset(void *v)
736 char **arglist = v;
737 struct var *vp, *vp2;
738 int errs, h;
739 char **ap;
741 errs = 0;
742 for (ap = arglist; *ap != NULL; ap++) {
743 if ((vp2 = lookup(*ap)) == NULL) {
744 if (getenv(*ap)) {
745 (void)unsetenv(*ap);
746 } else if (!sourcing) {
747 (void)printf("\"%s\": undefined variable\n", *ap);
748 errs++;
750 continue;
752 h = hash(*ap);
753 if (vp2 == variables[h]) {
754 variables[h] = variables[h]->v_link;
755 v_free(vp2->v_name);
756 v_free(vp2->v_value);
757 free(vp2);
758 continue;
760 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
761 continue;
762 vp->v_link = vp2->v_link;
763 v_free(vp2->v_name);
764 v_free(vp2->v_value);
765 free(vp2);
767 return errs;
771 * Show a variable value.
773 PUBLIC int
774 show(void *v)
776 const char **arglist = v;
777 struct var *vp;
778 const char **ap, **p;
779 int h, s;
781 if (*arglist == NULL) {
782 for (h = 0, s = 1; h < HSHSIZE; h++)
783 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
784 s++;
785 ap = salloc(s * sizeof(*ap));
786 for (h = 0, p = ap; h < HSHSIZE; h++)
787 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
788 *p++ = vp->v_name;
789 *p = NULL;
790 sort(ap);
791 for (p = ap; *p != NULL; p++)
792 (void)printf("%s=%s\n", *p, value(*p));
793 return 0;
796 for (ap = arglist; *ap != NULL; ap++) {
797 char *val = value(*ap);
798 (void)printf("%s=%s\n", *ap, val ? val : "<null>");
800 return 0;
805 * Put add users to a group.
807 PUBLIC int
808 group(void *v)
810 const char **argv = v;
811 struct grouphead *gh;
812 struct group *gp;
813 int h;
814 int s;
815 const char *gname;
816 const char **ap, **p;
818 if (*argv == NULL) {
819 for (h = 0, s = 1; h < HSHSIZE; h++)
820 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
821 s++;
822 ap = salloc(s * sizeof(*ap));
823 for (h = 0, p = ap; h < HSHSIZE; h++)
824 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
825 *p++ = gh->g_name;
826 *p = NULL;
827 sort(ap);
828 for (p = ap; *p != NULL; p++)
829 printgroup(*p);
830 return 0;
832 if (argv[1] == NULL) {
833 printgroup(*argv);
834 return 0;
836 gname = *argv;
837 h = hash(gname);
838 if ((gh = findgroup(gname)) == NULL) {
839 gh = ecalloc(1, sizeof(*gh));
840 gh->g_name = vcopy(gname);
841 gh->g_list = NULL;
842 gh->g_link = groups[h];
843 groups[h] = gh;
847 * Insert names from the command list into the group.
848 * Who cares if there are duplicates? They get tossed
849 * later anyway.
852 for (ap = argv + 1; *ap != NULL; ap++) {
853 gp = ecalloc(1, sizeof(*gp));
854 gp->ge_name = vcopy(*ap);
855 gp->ge_link = gh->g_list;
856 gh->g_list = gp;
858 return 0;
862 * Delete the named group alias. Return zero if the group was
863 * successfully deleted, or -1 if there was no such group.
865 static int
866 delgroup(const char *name)
868 struct grouphead *gh, *p;
869 struct group *g;
870 int h;
872 h = hash(name);
873 for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link)
874 if (strcmp(gh->g_name, name) == 0) {
875 if (p == NULL)
876 groups[h] = gh->g_link;
877 else
878 p->g_link = gh->g_link;
879 while (gh->g_list != NULL) {
880 g = gh->g_list;
881 gh->g_list = g->ge_link;
882 free(g->ge_name);
883 free(g);
885 free(gh->g_name);
886 free(gh);
887 return 0;
889 return -1;
893 * The unalias command takes a list of alises
894 * and discards the remembered groups of users.
896 PUBLIC int
897 unalias(void *v)
899 char **ap;
901 for (ap = v; *ap != NULL; ap++)
902 (void)delgroup(*ap);
903 return 0;
907 * The do nothing command for comments.
909 /*ARGSUSED*/
910 PUBLIC int
911 null(void *v __unused)
913 return 0;
917 * Change to another file. With no argument, print information about
918 * the current file.
920 PUBLIC int
921 file(void *v)
923 char **argv = v;
925 if (argv[0] == NULL) {
926 (void)newfileinfo(0);
927 return 0;
929 if (setfile(*argv) < 0)
930 return 1;
931 announce();
933 return 0;
937 * Expand file names like echo
939 PUBLIC int
940 echo(void *v)
942 char **argv = v;
943 char **ap;
944 const char *cp;
946 for (ap = argv; *ap != NULL; ap++) {
947 cp = *ap;
948 if ((cp = expand(cp)) != NULL) {
949 if (ap != argv)
950 (void)putchar(' ');
951 (void)printf("%s", cp);
954 (void)putchar('\n');
955 return 0;
959 * Routines to push and pop the condition code to support nested
960 * if/else/endif statements.
962 static void
963 push_cond(int c_cond)
965 struct cond_stack_s *csp;
966 csp = emalloc(sizeof(*csp));
967 csp->c_cond = c_cond;
968 csp->c_next = cond_stack;
969 cond_stack = csp;
972 static int
973 pop_cond(void)
975 int c_cond;
976 struct cond_stack_s *csp;
978 if ((csp = cond_stack) == NULL)
979 return -1;
981 c_cond = csp->c_cond;
982 cond_stack = csp->c_next;
983 free(csp);
984 return c_cond;
988 * Conditional commands. These allow one to parameterize one's
989 * .mailrc and do some things if sending, others if receiving.
991 static int
992 if_push(void)
994 push_cond(cond);
995 cond &= ~CELSE;
996 if ((cond & (CIF | CSKIP)) == (CIF | CSKIP)) {
997 cond |= CIGN;
998 return 1;
1000 return 0;
1003 PUBLIC int
1004 ifcmd(void *v)
1006 char **argv = v;
1007 char *keyword = argv[0];
1008 static const struct modetbl_s {
1009 const char *m_name;
1010 enum mailmode_e m_mode;
1011 } modetbl[] = {
1012 { "receiving", mm_receiving },
1013 { "sending", mm_sending },
1014 { "headersonly", mm_hdrsonly },
1015 { NULL, 0 },
1017 const struct modetbl_s *mtp;
1019 if (if_push())
1020 return 0;
1022 cond = CIF;
1023 for (mtp = modetbl; mtp->m_name; mtp++)
1024 if (strcasecmp(keyword, mtp->m_name) == 0)
1025 break;
1027 if (mtp->m_name == NULL) {
1028 cond = CNONE;
1029 (void)printf("Unrecognized if-keyword: \"%s\"\n", keyword);
1030 return 1;
1032 if (mtp->m_mode != mailmode)
1033 cond |= CSKIP;
1035 return 0;
1038 PUBLIC int
1039 ifdefcmd(void *v)
1041 char **argv = v;
1043 if (if_push())
1044 return 0;
1046 cond = CIF;
1047 if (value(argv[0]) == NULL)
1048 cond |= CSKIP;
1050 return 0;
1053 PUBLIC int
1054 ifndefcmd(void *v)
1056 int rval;
1057 rval = ifdefcmd(v);
1058 cond ^= CSKIP;
1059 return rval;
1063 * Implement 'else'. This is pretty simple -- we just
1064 * flip over the conditional flag.
1066 /*ARGSUSED*/
1067 PUBLIC int
1068 elsecmd(void *v __unused)
1070 if (cond_stack == NULL || (cond & (CIF | CELSE)) != CIF) {
1071 (void)printf("\"else\" without matching \"if\"\n");
1072 cond = CNONE;
1073 return 1;
1075 if ((cond & CIGN) == 0) {
1076 cond ^= CSKIP;
1077 cond |= CELSE;
1079 return 0;
1083 * End of if statement. Just set cond back to anything.
1085 /*ARGSUSED*/
1086 PUBLIC int
1087 endifcmd(void *v __unused)
1089 if (cond_stack == NULL || (cond & CIF) != CIF) {
1090 (void)printf("\"endif\" without matching \"if\"\n");
1091 cond = CNONE;
1092 return 1;
1094 cond = pop_cond();
1095 return 0;
1099 * Set the list of alternate names.
1101 PUBLIC int
1102 alternates(void *v)
1104 char **namelist = v;
1105 size_t c;
1106 char **ap, **ap2, *cp;
1108 c = argcount(namelist) + 1;
1109 if (c == 1) {
1110 if (altnames == 0)
1111 return 0;
1112 for (ap = altnames; *ap; ap++)
1113 (void)printf("%s ", *ap);
1114 (void)printf("\n");
1115 return 0;
1117 if (altnames != 0)
1118 free(altnames);
1119 altnames = ecalloc(c, sizeof(char *));
1120 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
1121 cp = ecalloc(strlen(*ap) + 1, sizeof(char));
1122 (void)strcpy(cp, *ap);
1123 *ap2 = cp;
1125 *ap2 = 0;
1126 return 0;