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