cmd3.c revision 1.37 1 /* $NetBSD: cmd3.c,v 1.37 2007/10/29 23:20:37 christos Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
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.
18 *
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.
30 */
31
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.37 2007/10/29 23:20:37 christos Exp $");
38 #endif
39 #endif /* not lint */
40
41 #include "rcv.h"
42 #include <util.h>
43 #include "extern.h"
44 #include "mime.h"
45 #include "thread.h"
46
47 /*
48 * Mail -- a mail program
49 *
50 * Still more user commands.
51 */
52
53
54 /*
55 * Do a dictionary order comparison of the arguments from
56 * qsort.
57 */
58 static int
59 diction(const void *a, const void *b)
60 {
61 return strcmp(*(const char *const *)a, *(const char *const *)b);
62 }
63
64 /*
65 * Sort the passed string vector into ascending dictionary
66 * order.
67 */
68 PUBLIC void
69 sort(const char **list)
70 {
71 const char **ap;
72
73 for (ap = list; *ap != NULL; ap++)
74 continue;
75 if (ap-list < 2)
76 return;
77 qsort(list, (size_t)(ap-list), sizeof(*list), diction);
78 }
79
80 /*
81 * Expand the shell escape by expanding unescaped !'s into the
82 * last issued command where possible.
83 */
84 static int
85 bangexp(char *str)
86 {
87 static char lastbang[128];
88 char bangbuf[LINESIZE];
89 char *cp, *cp2;
90 int n;
91 int changed = 0;
92
93 cp = str;
94 cp2 = bangbuf;
95 n = sizeof(bangbuf); /* bytes left in bangbuf */
96 while (*cp) {
97 if (*cp == '!') {
98 if (n < (int)strlen(lastbang)) {
99 overf:
100 (void)printf("Command buffer overflow\n");
101 return -1;
102 }
103 changed++;
104 (void)strcpy(cp2, lastbang);
105 cp2 += strlen(lastbang);
106 n -= strlen(lastbang);
107 cp++;
108 continue;
109 }
110 if (*cp == '\\' && cp[1] == '!') {
111 if (--n <= 1)
112 goto overf;
113 *cp2++ = '!';
114 cp += 2;
115 changed++;
116 }
117 if (--n <= 1)
118 goto overf;
119 *cp2++ = *cp++;
120 }
121 *cp2 = 0;
122 if (changed) {
123 (void)printf("!%s\n", bangbuf);
124 (void)fflush(stdout);
125 }
126 (void)strcpy(str, bangbuf);
127 (void)strlcpy(lastbang, bangbuf, sizeof(lastbang));
128 return 0;
129 }
130
131 /*
132 * Process a shell escape by saving signals, ignoring signals,
133 * and forking a sh -c
134 */
135 PUBLIC int
136 shell(void *v)
137 {
138 char *str = v;
139 sig_t sigint = signal(SIGINT, SIG_IGN);
140 const char *shellcmd;
141 char cmd[LINESIZE];
142
143 (void)strcpy(cmd, str);
144 if (bangexp(cmd) < 0)
145 return 1;
146 if ((shellcmd = value(ENAME_SHELL)) == NULL)
147 shellcmd = _PATH_CSHELL;
148 (void)run_command(shellcmd, 0, 0, 1, "-c", cmd, NULL);
149 (void)signal(SIGINT, sigint);
150 (void)printf("!\n");
151 return 0;
152 }
153
154 /*
155 * Fork an interactive shell.
156 */
157 /*ARGSUSED*/
158 PUBLIC int
159 dosh(void *v __unused)
160 {
161 sig_t sigint = signal(SIGINT, SIG_IGN);
162 const char *shellcmd;
163
164 if ((shellcmd = value(ENAME_SHELL)) == NULL)
165 shellcmd = _PATH_CSHELL;
166 (void)run_command(shellcmd, 0, 0, 1, NULL);
167 (void)signal(SIGINT, sigint);
168 (void)putchar('\n');
169 return 0;
170 }
171
172 /*
173 * Print out a nice help message from some file or another.
174 */
175
176 /*ARGSUSED*/
177 PUBLIC int
178 help(void *v __unused)
179 {
180 cathelp(_PATH_HELP);
181 return 0;
182 }
183
184 /*
185 * Change user's working directory.
186 */
187 PUBLIC int
188 schdir(void *v)
189 {
190 char **arglist = v;
191 const char *cp;
192
193 if (*arglist == NULL)
194 cp = homedir;
195 else
196 if ((cp = expand(*arglist)) == NULL)
197 return 1;
198 if (chdir(cp) < 0) {
199 warn("%s", cp);
200 return 1;
201 }
202 return 0;
203 }
204
205 /*
206 * Return the smopts field if "ReplyAsRecipient" is defined.
207 */
208 static struct name *
209 set_smopts(struct message *mp)
210 {
211 char *cp;
212 struct name *np = NULL;
213 char *reply_as_recipient = value(ENAME_REPLYASRECIPIENT);
214
215 if (reply_as_recipient &&
216 (cp = skin(hfield("to", mp))) != NULL &&
217 extract(cp, GTO)->n_flink == NULL) { /* check for one recipient */
218 char *p, *q;
219 size_t len = strlen(cp);
220 /*
221 * XXX - perhaps we always want to ignore
222 * "undisclosed-recipients:;" ?
223 */
224 for (p = q = reply_as_recipient; *p; p = q) {
225 while (*q != '\0' && *q != ',' && !is_WSP(*q))
226 q++;
227 if (p + len == q && strncasecmp(cp, p, len) == 0)
228 return np;
229 while (*q == ',' || is_WSP(*q))
230 q++;
231 }
232 np = extract(__UNCONST("-f"), GSMOPTS);
233 np = cat(np, extract(cp, GSMOPTS));
234 }
235
236 return np;
237 }
238
239 /*
240 * Modify the subject we are replying to to begin with Re: if
241 * it does not already.
242 */
243 static char *
244 reedit(char *subj)
245 {
246 char *newsubj;
247
248 if (subj == NULL)
249 return NULL;
250 if ((subj[0] == 'r' || subj[0] == 'R') &&
251 (subj[1] == 'e' || subj[1] == 'E') &&
252 subj[2] == ':')
253 return subj;
254 newsubj = salloc(strlen(subj) + 5);
255 (void)strcpy(newsubj, "Re: ");
256 (void)strcpy(newsubj + 4, subj);
257 return newsubj;
258 }
259
260 /*
261 * Set the "In-Reply-To" and "References" header fields appropriately.
262 * Used in replies.
263 */
264 static void
265 set_ident_fields(struct header *hp, struct message *mp)
266 {
267 char *in_reply_to;
268 char *references;
269
270 in_reply_to = hfield("message-id", mp);
271 hp->h_in_reply_to = in_reply_to;
272
273 references = hfield("references", mp);
274 hp->h_references = extract(references, GMISC);
275 hp->h_references = cat(hp->h_references, extract(in_reply_to, GMISC));
276 }
277
278 /*
279 * Reply to a list of messages. Extract each name from the
280 * message header and send them off to mail1()
281 */
282 static int
283 respond_core(int *msgvec)
284 {
285 struct message *mp;
286 char *cp, *rcv, *replyto;
287 char **ap;
288 struct name *np;
289 struct header head;
290
291 /* ensure that all header fields are initially NULL */
292 (void)memset(&head, 0, sizeof(head));
293
294 if (msgvec[1] != 0) {
295 (void)printf("Sorry, can't reply to multiple messages at once\n");
296 return 1;
297 }
298 mp = get_message(msgvec[0]);
299 touch(mp);
300 dot = mp;
301 if ((rcv = skin(hfield("from", mp))) == NULL)
302 rcv = skin(nameof(mp, 1));
303 if ((replyto = skin(hfield("reply-to", mp))) != NULL)
304 np = extract(replyto, GTO);
305 else if ((cp = skin(hfield("to", mp))) != NULL)
306 np = extract(cp, GTO);
307 else
308 np = NULL;
309 np = elide(np);
310 /*
311 * Delete my name from the reply list,
312 * and with it, all my alternate names.
313 */
314 np = delname(np, myname);
315 if (altnames)
316 for (ap = altnames; *ap; ap++)
317 np = delname(np, *ap);
318 if (np != NULL && replyto == NULL)
319 np = cat(np, extract(rcv, GTO));
320 else if (np == NULL) {
321 if (replyto != NULL)
322 (void)printf("Empty reply-to field -- replying to author\n");
323 np = extract(rcv, GTO);
324 }
325 head.h_to = np;
326 if ((head.h_subject = hfield("subject", mp)) == NULL)
327 head.h_subject = hfield("subj", mp);
328 head.h_subject = reedit(head.h_subject);
329 if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
330 np = elide(extract(cp, GCC));
331 np = delname(np, myname);
332 if (altnames != 0)
333 for (ap = altnames; *ap; ap++)
334 np = delname(np, *ap);
335 head.h_cc = np;
336 } else
337 head.h_cc = NULL;
338 head.h_bcc = NULL;
339 head.h_smopts = set_smopts(mp);
340 #ifdef MIME_SUPPORT
341 head.h_attach = NULL;
342 #endif
343 set_ident_fields(&head, mp);
344 mail1(&head, 1);
345 return 0;
346 }
347
348 /*
349 * Reply to a series of messages by simply mailing to the senders
350 * and not messing around with the To: and Cc: lists as in normal
351 * reply.
352 */
353 static int
354 Respond_core(int msgvec[])
355 {
356 struct header head;
357 struct message *mp;
358 int *ap;
359 char *cp;
360
361 /* ensure that all header fields are initially NULL */
362 (void)memset(&head, 0, sizeof(head));
363
364 head.h_to = NULL;
365 for (ap = msgvec; *ap != 0; ap++) {
366 mp = get_message(*ap);
367 touch(mp);
368 dot = mp;
369 if ((cp = skin(hfield("from", mp))) == NULL)
370 cp = skin(nameof(mp, 2));
371 head.h_to = cat(head.h_to, extract(cp, GTO));
372 }
373 if (head.h_to == NULL)
374 return 0;
375 mp = get_message(msgvec[0]);
376 if ((head.h_subject = hfield("subject", mp)) == NULL)
377 head.h_subject = hfield("subj", mp);
378 head.h_subject = reedit(head.h_subject);
379 head.h_cc = NULL;
380 head.h_bcc = NULL;
381 head.h_smopts = set_smopts(mp);
382 #ifdef MIME_SUPPORT
383 head.h_attach = NULL;
384 #endif
385 set_ident_fields(&head, mp);
386 mail1(&head, 1);
387 return 0;
388 }
389
390 PUBLIC int
391 respond(void *v)
392 {
393 int *msgvec = v;
394 if (value(ENAME_REPLYALL) == NULL)
395 return respond_core(msgvec);
396 else
397 return Respond_core(msgvec);
398 }
399
400 PUBLIC int
401 Respond(void *v)
402 {
403 int *msgvec = v;
404 if (value(ENAME_REPLYALL) == NULL)
405 return Respond_core(msgvec);
406 else
407 return respond_core(msgvec);
408 }
409
410 PUBLIC int
411 forward(void *v)
412 {
413 int *msgvec = v;
414 int *ip;
415 for ( ip = msgvec; *ip; ip++)
416 (void)printf("%d ", *ip);
417 (void)printf("forward not supported yet\n");
418 return 0;
419 }
420
421 static int
422 bounce_one(int msgno, const char **smargs, struct name *h_to)
423 {
424 char mailtempname[PATHSIZE];
425 struct message *mp;
426 int fd;
427 FILE *obuf;
428 int rval;
429
430 rval = 0;
431
432 obuf = NULL;
433 (void)snprintf(mailtempname, sizeof(mailtempname),
434 "%s/mail.RsXXXXXXXXXX", tmpdir);
435 if ((fd = mkstemp(mailtempname)) == -1 ||
436 (obuf = Fdopen(fd, "w+")) == NULL) {
437 if (fd != -1)
438 (void)close(fd);
439 warn("%s", mailtempname);
440 rval = 1;
441 goto done;
442 }
443 (void)rm(mailtempname);
444
445 mp = get_message(msgno);
446
447 if (mp == NULL) {
448 (void)printf("no such message %d\n", msgno);
449 rval = 1;
450 goto done;
451 }
452 else {
453 char *cp;
454 char **ap;
455 struct name *np;
456 struct header hdr;
457
458 /*
459 * Construct and output a new "To:" field:
460 * Remove our address from anything in the old "To:" field
461 * and append that list to the bounce address(es).
462 */
463 np = NULL;
464 if ((cp = skin(hfield("to", mp))) != NULL)
465 np = extract(cp, GTO);
466 np = delname(np, myname);
467 if (altnames)
468 for (ap = altnames; *ap; ap++)
469 np = delname(np, *ap);
470 np = cat(h_to, np);
471 (void)memset(&hdr, 0, sizeof(hdr));
472 hdr.h_to = elide(np);
473 (void)puthead(&hdr, obuf, GTO | GCOMMA);
474 }
475 if (sendmessage(mp, obuf, bouncetab, NULL, NULL)) {
476 (void)printf("bounce failed for message %d\n", msgno);
477 rval = 1;
478 goto done;
479 }
480 rewind(obuf); /* XXX - here or inside mail2() */
481 mail2(obuf, smargs);
482 done:
483 if (obuf)
484 (void)Fclose(obuf);
485 return rval;
486 }
487
488 PUBLIC int
489 bounce(void *v)
490 {
491 int *msgvec = v;
492 int *ip;
493 const char **smargs;
494 struct header hdr;
495 int rval;
496
497 if (bouncetab[0].i_count == 0) {
498 /* setup the bounce tab */
499 add_ignore("Status", bouncetab);
500 add_ignore("Delivered-To", bouncetab);
501 add_ignore("To", bouncetab);
502 add_ignore("X-Original-To", bouncetab);
503 }
504 (void)memset(&hdr, 0, sizeof(hdr));
505 if ((rval = grabh(&hdr, GTO)) != 0)
506 return rval;
507
508 if (hdr.h_to == NULL)
509 return 1;
510
511 smargs = unpack(hdr.h_to);
512 for ( ip = msgvec; *ip; ip++) {
513 int e;
514 if ((e = bounce_one(*ip, smargs, hdr.h_to)) != 0)
515 return e;
516 }
517 return 0;
518 }
519
520 /*
521 * Preserve the named messages, so that they will be sent
522 * back to the system mailbox.
523 */
524 PUBLIC int
525 preserve(void *v)
526 {
527 int *msgvec = v;
528 int *ip;
529
530 if (edit) {
531 (void)printf("Cannot \"preserve\" in edit mode\n");
532 return 1;
533 }
534 for (ip = msgvec; *ip != 0; ip++)
535 dot = set_m_flag(*ip, ~(MBOX | MPRESERVE), MPRESERVE);
536
537 return 0;
538 }
539
540 /*
541 * Mark all given messages as unread, preserving the new status.
542 */
543 PUBLIC int
544 unread(void *v)
545 {
546 int *msgvec = v;
547 int *ip;
548
549 for (ip = msgvec; *ip != 0; ip++)
550 dot = set_m_flag(*ip, ~(MREAD | MTOUCH | MSTATUS), MSTATUS);
551
552 return 0;
553 }
554
555 /*
556 * Mark all given messages as read.
557 */
558 PUBLIC int
559 markread(void *v)
560 {
561 int *msgvec = v;
562 int *ip;
563
564 for (ip = msgvec; *ip != 0; ip++)
565 dot = set_m_flag(*ip,
566 ~(MNEW | MTOUCH | MREAD | MSTATUS), MREAD | MSTATUS);
567
568 return 0;
569 }
570
571 /*
572 * Print the size of each message.
573 */
574 PUBLIC int
575 messize(void *v)
576 {
577 int *msgvec = v;
578 struct message *mp;
579 int *ip, mesg;
580
581 for (ip = msgvec; *ip != 0; ip++) {
582 mesg = *ip;
583 mp = get_message(mesg);
584 (void)printf("%d: %ld/%llu\n", mesg, mp->m_blines,
585 (unsigned long long)mp->m_size);
586 }
587 return 0;
588 }
589
590 /*
591 * Quit quickly. If we are sourcing, just pop the input level
592 * by returning an error.
593 */
594 /*ARGSUSED*/
595 PUBLIC int
596 rexit(void *v __unused)
597 {
598 if (sourcing)
599 return 1;
600 exit(0);
601 /*NOTREACHED*/
602 }
603
604 /*
605 * Set or display a variable value. Syntax is similar to that
606 * of csh.
607 */
608 PUBLIC int
609 set(void *v)
610 {
611 const char **arglist = v;
612 struct var *vp;
613 const char *cp;
614 char varbuf[LINESIZE];
615 const char **ap, **p;
616 int errs, h, s;
617 size_t l;
618
619 if (*arglist == NULL) {
620 for (h = 0, s = 1; h < HSHSIZE; h++)
621 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
622 s++;
623 ap = salloc(s * sizeof(*ap));
624 for (h = 0, p = ap; h < HSHSIZE; h++)
625 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
626 *p++ = vp->v_name;
627 *p = NULL;
628 sort(ap);
629 for (p = ap; *p != NULL; p++)
630 (void)printf("%s\t%s\n", *p, value(*p));
631 return 0;
632 }
633 errs = 0;
634 for (ap = arglist; *ap != NULL; ap++) {
635 cp = *ap;
636 while (*cp != '=' && *cp != '\0')
637 ++cp;
638 l = cp - *ap;
639 if (l >= sizeof(varbuf))
640 l = sizeof(varbuf) - 1;
641 (void)strncpy(varbuf, *ap, l);
642 varbuf[l] = '\0';
643 if (*cp == '\0')
644 cp = "";
645 else
646 cp++;
647 if (equal(varbuf, "")) {
648 (void)printf("Non-null variable name required\n");
649 errs++;
650 continue;
651 }
652 assign(varbuf, cp);
653 }
654 return errs;
655 }
656
657 /*
658 * Unset a bunch of variable values.
659 */
660 PUBLIC int
661 unset(void *v)
662 {
663 char **arglist = v;
664 struct var *vp, *vp2;
665 int errs, h;
666 char **ap;
667
668 errs = 0;
669 for (ap = arglist; *ap != NULL; ap++) {
670 if ((vp2 = lookup(*ap)) == NULL) {
671 if (getenv(*ap)) {
672 (void)unsetenv(*ap);
673 } else if (!sourcing) {
674 (void)printf("\"%s\": undefined variable\n", *ap);
675 errs++;
676 }
677 continue;
678 }
679 h = hash(*ap);
680 if (vp2 == variables[h]) {
681 variables[h] = variables[h]->v_link;
682 v_free(vp2->v_name);
683 v_free(vp2->v_value);
684 free(vp2);
685 continue;
686 }
687 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
688 continue;
689 vp->v_link = vp2->v_link;
690 v_free(vp2->v_name);
691 v_free(vp2->v_value);
692 free(vp2);
693 }
694 return errs;
695 }
696
697 /*
698 * Show a variable value.
699 */
700 PUBLIC int
701 show(void *v)
702 {
703 const char **arglist = v;
704 struct var *vp;
705 const char **ap, **p;
706 int h, s;
707
708 if (*arglist == NULL) {
709 for (h = 0, s = 1; h < HSHSIZE; h++)
710 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
711 s++;
712 ap = salloc(s * sizeof(*ap));
713 for (h = 0, p = ap; h < HSHSIZE; h++)
714 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
715 *p++ = vp->v_name;
716 *p = NULL;
717 sort(ap);
718 for (p = ap; *p != NULL; p++)
719 (void)printf("%s=%s\n", *p, value(*p));
720 return 0;
721 }
722
723 for (ap = arglist; *ap != NULL; ap++) {
724 char *val = value(*ap);
725 (void)printf("%s=%s\n", *ap, val ? val : "<null>");
726 }
727 return 0;
728 }
729
730
731 /*
732 * Put add users to a group.
733 */
734 PUBLIC int
735 group(void *v)
736 {
737 const char **argv = v;
738 struct grouphead *gh;
739 struct group *gp;
740 int h;
741 int s;
742 const char *gname;
743 const char **ap, **p;
744
745 if (*argv == NULL) {
746 for (h = 0, s = 1; h < HSHSIZE; h++)
747 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
748 s++;
749 ap = salloc(s * sizeof(*ap));
750 for (h = 0, p = ap; h < HSHSIZE; h++)
751 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
752 *p++ = gh->g_name;
753 *p = NULL;
754 sort(ap);
755 for (p = ap; *p != NULL; p++)
756 printgroup(*p);
757 return 0;
758 }
759 if (argv[1] == NULL) {
760 printgroup(*argv);
761 return 0;
762 }
763 gname = *argv;
764 h = hash(gname);
765 if ((gh = findgroup(gname)) == NULL) {
766 gh = ecalloc(1, sizeof(*gh));
767 gh->g_name = vcopy(gname);
768 gh->g_list = NULL;
769 gh->g_link = groups[h];
770 groups[h] = gh;
771 }
772
773 /*
774 * Insert names from the command list into the group.
775 * Who cares if there are duplicates? They get tossed
776 * later anyway.
777 */
778
779 for (ap = argv + 1; *ap != NULL; ap++) {
780 gp = ecalloc(1, sizeof(*gp));
781 gp->ge_name = vcopy(*ap);
782 gp->ge_link = gh->g_list;
783 gh->g_list = gp;
784 }
785 return 0;
786 }
787
788 /*
789 * Delete the named group alias. Return zero if the group was
790 * successfully deleted, or -1 if there was no such group.
791 */
792 static int
793 delgroup(const char *name)
794 {
795 struct grouphead *gh, *p;
796 struct group *g;
797 int h;
798
799 h = hash(name);
800 for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link)
801 if (strcmp(gh->g_name, name) == 0) {
802 if (p == NULL)
803 groups[h] = gh->g_link;
804 else
805 p->g_link = gh->g_link;
806 while (gh->g_list != NULL) {
807 g = gh->g_list;
808 gh->g_list = g->ge_link;
809 free(g->ge_name);
810 free(g);
811 }
812 free(gh->g_name);
813 free(gh);
814 return 0;
815 }
816 return -1;
817 }
818
819 /*
820 * The unalias command takes a list of alises
821 * and discards the remembered groups of users.
822 */
823 PUBLIC int
824 unalias(void *v)
825 {
826 char **ap;
827
828 for (ap = v; *ap != NULL; ap++)
829 (void)delgroup(*ap);
830 return 0;
831 }
832
833 /*
834 * The do nothing command for comments.
835 */
836 /*ARGSUSED*/
837 PUBLIC int
838 null(void *v __unused)
839 {
840 return 0;
841 }
842
843 /*
844 * Change to another file. With no argument, print information about
845 * the current file.
846 */
847 PUBLIC int
848 file(void *v)
849 {
850 char **argv = v;
851
852 if (argv[0] == NULL) {
853 (void)newfileinfo(0);
854 return 0;
855 }
856 if (setfile(*argv) < 0)
857 return 1;
858 announce();
859
860 return 0;
861 }
862
863 /*
864 * Expand file names like echo
865 */
866 PUBLIC int
867 echo(void *v)
868 {
869 char **argv = v;
870 char **ap;
871 const char *cp;
872
873 for (ap = argv; *ap != NULL; ap++) {
874 cp = *ap;
875 if ((cp = expand(cp)) != NULL) {
876 if (ap != argv)
877 (void)putchar(' ');
878 (void)printf("%s", cp);
879 }
880 }
881 (void)putchar('\n');
882 return 0;
883 }
884
885 /*
886 * Routines to push and pop the condition code to support nested
887 * if/else/endif statements.
888 */
889 static void
890 push_cond(int c_cond)
891 {
892 struct cond_stack_s *csp;
893 csp = emalloc(sizeof(*csp));
894 csp->c_cond = c_cond;
895 csp->c_next = cond_stack;
896 cond_stack = csp;
897 }
898
899 static int
900 pop_cond(void)
901 {
902 int c_cond;
903 struct cond_stack_s *csp;
904
905 if ((csp = cond_stack) == NULL)
906 return -1;
907
908 c_cond = csp->c_cond;
909 cond_stack = csp->c_next;
910 free(csp);
911 return c_cond;
912 }
913
914 /*
915 * Conditional commands. These allow one to parameterize one's
916 * .mailrc and do some things if sending, others if receiving.
917 */
918 static int
919 if_push(void)
920 {
921 push_cond(cond);
922 cond &= ~CELSE;
923 if ((cond & (CIF | CSKIP)) == (CIF | CSKIP)) {
924 cond |= CIGN;
925 return 1;
926 }
927 return 0;
928 }
929
930 PUBLIC int
931 ifcmd(void *v)
932 {
933 char **argv = v;
934 char *keyword = argv[0];
935 static const struct modetbl_s {
936 const char *m_name;
937 enum mailmode_e m_mode;
938 } modetbl[] = {
939 { "receiving", mm_receiving },
940 { "sending", mm_sending },
941 { "headersonly", mm_hdrsonly },
942 { NULL, 0 },
943 };
944 const struct modetbl_s *mtp;
945
946 if (if_push())
947 return 0;
948
949 cond = CIF;
950 for (mtp = modetbl; mtp->m_name; mtp++)
951 if (strcasecmp(keyword, mtp->m_name) == 0)
952 break;
953
954 if (mtp->m_name == NULL) {
955 cond = CNONE;
956 (void)printf("Unrecognized if-keyword: \"%s\"\n", keyword);
957 return 1;
958 }
959 if (mtp->m_mode != mailmode)
960 cond |= CSKIP;
961
962 return 0;
963 }
964
965 PUBLIC int
966 ifdefcmd(void *v)
967 {
968 char **argv = v;
969
970 if (if_push())
971 return 0;
972
973 cond = CIF;
974 if (value(argv[0]) == NULL)
975 cond |= CSKIP;
976
977 return 0;
978 }
979
980 PUBLIC int
981 ifndefcmd(void *v)
982 {
983 int rval;
984 rval = ifdefcmd(v);
985 cond ^= CSKIP;
986 return rval;
987 }
988
989 /*
990 * Implement 'else'. This is pretty simple -- we just
991 * flip over the conditional flag.
992 */
993 /*ARGSUSED*/
994 PUBLIC int
995 elsecmd(void *v __unused)
996 {
997 if (cond_stack == NULL || (cond & (CIF | CELSE)) != CIF) {
998 (void)printf("\"else\" without matching \"if\"\n");
999 cond = CNONE;
1000 return 1;
1001 }
1002 if ((cond & CIGN) == 0) {
1003 cond ^= CSKIP;
1004 cond |= CELSE;
1005 }
1006 return 0;
1007 }
1008
1009 /*
1010 * End of if statement. Just set cond back to anything.
1011 */
1012 /*ARGSUSED*/
1013 PUBLIC int
1014 endifcmd(void *v __unused)
1015 {
1016 if (cond_stack == NULL || (cond & CIF) != CIF) {
1017 (void)printf("\"endif\" without matching \"if\"\n");
1018 cond = CNONE;
1019 return 1;
1020 }
1021 cond = pop_cond();
1022 return 0;
1023 }
1024
1025 /*
1026 * Set the list of alternate names.
1027 */
1028 PUBLIC int
1029 alternates(void *v)
1030 {
1031 char **namelist = v;
1032 size_t c;
1033 char **ap, **ap2, *cp;
1034
1035 c = argcount(namelist) + 1;
1036 if (c == 1) {
1037 if (altnames == 0)
1038 return 0;
1039 for (ap = altnames; *ap; ap++)
1040 (void)printf("%s ", *ap);
1041 (void)printf("\n");
1042 return 0;
1043 }
1044 if (altnames != 0)
1045 free(altnames);
1046 altnames = ecalloc(c, sizeof(char *));
1047 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
1048 cp = ecalloc(strlen(*ap) + 1, sizeof(char));
1049 (void)strcpy(cp, *ap);
1050 *ap2 = cp;
1051 }
1052 *ap2 = 0;
1053 return 0;
1054 }
1055