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 35static 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 */ 58static int 59diction(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 */ 68PUBLIC void 69sort(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 */ 84static int 85bangexp(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)) { 99overf: 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 */ 135PUBLIC int 136shell(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*/ 158PUBLIC int 159dosh(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*/ 177PUBLIC int 178help(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 */ 196PUBLIC int 197schdir(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 */ 217static struct name * 218set_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 */ 252static char * 253reedit(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 */ 273static void 274set_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 */ 291static 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 */ 362static 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 399PUBLIC int 400respond(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 409PUBLIC int 410Respond(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 */ 423PUBLIC int 424preserve(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 */ 442PUBLIC int 443unread(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 */ 457PUBLIC int 458markread(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 */ 473PUBLIC int 474messize(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*/ 494PUBLIC int 495rexit(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 */ 507PUBLIC int 508set(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 */ 559PUBLIC int 560unset(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 */ 599PUBLIC int 600show(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 */ 633PUBLIC int 634group(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 */ 691static int 692delgroup(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 */ 722PUBLIC int 723unalias(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*/ 736PUBLIC int 737null(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 */ 746PUBLIC int 747file(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 */ 765PUBLIC int 766echo(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 */ 788static void 789push_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 798static int 799pop_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 */ 817static int 818if_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 829PUBLIC int 830ifcmd(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 864PUBLIC int 865ifdefcmd(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 879PUBLIC int 880ifndefcmd(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*/ 893PUBLIC int 894elsecmd(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*/ 912PUBLIC int 913endifcmd(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 */ 927PUBLIC int 928alternates(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