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