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