cmd3.c revision 1.33 1 /* $NetBSD: cmd3.c,v 1.33 2006/11/28 18:45:32 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.33 2006/11/28 18:45:32 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 int c;
181 FILE *f;
182
183 if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
184 warn(_PATH_HELP);
185 return 1;
186 }
187 while ((c = getc(f)) != EOF)
188 (void)putchar(c);
189 (void)Fclose(f);
190 return 0;
191 }
192
193 /*
194 * Change user's working directory.
195 */
196 PUBLIC int
197 schdir(void *v)
198 {
199 char **arglist = v;
200 const char *cp;
201
202 if (*arglist == NULL)
203 cp = homedir;
204 else
205 if ((cp = expand(*arglist)) == NULL)
206 return 1;
207 if (chdir(cp) < 0) {
208 warn("%s", cp);
209 return 1;
210 }
211 return 0;
212 }
213
214 /*
215 * Return the smopts field if "ReplyAsRecipient" is defined.
216 */
217 static struct name *
218 set_smopts(struct message *mp)
219 {
220 char *cp;
221 struct name *np = NULL;
222 char *reply_as_recipient = value(ENAME_REPLYASRECIPIENT);
223
224 if (reply_as_recipient &&
225 (cp = skin(hfield("to", mp))) != NULL &&
226 extract(cp, GTO)->n_flink == NULL) { /* check for one recipient */
227 char *p, *q;
228 size_t len = strlen(cp);
229 /*
230 * XXX - perhaps we always want to ignore
231 * "undisclosed-recipients:;" ?
232 */
233 for (p = q = reply_as_recipient; *p; p = q) {
234 while (*q != '\0' && *q != ',' && !isblank((unsigned char)*q))
235 q++;
236 if (p + len == q && strncasecmp(cp, p, len) == 0)
237 return np;
238 while (*q == ',' || isblank((unsigned char)*q))
239 q++;
240 }
241 np = extract(__UNCONST("-f"), GSMOPTS);
242 np = cat(np, extract(cp, GSMOPTS));
243 }
244
245 return np;
246 }
247
248 /*
249 * Modify the subject we are replying to to begin with Re: if
250 * it does not already.
251 */
252 static char *
253 reedit(char *subj)
254 {
255 char *newsubj;
256
257 if (subj == NULL)
258 return NULL;
259 if ((subj[0] == 'r' || subj[0] == 'R') &&
260 (subj[1] == 'e' || subj[1] == 'E') &&
261 subj[2] == ':')
262 return subj;
263 newsubj = salloc(strlen(subj) + 5);
264 (void)strcpy(newsubj, "Re: ");
265 (void)strcpy(newsubj + 4, subj);
266 return newsubj;
267 }
268
269 /*
270 * Set the "In-Reply-To" and "References" header fields appropriately.
271 * Used in replies.
272 */
273 static void
274 set_ident_fields(struct header *hp, struct message *mp)
275 {
276 char *in_reply_to;
277 char *references;
278
279 in_reply_to = hfield("message-id", mp);
280 hp->h_in_reply_to = in_reply_to;
281
282 references = hfield("references", mp);
283 hp->h_references = extract(references, GMISC);
284 hp->h_references = cat(hp->h_references, extract(in_reply_to, GMISC));
285 }
286
287 /*
288 * Reply to a list of messages. Extract each name from the
289 * message header and send them off to mail1()
290 */
291 static int
292 _respond(int *msgvec)
293 {
294 struct message *mp;
295 char *cp, *rcv, *replyto;
296 char **ap;
297 struct name *np;
298 struct header head;
299
300 /* ensure that all header fields are initially NULL */
301 (void)memset(&head, 0, sizeof(head));
302
303 if (msgvec[1] != 0) {
304 (void)printf("Sorry, can't reply to multiple messages at once\n");
305 return 1;
306 }
307 mp = get_message(msgvec[0]);
308 touch(mp);
309 dot = mp;
310 if ((rcv = skin(hfield("from", mp))) == NULL)
311 rcv = skin(nameof(mp, 1));
312 if ((replyto = skin(hfield("reply-to", mp))) != NULL)
313 np = extract(replyto, GTO);
314 else if ((cp = skin(hfield("to", mp))) != NULL)
315 np = extract(cp, GTO);
316 else
317 np = NULL;
318 np = elide(np);
319 /*
320 * Delete my name from the reply list,
321 * and with it, all my alternate names.
322 */
323 np = delname(np, myname);
324 if (altnames)
325 for (ap = altnames; *ap; ap++)
326 np = delname(np, *ap);
327 if (np != NULL && replyto == NULL)
328 np = cat(np, extract(rcv, GTO));
329 else if (np == NULL) {
330 if (replyto != NULL)
331 (void)printf("Empty reply-to field -- replying to author\n");
332 np = extract(rcv, GTO);
333 }
334 head.h_to = np;
335 if ((head.h_subject = hfield("subject", mp)) == NULL)
336 head.h_subject = hfield("subj", mp);
337 head.h_subject = reedit(head.h_subject);
338 if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) {
339 np = elide(extract(cp, GCC));
340 np = delname(np, myname);
341 if (altnames != 0)
342 for (ap = altnames; *ap; ap++)
343 np = delname(np, *ap);
344 head.h_cc = np;
345 } else
346 head.h_cc = NULL;
347 head.h_bcc = NULL;
348 head.h_smopts = set_smopts(mp);
349 #ifdef MIME_SUPPORT
350 head.h_attach = NULL;
351 #endif
352 set_ident_fields(&head, mp);
353 mail1(&head, 1);
354 return 0;
355 }
356
357 /*
358 * Reply to a series of messages by simply mailing to the senders
359 * and not messing around with the To: and Cc: lists as in normal
360 * reply.
361 */
362 static int
363 _Respond(int msgvec[])
364 {
365 struct header head;
366 struct message *mp;
367 int *ap;
368 char *cp;
369
370 /* ensure that all header fields are initially NULL */
371 (void)memset(&head, 0, sizeof(head));
372
373 head.h_to = NULL;
374 for (ap = msgvec; *ap != 0; ap++) {
375 mp = get_message(*ap);
376 touch(mp);
377 dot = mp;
378 if ((cp = skin(hfield("from", mp))) == NULL)
379 cp = skin(nameof(mp, 2));
380 head.h_to = cat(head.h_to, extract(cp, GTO));
381 }
382 if (head.h_to == NULL)
383 return 0;
384 mp = get_message(msgvec[0]);
385 if ((head.h_subject = hfield("subject", mp)) == NULL)
386 head.h_subject = hfield("subj", mp);
387 head.h_subject = reedit(head.h_subject);
388 head.h_cc = NULL;
389 head.h_bcc = NULL;
390 head.h_smopts = set_smopts(mp);
391 #ifdef MIME_SUPPORT
392 head.h_attach = NULL;
393 #endif
394 set_ident_fields(&head, mp);
395 mail1(&head, 1);
396 return 0;
397 }
398
399 PUBLIC int
400 respond(void *v)
401 {
402 int *msgvec = v;
403 if (value(ENAME_REPLYALL) == NULL)
404 return _respond(msgvec);
405 else
406 return _Respond(msgvec);
407 }
408
409 PUBLIC int
410 Respond(void *v)
411 {
412 int *msgvec = v;
413 if (value(ENAME_REPLYALL) == NULL)
414 return _Respond(msgvec);
415 else
416 return _respond(msgvec);
417 }
418
419 /*
420 * Preserve the named messages, so that they will be sent
421 * back to the system mailbox.
422 */
423 PUBLIC int
424 preserve(void *v)
425 {
426 int *msgvec = v;
427 int *ip;
428
429 if (edit) {
430 (void)printf("Cannot \"preserve\" in edit mode\n");
431 return 1;
432 }
433 for (ip = msgvec; *ip != 0; ip++)
434 dot = set_m_flag(*ip, ~(MBOX | MPRESERVE), MPRESERVE);
435
436 return 0;
437 }
438
439 /*
440 * Mark all given messages as unread, preserving the new status.
441 */
442 PUBLIC int
443 unread(void *v)
444 {
445 int *msgvec = v;
446 int *ip;
447
448 for (ip = msgvec; *ip != 0; ip++)
449 dot = set_m_flag(*ip, ~(MREAD | MTOUCH | MSTATUS), MSTATUS);
450
451 return 0;
452 }
453
454 /*
455 * Mark all given messages as read.
456 */
457 PUBLIC int
458 markread(void *v)
459 {
460 int *msgvec = v;
461 int *ip;
462
463 for (ip = msgvec; *ip != 0; ip++)
464 dot = set_m_flag(*ip,
465 ~(MNEW | MTOUCH | MREAD | MSTATUS), MREAD | MSTATUS);
466
467 return 0;
468 }
469
470 /*
471 * Print the size of each message.
472 */
473 PUBLIC int
474 messize(void *v)
475 {
476 int *msgvec = v;
477 struct message *mp;
478 int *ip, mesg;
479
480 for (ip = msgvec; *ip != 0; ip++) {
481 mesg = *ip;
482 mp = get_message(mesg);
483 (void)printf("%d: %ld/%llu\n", mesg, mp->m_blines,
484 (unsigned long long)mp->m_size);
485 }
486 return 0;
487 }
488
489 /*
490 * Quit quickly. If we are sourcing, just pop the input level
491 * by returning an error.
492 */
493 /*ARGSUSED*/
494 PUBLIC int
495 rexit(void *v __unused)
496 {
497 if (sourcing)
498 return 1;
499 exit(0);
500 /*NOTREACHED*/
501 }
502
503 /*
504 * Set or display a variable value. Syntax is similar to that
505 * of csh.
506 */
507 PUBLIC int
508 set(void *v)
509 {
510 const char **arglist = v;
511 struct var *vp;
512 const char *cp;
513 char varbuf[LINESIZE];
514 const char **ap, **p;
515 int errs, h, s;
516 size_t l;
517
518 if (*arglist == NULL) {
519 for (h = 0, s = 1; h < HSHSIZE; h++)
520 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
521 s++;
522 ap = salloc(s * sizeof *ap);
523 for (h = 0, p = ap; h < HSHSIZE; h++)
524 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
525 *p++ = vp->v_name;
526 *p = NULL;
527 sort(ap);
528 for (p = ap; *p != NULL; p++)
529 (void)printf("%s\t%s\n", *p, value(*p));
530 return 0;
531 }
532 errs = 0;
533 for (ap = arglist; *ap != NULL; ap++) {
534 cp = *ap;
535 while (*cp != '=' && *cp != '\0')
536 ++cp;
537 l = cp - *ap;
538 if (l >= sizeof varbuf)
539 l = sizeof(varbuf) - 1;
540 (void)strncpy(varbuf, *ap, l);
541 varbuf[l] = '\0';
542 if (*cp == '\0')
543 cp = "";
544 else
545 cp++;
546 if (equal(varbuf, "")) {
547 (void)printf("Non-null variable name required\n");
548 errs++;
549 continue;
550 }
551 assign(varbuf, cp);
552 }
553 return errs;
554 }
555
556 /*
557 * Unset a bunch of variable values.
558 */
559 PUBLIC int
560 unset(void *v)
561 {
562 char **arglist = v;
563 struct var *vp, *vp2;
564 int errs, h;
565 char **ap;
566
567 errs = 0;
568 for (ap = arglist; *ap != NULL; ap++) {
569 if ((vp2 = lookup(*ap)) == NULL) {
570 if (getenv(*ap)) {
571 (void)unsetenv(*ap);
572 } else if (!sourcing) {
573 (void)printf("\"%s\": undefined variable\n", *ap);
574 errs++;
575 }
576 continue;
577 }
578 h = hash(*ap);
579 if (vp2 == variables[h]) {
580 variables[h] = variables[h]->v_link;
581 v_free(vp2->v_name);
582 v_free(vp2->v_value);
583 free(vp2);
584 continue;
585 }
586 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
587 continue;
588 vp->v_link = vp2->v_link;
589 v_free(vp2->v_name);
590 v_free(vp2->v_value);
591 free(vp2);
592 }
593 return errs;
594 }
595
596 /*
597 * Show a variable value.
598 */
599 PUBLIC int
600 show(void *v)
601 {
602 const char **arglist = v;
603 struct var *vp;
604 const char **ap, **p;
605 int h, s;
606
607 if (*arglist == NULL) {
608 for (h = 0, s = 1; h < HSHSIZE; h++)
609 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
610 s++;
611 ap = salloc(s * sizeof *ap);
612 for (h = 0, p = ap; h < HSHSIZE; h++)
613 for (vp = variables[h]; vp != NULL; vp = vp->v_link)
614 *p++ = vp->v_name;
615 *p = NULL;
616 sort(ap);
617 for (p = ap; *p != NULL; p++)
618 (void)printf("%s=%s\n", *p, value(*p));
619 return 0;
620 }
621
622 for (ap = arglist; *ap != NULL; ap++) {
623 char *val = value(*ap);
624 (void)printf("%s=%s\n", *ap, val ? val : "<null>");
625 }
626 return 0;
627 }
628
629
630 /*
631 * Put add users to a group.
632 */
633 PUBLIC int
634 group(void *v)
635 {
636 const char **argv = v;
637 struct grouphead *gh;
638 struct group *gp;
639 int h;
640 int s;
641 const char *gname;
642 const char **ap, **p;
643
644 if (*argv == NULL) {
645 for (h = 0, s = 1; h < HSHSIZE; h++)
646 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
647 s++;
648 ap = salloc(s * sizeof *ap);
649 for (h = 0, p = ap; h < HSHSIZE; h++)
650 for (gh = groups[h]; gh != NULL; gh = gh->g_link)
651 *p++ = gh->g_name;
652 *p = NULL;
653 sort(ap);
654 for (p = ap; *p != NULL; p++)
655 printgroup(*p);
656 return 0;
657 }
658 if (argv[1] == NULL) {
659 printgroup(*argv);
660 return 0;
661 }
662 gname = *argv;
663 h = hash(gname);
664 if ((gh = findgroup(gname)) == NULL) {
665 gh = (struct grouphead *) ecalloc(1, sizeof *gh);
666 gh->g_name = vcopy(gname);
667 gh->g_list = NULL;
668 gh->g_link = groups[h];
669 groups[h] = gh;
670 }
671
672 /*
673 * Insert names from the command list into the group.
674 * Who cares if there are duplicates? They get tossed
675 * later anyway.
676 */
677
678 for (ap = argv + 1; *ap != NULL; ap++) {
679 gp = (struct group *) ecalloc(1, sizeof *gp);
680 gp->ge_name = vcopy(*ap);
681 gp->ge_link = gh->g_list;
682 gh->g_list = gp;
683 }
684 return 0;
685 }
686
687 /*
688 * Delete the named group alias. Return zero if the group was
689 * successfully deleted, or -1 if there was no such group.
690 */
691 static int
692 delgroup(const char *name)
693 {
694 struct grouphead *gh, *p;
695 struct group *g;
696 int h;
697
698 h = hash(name);
699 for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link)
700 if (strcmp(gh->g_name, name) == 0) {
701 if (p == NULL)
702 groups[h] = gh->g_link;
703 else
704 p->g_link = gh->g_link;
705 while (gh->g_list != NULL) {
706 g = gh->g_list;
707 gh->g_list = g->ge_link;
708 free(g->ge_name);
709 free(g);
710 }
711 free(gh->g_name);
712 free(gh);
713 return 0;
714 }
715 return -1;
716 }
717
718 /*
719 * The unalias command takes a list of alises
720 * and discards the remembered groups of users.
721 */
722 PUBLIC int
723 unalias(void *v)
724 {
725 char **ap;
726
727 for (ap = v; *ap != NULL; ap++)
728 (void)delgroup(*ap);
729 return 0;
730 }
731
732 /*
733 * The do nothing command for comments.
734 */
735 /*ARGSUSED*/
736 PUBLIC int
737 null(void *v __unused)
738 {
739 return 0;
740 }
741
742 /*
743 * Change to another file. With no argument, print information about
744 * the current file.
745 */
746 PUBLIC int
747 file(void *v)
748 {
749 char **argv = v;
750
751 if (argv[0] == NULL) {
752 (void)newfileinfo(0);
753 return 0;
754 }
755 if (setfile(*argv) < 0)
756 return 1;
757 announce();
758
759 return 0;
760 }
761
762 /*
763 * Expand file names like echo
764 */
765 PUBLIC int
766 echo(void *v)
767 {
768 char **argv = v;
769 char **ap;
770 const char *cp;
771
772 for (ap = argv; *ap != NULL; ap++) {
773 cp = *ap;
774 if ((cp = expand(cp)) != NULL) {
775 if (ap != argv)
776 (void)putchar(' ');
777 (void)printf("%s", cp);
778 }
779 }
780 (void)putchar('\n');
781 return 0;
782 }
783
784 /*
785 * Routines to push and pop the condition code to support nested
786 * if/else/endif statements.
787 */
788 static void
789 push_cond(int c_cond)
790 {
791 struct cond_stack_s *csp;
792 csp = emalloc(sizeof(*csp));
793 csp->c_cond = c_cond;
794 csp->c_next = cond_stack;
795 cond_stack = csp;
796 }
797
798 static int
799 pop_cond(void)
800 {
801 int c_cond;
802 struct cond_stack_s *csp;
803
804 if ((csp = cond_stack) == NULL)
805 return -1;
806
807 c_cond = csp->c_cond;
808 cond_stack = csp->c_next;
809 free(csp);
810 return c_cond;
811 }
812
813 /*
814 * Conditional commands. These allow one to parameterize one's
815 * .mailrc and do some things if sending, others if receiving.
816 */
817 static int
818 if_push(void)
819 {
820 push_cond(cond);
821 cond &= ~CELSE;
822 if ((cond & (CIF | CSKIP)) == (CIF | CSKIP)) {
823 cond |= CIGN;
824 return 1;
825 }
826 return 0;
827 }
828
829 PUBLIC int
830 ifcmd(void *v)
831 {
832 char **argv = v;
833 char *keyword = argv[0];
834 static const struct modetbl_s {
835 const char *m_name;
836 enum mailmode_e m_mode;
837 } modetbl[] = {
838 { "receiving", mm_receiving },
839 { "sending", mm_sending },
840 { "headersonly", mm_hdrsonly },
841 { NULL, 0 },
842 };
843 const struct modetbl_s *mtp;
844
845 if (if_push())
846 return 0;
847
848 cond = CIF;
849 for (mtp = modetbl; mtp->m_name; mtp++)
850 if (strcasecmp(keyword, mtp->m_name) == 0)
851 break;
852
853 if (mtp->m_name == NULL) {
854 cond = CNONE;
855 (void)printf("Unrecognized if-keyword: \"%s\"\n", keyword);
856 return 1;
857 }
858 if (mtp->m_mode != mailmode)
859 cond |= CSKIP;
860
861 return 0;
862 }
863
864 PUBLIC int
865 ifdefcmd(void *v)
866 {
867 char **argv = v;
868
869 if (if_push())
870 return 0;
871
872 cond = CIF;
873 if (value(argv[0]) == NULL)
874 cond |= CSKIP;
875
876 return 0;
877 }
878
879 PUBLIC int
880 ifndefcmd(void *v)
881 {
882 int rval;
883 rval = ifdefcmd(v);
884 cond ^= CSKIP;
885 return rval;
886 }
887
888 /*
889 * Implement 'else'. This is pretty simple -- we just
890 * flip over the conditional flag.
891 */
892 /*ARGSUSED*/
893 PUBLIC int
894 elsecmd(void *v __unused)
895 {
896 if (cond_stack == NULL || (cond & (CIF | CELSE)) != CIF) {
897 (void)printf("\"else\" without matching \"if\"\n");
898 cond = CNONE;
899 return 1;
900 }
901 if ((cond & CIGN) == 0) {
902 cond ^= CSKIP;
903 cond |= CELSE;
904 }
905 return 0;
906 }
907
908 /*
909 * End of if statement. Just set cond back to anything.
910 */
911 /*ARGSUSED*/
912 PUBLIC int
913 endifcmd(void *v __unused)
914 {
915 if (cond_stack == NULL || (cond & CIF) != CIF) {
916 (void)printf("\"endif\" without matching \"if\"\n");
917 cond = CNONE;
918 return 1;
919 }
920 cond = pop_cond();
921 return 0;
922 }
923
924 /*
925 * Set the list of alternate names.
926 */
927 PUBLIC int
928 alternates(void *v)
929 {
930 char **namelist = v;
931 int c;
932 char **ap, **ap2, *cp;
933
934 c = argcount(namelist) + 1;
935 if (c == 1) {
936 if (altnames == 0)
937 return 0;
938 for (ap = altnames; *ap; ap++)
939 (void)printf("%s ", *ap);
940 (void)printf("\n");
941 return 0;
942 }
943 if (altnames != 0)
944 free(altnames);
945 altnames = (char **) ecalloc((unsigned) c, sizeof (char *));
946 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
947 cp = ecalloc((unsigned) strlen(*ap) + 1, sizeof (char));
948 (void)strcpy(cp, *ap);
949 *ap2 = cp;
950 }
951 *ap2 = 0;
952 return 0;
953 }
954