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