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