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