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 35static 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 */ 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 cathelp(_PATH_HELP); 181 return 0; 182} 183 184/* 185 * Change user's working directory. 186 */ 187PUBLIC int 188schdir(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 */ 208static struct name * 209set_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 */ 243static char * 244reedit(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 */ 264static void 265set_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 */ 282static 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 */ 353static 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 390PUBLIC int 391respond(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 400PUBLIC int 401Respond(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 */ 414PUBLIC int 415preserve(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 */ 433PUBLIC int 434unread(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 */ 448PUBLIC int 449markread(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 */ 464PUBLIC int 465messize(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*/ 485PUBLIC int 486rexit(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 */ 498PUBLIC int 499set(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 */ 550PUBLIC int 551unset(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 */ 590PUBLIC int 591show(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 */ 624PUBLIC int 625group(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 */ 682static int 683delgroup(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 */ 713PUBLIC int 714unalias(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*/ 727PUBLIC int 728null(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 */ 737PUBLIC int 738file(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 */ 756PUBLIC int 757echo(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 */ 779static void 780push_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 789static int 790pop_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 */ 808static int 809if_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 820PUBLIC int 821ifcmd(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 855PUBLIC int 856ifdefcmd(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 870PUBLIC int 871ifndefcmd(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*/ 884PUBLIC int 885elsecmd(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*/ 903PUBLIC int 904endifcmd(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 */ 918PUBLIC int 919alternates(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