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