cmd3.c revision 1.34 1 /* $NetBSD: cmd3.c,v 1.34 2007/06/05 17:50:22 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.34 2007/06/05 17:50:22 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 != ',' && !isblank((unsigned char)*q))
226 q++;
227 if (p + len == q && strncasecmp(cp, p, len) == 0)
228 return np;
229 while (*q == ',' || isblank((unsigned char)*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(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(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(msgvec);
396 else
397 return _Respond(msgvec);
398 }
399
400 PUBLIC int
401 Respond(void *v)
402 {
403 int *msgvec = v;
404 if (value(ENAME_REPLYALL) == NULL)
405 return _Respond(msgvec);
406 else
407 return _respond(msgvec);
408 }
409
410 /*
411 * Preserve the named messages, so that they will be sent
412 * back to the system mailbox.
413 */
414 PUBLIC int
415 preserve(void *v)
416 {
417 int *msgvec = v;
418 int *ip;
419
420 if (edit) {
421 (void)printf("Cannot \"preserve\" in edit mode\n");
422 return 1;
423 }
424 for (ip = msgvec; *ip != 0; ip++)
425 dot = set_m_flag(*ip, ~(MBOX | MPRESERVE), MPRESERVE);
426
427 return 0;
428 }
429
430 /*
431 * Mark all given messages as unread, preserving the new status.
432 */
433 PUBLIC int
434 unread(void *v)
435 {
436 int *msgvec = v;
437 int *ip;
438
439 for (ip = msgvec; *ip != 0; ip++)
440 dot = set_m_flag(*ip, ~(MREAD | MTOUCH | MSTATUS), MSTATUS);
441
442 return 0;
443 }
444
445 /*
446 * Mark all given messages as read.
447 */
448 PUBLIC int
449 markread(void *v)
450 {
451 int *msgvec = v;
452 int *ip;
453
454 for (ip = msgvec; *ip != 0; ip++)
455 dot = set_m_flag(*ip,
456 ~(MNEW | MTOUCH | MREAD | MSTATUS), MREAD | MSTATUS);
457
458 return 0;
459 }
460
461 /*
462 * Print the size of each message.
463 */
464 PUBLIC int
465 messize(void *v)
466 {
467 int *msgvec = v;
468 struct message *mp;
469 int *ip, mesg;
470
471 for (ip = msgvec; *ip != 0; ip++) {
472 mesg = *ip;
473 mp = get_message(mesg);
474 (void)printf("%d: %ld/%llu\n", mesg, mp->m_blines,
475 (unsigned long long)mp->m_size);
476 }
477 return 0;
478 }
479
480 /*
481 * Quit quickly. If we are sourcing, just pop the input level
482 * by returning an error.
483 */
484 /*ARGSUSED*/
485 PUBLIC int
486 rexit(void *v __unused)
487 {
488 if (sourcing)
489 return 1;
490 exit(0);
491 /*NOTREACHED*/
492 }
493
494 /*
495 * Set or display a variable value. Syntax is similar to that
496 * of csh.
497 */
498 PUBLIC int
499 set(void *v)
500 {
501 const char **arglist = v;
502 struct var *vp;
503 const char *cp;
504 char varbuf[LINESIZE];
505 const char **ap, **p;
506 int errs, h, s;
507 size_t l;
508
509 if (*arglist == NULL) {
510 for (h = 0, s = 1; h < HSHSIZE; h++)
511 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
512 s++;
513 ap = salloc(s * sizeof *ap);
514 for (h = 0, p = ap; h < HSHSIZE; h++)
515 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
516 *p++ = vp->v_name;
517 *p = NULL;
518 sort(ap);
519 for (p = ap; *p != NULL; p++)
520 (void)printf("%s\t%s\n", *p, value(*p));
521 return 0;
522 }
523 errs = 0;
524 for (ap = arglist; *ap != NULL; ap++) {
525 cp = *ap;
526 while (*cp != '=' && *cp != '\0')
527 ++cp;
528 l = cp - *ap;
529 if (l >= sizeof varbuf)
530 l = sizeof(varbuf) - 1;
531 (void)strncpy(varbuf, *ap, l);
532 varbuf[l] = '\0';
533 if (*cp == '\0')
534 cp = "";
535 else
536 cp++;
537 if (equal(varbuf, "")) {
538 (void)printf("Non-null variable name required\n");
539 errs++;
540 continue;
541 }
542 assign(varbuf, cp);
543 }
544 return errs;
545 }
546
547 /*
548 * Unset a bunch of variable values.
549 */
550 PUBLIC int
551 unset(void *v)
552 {
553 char **arglist = v;
554 struct var *vp, *vp2;
555 int errs, h;
556 char **ap;
557
558 errs = 0;
559 for (ap = arglist; *ap != NULL; ap++) {
560 if ((vp2 = lookup(*ap)) == NULL) {
561 if (getenv(*ap)) {
562 (void)unsetenv(*ap);
563 } else if (!sourcing) {
564 (void)printf("\"%s\": undefined variable\n", *ap);
565 errs++;
566 }
567 continue;
568 }
569 h = hash(*ap);
570 if (vp2 == variables[h]) {
571 variables[h] = variables[h]->v_link;
572 v_free(vp2->v_name);
573 v_free(vp2->v_value);
574 free(vp2);
575 continue;
576 }
577 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
578 continue;
579 vp->v_link = vp2->v_link;
580 v_free(vp2->v_name);
581 v_free(vp2->v_value);
582 free(vp2);
583 }
584 return errs;
585 }
586
587 /*
588 * Show a variable value.
589 */
590 PUBLIC int
591 show(void *v)
592 {
593 const char **arglist = v;
594 struct var *vp;
595 const char **ap, **p;
596 int h, s;
597
598 if (*arglist == NULL) {
599 for (h = 0, s = 1; h < HSHSIZE; h++)
600 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
601 s++;
602 ap = salloc(s * sizeof *ap);
603 for (h = 0, p = ap; h < HSHSIZE; h++)
604 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
605 *p++ = vp->v_name;
606 *p = NULL;
607 sort(ap);
608 for (p = ap; *p != NULL; p++)
609 (void)printf("%s=%s\n", *p, value(*p));
610 return 0;
611 }
612
613 for (ap = arglist; *ap != NULL; ap++) {
614 char *val = value(*ap);
615 (void)printf("%s=%s\n", *ap, val ? val : "<null>");
616 }
617 return 0;
618 }
619
620
621 /*
622 * Put add users to a group.
623 */
624 PUBLIC int
625 group(void *v)
626 {
627 const char **argv = v;
628 struct grouphead *gh;
629 struct group *gp;
630 int h;
631 int s;
632 const char *gname;
633 const char **ap, **p;
634
635 if (*argv == NULL) {
636 for (h = 0, s = 1; h < HSHSIZE; h++)
637 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
638 s++;
639 ap = salloc(s * sizeof *ap);
640 for (h = 0, p = ap; h < HSHSIZE; h++)
641 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
642 *p++ = gh->g_name;
643 *p = NULL;
644 sort(ap);
645 for (p = ap; *p != NULL; p++)
646 printgroup(*p);
647 return 0;
648 }
649 if (argv[1] == NULL) {
650 printgroup(*argv);
651 return 0;
652 }
653 gname = *argv;
654 h = hash(gname);
655 if ((gh = findgroup(gname)) == NULL) {
656 gh = (struct grouphead *) ecalloc(1, sizeof *gh);
657 gh->g_name = vcopy(gname);
658 gh->g_list = NULL;
659 gh->g_link = groups[h];
660 groups[h] = gh;
661 }
662
663 /*
664 * Insert names from the command list into the group.
665 * Who cares if there are duplicates? They get tossed
666 * later anyway.
667 */
668
669 for (ap = argv + 1; *ap != NULL; ap++) {
670 gp = (struct group *) ecalloc(1, sizeof *gp);
671 gp->ge_name = vcopy(*ap);
672 gp->ge_link = gh->g_list;
673 gh->g_list = gp;
674 }
675 return 0;
676 }
677
678 /*
679 * Delete the named group alias. Return zero if the group was
680 * successfully deleted, or -1 if there was no such group.
681 */
682 static int
683 delgroup(const char *name)
684 {
685 struct grouphead *gh, *p;
686 struct group *g;
687 int h;
688
689 h = hash(name);
690 for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link)
691 if (strcmp(gh->g_name, name) == 0) {
692 if (p == NULL)
693 groups[h] = gh->g_link;
694 else
695 p->g_link = gh->g_link;
696 while (gh->g_list != NULL) {
697 g = gh->g_list;
698 gh->g_list = g->ge_link;
699 free(g->ge_name);
700 free(g);
701 }
702 free(gh->g_name);
703 free(gh);
704 return 0;
705 }
706 return -1;
707 }
708
709 /*
710 * The unalias command takes a list of alises
711 * and discards the remembered groups of users.
712 */
713 PUBLIC int
714 unalias(void *v)
715 {
716 char **ap;
717
718 for (ap = v; *ap != NULL; ap++)
719 (void)delgroup(*ap);
720 return 0;
721 }
722
723 /*
724 * The do nothing command for comments.
725 */
726 /*ARGSUSED*/
727 PUBLIC int
728 null(void *v __unused)
729 {
730 return 0;
731 }
732
733 /*
734 * Change to another file. With no argument, print information about
735 * the current file.
736 */
737 PUBLIC int
738 file(void *v)
739 {
740 char **argv = v;
741
742 if (argv[0] == NULL) {
743 (void)newfileinfo(0);
744 return 0;
745 }
746 if (setfile(*argv) < 0)
747 return 1;
748 announce();
749
750 return 0;
751 }
752
753 /*
754 * Expand file names like echo
755 */
756 PUBLIC int
757 echo(void *v)
758 {
759 char **argv = v;
760 char **ap;
761 const char *cp;
762
763 for (ap = argv; *ap != NULL; ap++) {
764 cp = *ap;
765 if ((cp = expand(cp)) != NULL) {
766 if (ap != argv)
767 (void)putchar(' ');
768 (void)printf("%s", cp);
769 }
770 }
771 (void)putchar('\n');
772 return 0;
773 }
774
775 /*
776 * Routines to push and pop the condition code to support nested
777 * if/else/endif statements.
778 */
779 static void
780 push_cond(int c_cond)
781 {
782 struct cond_stack_s *csp;
783 csp = emalloc(sizeof(*csp));
784 csp->c_cond = c_cond;
785 csp->c_next = cond_stack;
786 cond_stack = csp;
787 }
788
789 static int
790 pop_cond(void)
791 {
792 int c_cond;
793 struct cond_stack_s *csp;
794
795 if ((csp = cond_stack) == NULL)
796 return -1;
797
798 c_cond = csp->c_cond;
799 cond_stack = csp->c_next;
800 free(csp);
801 return c_cond;
802 }
803
804 /*
805 * Conditional commands. These allow one to parameterize one's
806 * .mailrc and do some things if sending, others if receiving.
807 */
808 static int
809 if_push(void)
810 {
811 push_cond(cond);
812 cond &= ~CELSE;
813 if ((cond & (CIF | CSKIP)) == (CIF | CSKIP)) {
814 cond |= CIGN;
815 return 1;
816 }
817 return 0;
818 }
819
820 PUBLIC int
821 ifcmd(void *v)
822 {
823 char **argv = v;
824 char *keyword = argv[0];
825 static const struct modetbl_s {
826 const char *m_name;
827 enum mailmode_e m_mode;
828 } modetbl[] = {
829 { "receiving", mm_receiving },
830 { "sending", mm_sending },
831 { "headersonly", mm_hdrsonly },
832 { NULL, 0 },
833 };
834 const struct modetbl_s *mtp;
835
836 if (if_push())
837 return 0;
838
839 cond = CIF;
840 for (mtp = modetbl; mtp->m_name; mtp++)
841 if (strcasecmp(keyword, mtp->m_name) == 0)
842 break;
843
844 if (mtp->m_name == NULL) {
845 cond = CNONE;
846 (void)printf("Unrecognized if-keyword: \"%s\"\n", keyword);
847 return 1;
848 }
849 if (mtp->m_mode != mailmode)
850 cond |= CSKIP;
851
852 return 0;
853 }
854
855 PUBLIC int
856 ifdefcmd(void *v)
857 {
858 char **argv = v;
859
860 if (if_push())
861 return 0;
862
863 cond = CIF;
864 if (value(argv[0]) == NULL)
865 cond |= CSKIP;
866
867 return 0;
868 }
869
870 PUBLIC int
871 ifndefcmd(void *v)
872 {
873 int rval;
874 rval = ifdefcmd(v);
875 cond ^= CSKIP;
876 return rval;
877 }
878
879 /*
880 * Implement 'else'. This is pretty simple -- we just
881 * flip over the conditional flag.
882 */
883 /*ARGSUSED*/
884 PUBLIC int
885 elsecmd(void *v __unused)
886 {
887 if (cond_stack == NULL || (cond & (CIF | CELSE)) != CIF) {
888 (void)printf("\"else\" without matching \"if\"\n");
889 cond = CNONE;
890 return 1;
891 }
892 if ((cond & CIGN) == 0) {
893 cond ^= CSKIP;
894 cond |= CELSE;
895 }
896 return 0;
897 }
898
899 /*
900 * End of if statement. Just set cond back to anything.
901 */
902 /*ARGSUSED*/
903 PUBLIC int
904 endifcmd(void *v __unused)
905 {
906 if (cond_stack == NULL || (cond & CIF) != CIF) {
907 (void)printf("\"endif\" without matching \"if\"\n");
908 cond = CNONE;
909 return 1;
910 }
911 cond = pop_cond();
912 return 0;
913 }
914
915 /*
916 * Set the list of alternate names.
917 */
918 PUBLIC int
919 alternates(void *v)
920 {
921 char **namelist = v;
922 int c;
923 char **ap, **ap2, *cp;
924
925 c = argcount(namelist) + 1;
926 if (c == 1) {
927 if (altnames == 0)
928 return 0;
929 for (ap = altnames; *ap; ap++)
930 (void)printf("%s ", *ap);
931 (void)printf("\n");
932 return 0;
933 }
934 if (altnames != 0)
935 free(altnames);
936 altnames = (char **) ecalloc((unsigned) c, sizeof (char *));
937 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
938 cp = ecalloc((unsigned) strlen(*ap) + 1, sizeof (char));
939 (void)strcpy(cp, *ap);
940 *ap2 = cp;
941 }
942 *ap2 = 0;
943 return 0;
944 }
945