cmd3.c revision 1.31
1/* $NetBSD: cmd3.c,v 1.31 2006/10/21 21:37:20 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.31 2006/10/21 21:37:20 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 46/* 47 * Mail -- a mail program 48 * 49 * Still more user commands. 50 */ 51static int delgroup(const char *); 52static int diction(const void *, const void *); 53 54/* 55 * Process a shell escape by saving signals, ignoring signals, 56 * and forking a sh -c 57 */ 58int 59shell(void *v) 60{ 61 char *str = v; 62 sig_t sigint = signal(SIGINT, SIG_IGN); 63 const char *shellcmd; 64 char cmd[BUFSIZ]; 65 66 (void)strcpy(cmd, str); 67 if (bangexp(cmd) < 0) 68 return 1; 69 if ((shellcmd = value("SHELL")) == NULL) 70 shellcmd = _PATH_CSHELL; 71 (void)run_command(shellcmd, 0, 0, 1, "-c", cmd, NULL); 72 (void)signal(SIGINT, sigint); 73 (void)printf("!\n"); 74 return 0; 75} 76 77/* 78 * Fork an interactive shell. 79 */ 80/*ARGSUSED*/ 81int 82dosh(void *v __unused) 83{ 84 sig_t sigint = signal(SIGINT, SIG_IGN); 85 const char *shellcmd; 86 87 if ((shellcmd = value("SHELL")) == NULL) 88 shellcmd = _PATH_CSHELL; 89 (void)run_command(shellcmd, 0, 0, 1, NULL); 90 (void)signal(SIGINT, sigint); 91 (void)putchar('\n'); 92 return 0; 93} 94 95/* 96 * Expand the shell escape by expanding unescaped !'s into the 97 * last issued command where possible. 98 */ 99 100char lastbang[128]; 101 102int 103bangexp(char *str) 104{ 105 char bangbuf[BUFSIZ]; 106 char *cp, *cp2; 107 int n; 108 int changed = 0; 109 110 cp = str; 111 cp2 = bangbuf; 112 n = BUFSIZ; 113 while (*cp) { 114 if (*cp == '!') { 115 if (n < strlen(lastbang)) { 116overf: 117 (void)printf("Command buffer overflow\n"); 118 return(-1); 119 } 120 changed++; 121 (void)strcpy(cp2, lastbang); 122 cp2 += strlen(lastbang); 123 n -= strlen(lastbang); 124 cp++; 125 continue; 126 } 127 if (*cp == '\\' && cp[1] == '!') { 128 if (--n <= 1) 129 goto overf; 130 *cp2++ = '!'; 131 cp += 2; 132 changed++; 133 } 134 if (--n <= 1) 135 goto overf; 136 *cp2++ = *cp++; 137 } 138 *cp2 = 0; 139 if (changed) { 140 (void)printf("!%s\n", bangbuf); 141 (void)fflush(stdout); 142 } 143 (void)strcpy(str, bangbuf); 144 (void)strncpy(lastbang, bangbuf, 128); 145 lastbang[127] = 0; 146 return(0); 147} 148 149/* 150 * Print out a nice help message from some file or another. 151 */ 152 153int 154/*ARGSUSED*/ 155help(void *v __unused) 156{ 157 int c; 158 FILE *f; 159 160 if ((f = Fopen(_PATH_HELP, "r")) == NULL) { 161 warn(_PATH_HELP); 162 return(1); 163 } 164 while ((c = getc(f)) != EOF) 165 (void)putchar(c); 166 (void)Fclose(f); 167 return(0); 168} 169 170/* 171 * Change user's working directory. 172 */ 173int 174schdir(void *v) 175{ 176 char **arglist = v; 177 const char *cp; 178 179 if (*arglist == NULL) 180 cp = homedir; 181 else 182 if ((cp = expand(*arglist)) == NULL) 183 return(1); 184 if (chdir(cp) < 0) { 185 warn("%s", cp); 186 return(1); 187 } 188 return 0; 189} 190 191int 192respond(void *v) 193{ 194 int *msgvec = v; 195 if (value("Replyall") == NULL) 196 return (_respond(msgvec)); 197 else 198 return (_Respond(msgvec)); 199} 200 201static struct name * 202set_smopts(struct message *mp) 203{ 204 char *cp; 205 struct name *np = NULL; 206 char *reply_as_recipient = value("ReplyAsRecipient"); 207 208 if (reply_as_recipient && 209 (cp = skin(hfield("to", mp))) != NULL && 210 extract(cp, GTO)->n_flink == NULL) { /* check for one recipient */ 211 char *p, *q; 212 size_t len = strlen(cp); 213 /* 214 * XXX - perhaps we always want to ignore 215 * "undisclosed-recipients:;" ? 216 */ 217 for (p = q = reply_as_recipient; *p; p = q) { 218 while (*q != '\0' && *q != ',' && !isblank((unsigned char)*q)) 219 q++; 220 if (p + len == q && strncasecmp(cp, p, len) == 0) 221 return np; 222 while (*q == ',' || isblank((unsigned char)*q)) 223 q++; 224 } 225 np = extract(__UNCONST("-f"), GSMOPTS); 226 np = cat(np, extract(cp, GSMOPTS)); 227 } 228 229 return np; 230} 231 232/* 233 * Reply to a list of messages. Extract each name from the 234 * message header and send them off to mail1() 235 */ 236int 237_respond(int *msgvec) 238{ 239 struct message *mp; 240 char *cp, *rcv, *replyto; 241 char **ap; 242 struct name *np; 243 struct header head; 244 245 if (msgvec[1] != 0) { 246 (void)printf("Sorry, can't reply to multiple messages at once\n"); 247 return(1); 248 } 249 mp = &message[msgvec[0] - 1]; 250 touch(mp); 251 dot = mp; 252 if ((rcv = skin(hfield("from", mp))) == NULL) 253 rcv = skin(nameof(mp, 1)); 254 if ((replyto = skin(hfield("reply-to", mp))) != NULL) 255 np = extract(replyto, GTO); 256 else if ((cp = skin(hfield("to", mp))) != NULL) 257 np = extract(cp, GTO); 258 else 259 np = NULL; 260 np = elide(np); 261 /* 262 * Delete my name from the reply list, 263 * and with it, all my alternate names. 264 */ 265 np = delname(np, myname); 266 if (altnames) 267 for (ap = altnames; *ap; ap++) 268 np = delname(np, *ap); 269 if (np != NULL && replyto == NULL) 270 np = cat(np, extract(rcv, GTO)); 271 else if (np == NULL) { 272 if (replyto != NULL) 273 (void)printf("Empty reply-to field -- replying to author\n"); 274 np = extract(rcv, GTO); 275 } 276 head.h_to = np; 277 if ((head.h_subject = hfield("subject", mp)) == NULL) 278 head.h_subject = hfield("subj", mp); 279 head.h_subject = reedit(head.h_subject); 280 if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) { 281 np = elide(extract(cp, GCC)); 282 np = delname(np, myname); 283 if (altnames != 0) 284 for (ap = altnames; *ap; ap++) 285 np = delname(np, *ap); 286 head.h_cc = np; 287 } else 288 head.h_cc = NULL; 289 head.h_bcc = NULL; 290 head.h_smopts = set_smopts(mp); 291#ifdef MIME_SUPPORT 292 head.h_attach = NULL; 293#endif 294 mail1(&head, 1); 295 return(0); 296} 297 298/* 299 * Modify the subject we are replying to to begin with Re: if 300 * it does not already. 301 */ 302char * 303reedit(char *subj) 304{ 305 char *newsubj; 306 307 if (subj == NULL) 308 return NULL; 309 if ((subj[0] == 'r' || subj[0] == 'R') && 310 (subj[1] == 'e' || subj[1] == 'E') && 311 subj[2] == ':') 312 return subj; 313 newsubj = salloc(strlen(subj) + 5); 314 (void)strcpy(newsubj, "Re: "); 315 (void)strcpy(newsubj + 4, subj); 316 return newsubj; 317} 318 319/* 320 * Preserve the named messages, so that they will be sent 321 * back to the system mailbox. 322 */ 323int 324preserve(void *v) 325{ 326 int *msgvec = v; 327 struct message *mp; 328 int *ip, mesg; 329 330 if (edit) { 331 (void)printf("Cannot \"preserve\" in edit mode\n"); 332 return(1); 333 } 334 for (ip = msgvec; *ip != 0; ip++) { 335 mesg = *ip; 336 mp = &message[mesg - 1]; 337 mp->m_flag |= MPRESERVE; 338 mp->m_flag &= ~MBOX; 339 dot = mp; 340 } 341 return(0); 342} 343 344/* 345 * Mark all given messages as unread. 346 */ 347int 348unread(void *v) 349{ 350 int *msgvec = v; 351 int *ip; 352 353 for (ip = msgvec; *ip != 0; ip++) { 354 dot = &message[*ip - 1]; 355 dot->m_flag &= ~(MREAD|MTOUCH); 356 dot->m_flag |= MSTATUS; 357 } 358 return(0); 359} 360 361/* 362 * Print the size of each message. 363 */ 364int 365messize(void *v) 366{ 367 int *msgvec = v; 368 struct message *mp; 369 int *ip, mesg; 370 371 for (ip = msgvec; *ip != 0; ip++) { 372 mesg = *ip; 373 mp = &message[mesg - 1]; 374 (void)printf("%d: %ld/%llu\n", mesg, mp->m_blines, 375 (unsigned long long)mp->m_size); 376 } 377 return(0); 378} 379 380/* 381 * Quit quickly. If we are sourcing, just pop the input level 382 * by returning an error. 383 */ 384int 385/*ARGSUSED*/ 386rexit(void *v __unused) 387{ 388 if (sourcing) 389 return(1); 390 exit(0); 391 /*NOTREACHED*/ 392} 393 394/* 395 * Set or display a variable value. Syntax is similar to that 396 * of csh. 397 */ 398int 399set(void *v) 400{ 401 char **arglist = v; 402 struct var *vp; 403 const char *cp; 404 char varbuf[BUFSIZ], **ap, **p; 405 int errs, h, s; 406 size_t l; 407 408 if (*arglist == NULL) { 409 for (h = 0, s = 1; h < HSHSIZE; h++) 410 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 411 s++; 412 ap = salloc(s * sizeof *ap); 413 for (h = 0, p = ap; h < HSHSIZE; h++) 414 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 415 *p++ = vp->v_name; 416 *p = NULL; 417 sort(ap); 418 for (p = ap; *p != NULL; p++) 419 (void)printf("%s\t%s\n", *p, value(*p)); 420 return(0); 421 } 422 errs = 0; 423 for (ap = arglist; *ap != NULL; ap++) { 424 cp = *ap; 425 while (*cp != '=' && *cp != '\0') 426 ++cp; 427 l = cp - *ap; 428 if (l >= sizeof varbuf) 429 l = sizeof varbuf - 1; 430 (void)strncpy(varbuf, *ap, l); 431 varbuf[l] = '\0'; 432 if (*cp == '\0') 433 cp = ""; 434 else 435 cp++; 436 if (equal(varbuf, "")) { 437 (void)printf("Non-null variable name required\n"); 438 errs++; 439 continue; 440 } 441 assign(varbuf, cp); 442 } 443 return(errs); 444} 445 446/* 447 * Unset a bunch of variable values. 448 */ 449int 450unset(void *v) 451{ 452 char **arglist = v; 453 struct var *vp, *vp2; 454 int errs, h; 455 char **ap; 456 457 errs = 0; 458 for (ap = arglist; *ap != NULL; ap++) { 459 if ((vp2 = lookup(*ap)) == NULL) { 460 if (getenv(*ap)) { 461 (void)unsetenv(*ap); 462 } else if (!sourcing) { 463 (void)printf("\"%s\": undefined variable\n", *ap); 464 errs++; 465 } 466 continue; 467 } 468 h = hash(*ap); 469 if (vp2 == variables[h]) { 470 variables[h] = variables[h]->v_link; 471 v_free(vp2->v_name); 472 v_free(vp2->v_value); 473 free(vp2); 474 continue; 475 } 476 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link) 477 ; 478 vp->v_link = vp2->v_link; 479 v_free(vp2->v_name); 480 v_free(vp2->v_value); 481 free(vp2); 482 } 483 return(errs); 484} 485 486/* 487 * Show a variable value. 488 */ 489int 490show(void *v) 491{ 492 char **arglist = v; 493 struct var *vp; 494 char **ap, **p; 495 int h, s; 496 497 if (*arglist == NULL) { 498 for (h = 0, s = 1; h < HSHSIZE; h++) 499 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 500 s++; 501 ap = salloc(s * sizeof *ap); 502 for (h = 0, p = ap; h < HSHSIZE; h++) 503 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 504 *p++ = vp->v_name; 505 *p = NULL; 506 sort(ap); 507 for (p = ap; *p != NULL; p++) 508 (void)printf("%s=%s\n", *p, value(*p)); 509 return(0); 510 } 511 512 for (ap = arglist; *ap != NULL; ap++) { 513 char *val = value(*ap); 514 (void)printf("%s=%s\n", *ap, val ? val : "<null>"); 515 } 516 return 0; 517} 518 519/* 520 * Put add users to a group. 521 */ 522int 523group(void *v) 524{ 525 char **argv = v; 526 struct grouphead *gh; 527 struct group *gp; 528 int h; 529 int s; 530 char **ap, *gname, **p; 531 532 if (*argv == NULL) { 533 for (h = 0, s = 1; h < HSHSIZE; h++) 534 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 535 s++; 536 ap = salloc(s * sizeof *ap); 537 for (h = 0, p = ap; h < HSHSIZE; h++) 538 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 539 *p++ = gh->g_name; 540 *p = NULL; 541 sort(ap); 542 for (p = ap; *p != NULL; p++) 543 printgroup(*p); 544 return(0); 545 } 546 if (argv[1] == NULL) { 547 printgroup(*argv); 548 return(0); 549 } 550 gname = *argv; 551 h = hash(gname); 552 if ((gh = findgroup(gname)) == NULL) { 553 gh = (struct grouphead *) ecalloc(1, sizeof *gh); 554 gh->g_name = vcopy(gname); 555 gh->g_list = NULL; 556 gh->g_link = groups[h]; 557 groups[h] = gh; 558 } 559 560 /* 561 * Insert names from the command list into the group. 562 * Who cares if there are duplicates? They get tossed 563 * later anyway. 564 */ 565 566 for (ap = argv + 1; *ap != NULL; ap++) { 567 gp = (struct group *) ecalloc(1, sizeof *gp); 568 gp->ge_name = vcopy(*ap); 569 gp->ge_link = gh->g_list; 570 gh->g_list = gp; 571 } 572 return(0); 573} 574 575/* 576 * The unalias command takes a list of alises 577 * and discards the remembered groups of users. 578 */ 579int 580unalias(void *v) 581{ 582 char **ap; 583 584 for (ap = v; *ap != NULL; ap++) 585 (void)delgroup(*ap); 586 return 0; 587} 588 589/* 590 * Delete the named group alias. Return zero if the group was 591 * successfully deleted, or -1 if there was no such group. 592 */ 593static int 594delgroup(const char *name) 595{ 596 struct grouphead *gh, *p; 597 struct group *g; 598 int h; 599 600 h = hash(name); 601 for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link) 602 if (strcmp(gh->g_name, name) == 0) { 603 if (p == NULL) 604 groups[h] = gh->g_link; 605 else 606 p->g_link = gh->g_link; 607 while (gh->g_list != NULL) { 608 g = gh->g_list; 609 gh->g_list = g->ge_link; 610 free(g->ge_name); 611 free(g); 612 } 613 free(gh->g_name); 614 free(gh); 615 return 0; 616 } 617 return -1; 618} 619 620/* 621 * Sort the passed string vecotor into ascending dictionary 622 * order. 623 */ 624void 625sort(char **list) 626{ 627 char **ap; 628 629 for (ap = list; *ap != NULL; ap++) 630 ; 631 if (ap-list < 2) 632 return; 633 qsort(list, (size_t)(ap-list), sizeof(*list), diction); 634} 635 636/* 637 * Do a dictionary order comparison of the arguments from 638 * qsort. 639 */ 640static int 641diction(const void *a, const void *b) 642{ 643 return(strcmp(*(const char *const *)a, *(const char *const *)b)); 644} 645 646/* 647 * The do nothing command for comments. 648 */ 649 650/*ARGSUSED*/ 651int 652null(void *v __unused) 653{ 654 return 0; 655} 656 657/* 658 * Change to another file. With no argument, print information about 659 * the current file. 660 */ 661int 662file(void *v) 663{ 664 char **argv = v; 665 666 if (argv[0] == NULL) { 667 (void)newfileinfo(0); 668 return 0; 669 } 670 if (setfile(*argv) < 0) 671 return 1; 672 announce(); 673 return 0; 674} 675 676/* 677 * Expand file names like echo 678 */ 679int 680echo(void *v) 681{ 682 char **argv = v; 683 char **ap; 684 const char *cp; 685 686 for (ap = argv; *ap != NULL; ap++) { 687 cp = *ap; 688 if ((cp = expand(cp)) != NULL) { 689 if (ap != argv) 690 (void)putchar(' '); 691 (void)printf("%s", cp); 692 } 693 } 694 (void)putchar('\n'); 695 return 0; 696} 697 698int 699Respond(void *v) 700{ 701 int *msgvec = v; 702 if (value("Replyall") == NULL) 703 return (_Respond(msgvec)); 704 else 705 return (_respond(msgvec)); 706} 707 708/* 709 * Reply to a series of messages by simply mailing to the senders 710 * and not messing around with the To: and Cc: lists as in normal 711 * reply. 712 */ 713int 714_Respond(int msgvec[]) 715{ 716 struct header head; 717 struct message *mp; 718 int *ap; 719 char *cp; 720 721 head.h_to = NULL; 722 for (ap = msgvec; *ap != 0; ap++) { 723 mp = &message[*ap - 1]; 724 touch(mp); 725 dot = mp; 726 if ((cp = skin(hfield("from", mp))) == NULL) 727 cp = skin(nameof(mp, 2)); 728 head.h_to = cat(head.h_to, extract(cp, GTO)); 729 } 730 if (head.h_to == NULL) 731 return 0; 732 mp = &message[msgvec[0] - 1]; 733 if ((head.h_subject = hfield("subject", mp)) == NULL) 734 head.h_subject = hfield("subj", mp); 735 head.h_subject = reedit(head.h_subject); 736 head.h_cc = NULL; 737 head.h_bcc = NULL; 738 head.h_smopts = set_smopts(mp); 739#ifdef MIME_SUPPORT 740 head.h_attach = NULL; 741#endif 742 mail1(&head, 1); 743 return 0; 744} 745 746/* 747 * Conditional commands. These allow one to parameterize one's 748 * .mailrc and do some things if sending, others if receiving. 749 */ 750int 751ifcmd(void *v) 752{ 753 char **argv = v; 754 char *cp; 755 756 if (cond != CANY) { 757 (void)printf("Illegal nested \"if\"\n"); 758 return(1); 759 } 760 cond = CANY; 761 cp = argv[0]; 762 switch (*cp) { 763 case 'r': case 'R': 764 cond = CRCV; 765 break; 766 767 case 's': case 'S': 768 cond = CSEND; 769 break; 770 771 default: 772 (void)printf("Unrecognized if-keyword: \"%s\"\n", cp); 773 return(1); 774 } 775 return(0); 776} 777 778/* 779 * Implement 'else'. This is pretty simple -- we just 780 * flip over the conditional flag. 781 */ 782int 783/*ARGSUSED*/ 784elsecmd(void *v __unused) 785{ 786 787 switch (cond) { 788 case CANY: 789 (void)printf("\"Else\" without matching \"if\"\n"); 790 return(1); 791 792 case CSEND: 793 cond = CRCV; 794 break; 795 796 case CRCV: 797 cond = CSEND; 798 break; 799 800 default: 801 (void)printf("Mail's idea of conditions is screwed up\n"); 802 cond = CANY; 803 break; 804 } 805 return(0); 806} 807 808/* 809 * End of if statement. Just set cond back to anything. 810 */ 811int 812/*ARGSUSED*/ 813endifcmd(void *v __unused) 814{ 815 816 if (cond == CANY) { 817 (void)printf("\"Endif\" without matching \"if\"\n"); 818 return(1); 819 } 820 cond = CANY; 821 return(0); 822} 823 824/* 825 * Set the list of alternate names. 826 */ 827int 828alternates(void *v) 829{ 830 char **namelist = v; 831 int c; 832 char **ap, **ap2, *cp; 833 834 c = argcount(namelist) + 1; 835 if (c == 1) { 836 if (altnames == 0) 837 return(0); 838 for (ap = altnames; *ap; ap++) 839 (void)printf("%s ", *ap); 840 (void)printf("\n"); 841 return(0); 842 } 843 if (altnames != 0) 844 free(altnames); 845 altnames = (char **) ecalloc((unsigned) c, sizeof (char *)); 846 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) { 847 cp = ecalloc((unsigned) strlen(*ap) + 1, sizeof (char)); 848 (void)strcpy(cp, *ap); 849 *ap2 = cp; 850 } 851 *ap2 = 0; 852 return(0); 853} 854