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 (_respond(msgvec)); 191 else 192 return (_Respond(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 _respond(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 mail1(&head, 1); 256 return(0); 257 } 258 259 /* 260 * Modify the subject we are replying to to begin with Re: if 261 * it does not already. 262 */ 263 char * 264 reedit(subj) 265 register char *subj; 266 { 267 char *newsubj; 268 269 if (subj == NOSTR) 270 return NOSTR; 271 if ((subj[0] == 'r' || subj[0] == 'R') && 272 (subj[1] == 'e' || subj[1] == 'E') && 273 subj[2] == ':') 274 return subj; 275 newsubj = salloc(strlen(subj) + 5); 276 strcpy(newsubj, "Re: "); 277 strcpy(newsubj + 4, subj); 278 return newsubj; 279 } 280 281 /* 282 * Preserve the named messages, so that they will be sent 283 * back to the system mailbox. 284 */ 285 int 286 preserve(msgvec) 287 int *msgvec; 288 { 289 register struct message *mp; 290 register int *ip, mesg; 291 292 if (edit) { 293 printf("Cannot \"preserve\" in edit mode\n"); 294 return(1); 295 } 296 for (ip = msgvec; *ip != NULL; ip++) { 297 mesg = *ip; 298 mp = &message[mesg-1]; 299 mp->m_flag |= MPRESERVE; 300 mp->m_flag &= ~MBOX; 301 dot = mp; 302 } 303 return(0); 304 } 305 306 /* 307 * Mark all given messages as unread. 308 */ 309 int 310 unread(msgvec) 311 int msgvec[]; 312 { 313 register int *ip; 314 315 for (ip = msgvec; *ip != NULL; ip++) { 316 dot = &message[*ip-1]; 317 dot->m_flag &= ~(MREAD|MTOUCH); 318 dot->m_flag |= MSTATUS; 319 } 320 return(0); 321 } 322 323 /* 324 * Print the size of each message. 325 */ 326 int 327 messize(msgvec) 328 int *msgvec; 329 { 330 register struct message *mp; 331 register int *ip, mesg; 332 333 for (ip = msgvec; *ip != NULL; ip++) { 334 mesg = *ip; 335 mp = &message[mesg-1]; 336 printf("%d: %d/%ld\n", mesg, mp->m_lines, mp->m_size); 337 } 338 return(0); 339 } 340 341 /* 342 * Quit quickly. If we are sourcing, just pop the input level 343 * by returning an error. 344 */ 345 int 346 rexit(e) 347 int e; 348 { 349 if (sourcing) 350 return(1); 351 exit(e); 352 /*NOTREACHED*/ 353 } 354 355 /* 356 * Set or display a variable value. Syntax is similar to that 357 * of csh. 358 */ 359 int 360 set(arglist) 361 char **arglist; 362 { 363 register struct var *vp; 364 register char *cp, *cp2; 365 char varbuf[BUFSIZ], **ap, **p; 366 int errs, h, s; 367 368 if (*arglist == NOSTR) { 369 for (h = 0, s = 1; h < HSHSIZE; h++) 370 for (vp = variables[h]; vp != NOVAR; vp = vp->v_link) 371 s++; 372 ap = (char **) salloc(s * sizeof *ap); 373 for (h = 0, p = ap; h < HSHSIZE; h++) 374 for (vp = variables[h]; vp != NOVAR; vp = vp->v_link) 375 *p++ = vp->v_name; 376 *p = NOSTR; 377 sort(ap); 378 for (p = ap; *p != NOSTR; p++) 379 printf("%s\t%s\n", *p, value(*p)); 380 return(0); 381 } 382 errs = 0; 383 for (ap = arglist; *ap != NOSTR; ap++) { 384 cp = *ap; 385 cp2 = varbuf; 386 while (*cp != '=' && *cp != '\0') 387 *cp2++ = *cp++; 388 *cp2 = '\0'; 389 if (*cp == '\0') 390 cp = ""; 391 else 392 cp++; 393 if (equal(varbuf, "")) { 394 printf("Non-null variable name required\n"); 395 errs++; 396 continue; 397 } 398 assign(varbuf, cp); 399 } 400 return(errs); 401 } 402 403 /* 404 * Unset a bunch of variable values. 405 */ 406 int 407 unset(arglist) 408 char **arglist; 409 { 410 register struct var *vp, *vp2; 411 int errs, h; 412 char **ap; 413 414 errs = 0; 415 for (ap = arglist; *ap != NOSTR; ap++) { 416 if ((vp2 = lookup(*ap)) == NOVAR) { 417 if (!sourcing) { 418 printf("\"%s\": undefined variable\n", *ap); 419 errs++; 420 } 421 continue; 422 } 423 h = hash(*ap); 424 if (vp2 == variables[h]) { 425 variables[h] = variables[h]->v_link; 426 vfree(vp2->v_name); 427 vfree(vp2->v_value); 428 free((char *)vp2); 429 continue; 430 } 431 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link) 432 ; 433 vp->v_link = vp2->v_link; 434 vfree(vp2->v_name); 435 vfree(vp2->v_value); 436 free((char *) vp2); 437 } 438 return(errs); 439 } 440 441 /* 442 * Put add users to a group. 443 */ 444 int 445 group(argv) 446 char **argv; 447 { 448 register struct grouphead *gh; 449 register struct group *gp; 450 register int h; 451 int s; 452 char **ap, *gname, **p; 453 454 if (*argv == NOSTR) { 455 for (h = 0, s = 1; h < HSHSIZE; h++) 456 for (gh = groups[h]; gh != NOGRP; gh = gh->g_link) 457 s++; 458 ap = (char **) salloc(s * sizeof *ap); 459 for (h = 0, p = ap; h < HSHSIZE; h++) 460 for (gh = groups[h]; gh != NOGRP; gh = gh->g_link) 461 *p++ = gh->g_name; 462 *p = NOSTR; 463 sort(ap); 464 for (p = ap; *p != NOSTR; p++) 465 printgroup(*p); 466 return(0); 467 } 468 if (argv[1] == NOSTR) { 469 printgroup(*argv); 470 return(0); 471 } 472 gname = *argv; 473 h = hash(gname); 474 if ((gh = findgroup(gname)) == NOGRP) { 475 gh = (struct grouphead *) calloc(sizeof *gh, 1); 476 gh->g_name = vcopy(gname); 477 gh->g_list = NOGE; 478 gh->g_link = groups[h]; 479 groups[h] = gh; 480 } 481 482 /* 483 * Insert names from the command list into the group. 484 * Who cares if there are duplicates? They get tossed 485 * later anyway. 486 */ 487 488 for (ap = argv+1; *ap != NOSTR; ap++) { 489 gp = (struct group *) calloc(sizeof *gp, 1); 490 gp->ge_name = vcopy(*ap); 491 gp->ge_link = gh->g_list; 492 gh->g_list = gp; 493 } 494 return(0); 495 } 496 497 /* 498 * Sort the passed string vecotor into ascending dictionary 499 * order. 500 */ 501 void 502 sort(list) 503 char **list; 504 { 505 register char **ap; 506 int diction(); 507 508 for (ap = list; *ap != NOSTR; ap++) 509 ; 510 if (ap-list < 2) 511 return; 512 qsort(list, ap-list, sizeof(*list), diction); 513 } 514 515 /* 516 * Do a dictionary order comparison of the arguments from 517 * qsort. 518 */ 519 int 520 diction(a, b) 521 const void *a, *b; 522 { 523 return(strcmp(*(char **)a, *(char **)b)); 524 } 525 526 /* 527 * The do nothing command for comments. 528 */ 529 530 /*ARGSUSED*/ 531 int 532 null(e) 533 int e; 534 { 535 return 0; 536 } 537 538 /* 539 * Change to another file. With no argument, print information about 540 * the current file. 541 */ 542 int 543 file(argv) 544 register char **argv; 545 { 546 547 if (argv[0] == NOSTR) { 548 newfileinfo(); 549 return 0; 550 } 551 if (setfile(*argv) < 0) 552 return 1; 553 announce(); 554 return 0; 555 } 556 557 /* 558 * Expand file names like echo 559 */ 560 int 561 echo(argv) 562 char **argv; 563 { 564 register char **ap; 565 register char *cp; 566 567 for (ap = argv; *ap != NOSTR; ap++) { 568 cp = *ap; 569 if ((cp = expand(cp)) != NOSTR) { 570 if (ap != argv) 571 putchar(' '); 572 printf("%s", cp); 573 } 574 } 575 putchar('\n'); 576 return 0; 577 } 578 579 int 580 Respond(msgvec) 581 int *msgvec; 582 { 583 if (value("Replyall") == NOSTR) 584 return (_Respond(msgvec)); 585 else 586 return (_respond(msgvec)); 587 } 588 589 /* 590 * Reply to a series of messages by simply mailing to the senders 591 * and not messing around with the To: and Cc: lists as in normal 592 * reply. 593 */ 594 int 595 _Respond(msgvec) 596 int msgvec[]; 597 { 598 struct header head; 599 struct message *mp; 600 register int *ap; 601 register char *cp; 602 603 head.h_to = NIL; 604 for (ap = msgvec; *ap != 0; ap++) { 605 mp = &message[*ap - 1]; 606 touch(mp); 607 dot = mp; 608 if ((cp = skin(hfield("from", mp))) == NOSTR) 609 cp = skin(nameof(mp, 2)); 610 head.h_to = cat(head.h_to, extract(cp, GTO)); 611 } 612 if (head.h_to == NIL) 613 return 0; 614 mp = &message[msgvec[0] - 1]; 615 if ((head.h_subject = hfield("subject", mp)) == NOSTR) 616 head.h_subject = hfield("subj", mp); 617 head.h_subject = reedit(head.h_subject); 618 head.h_cc = NIL; 619 head.h_bcc = NIL; 620 head.h_smopts = NIL; 621 mail1(&head, 1); 622 return 0; 623 } 624 625 /* 626 * Conditional commands. These allow one to parameterize one's 627 * .mailrc and do some things if sending, others if receiving. 628 */ 629 int 630 ifcmd(argv) 631 char **argv; 632 { 633 register char *cp; 634 635 if (cond != CANY) { 636 printf("Illegal nested \"if\"\n"); 637 return(1); 638 } 639 cond = CANY; 640 cp = argv[0]; 641 switch (*cp) { 642 case 'r': case 'R': 643 cond = CRCV; 644 break; 645 646 case 's': case 'S': 647 cond = CSEND; 648 break; 649 650 default: 651 printf("Unrecognized if-keyword: \"%s\"\n", cp); 652 return(1); 653 } 654 return(0); 655 } 656 657 /* 658 * Implement 'else'. This is pretty simple -- we just 659 * flip over the conditional flag. 660 */ 661 int 662 elsecmd() 663 { 664 665 switch (cond) { 666 case CANY: 667 printf("\"Else\" without matching \"if\"\n"); 668 return(1); 669 670 case CSEND: 671 cond = CRCV; 672 break; 673 674 case CRCV: 675 cond = CSEND; 676 break; 677 678 default: 679 printf("Mail's idea of conditions is screwed up\n"); 680 cond = CANY; 681 break; 682 } 683 return(0); 684 } 685 686 /* 687 * End of if statement. Just set cond back to anything. 688 */ 689 int 690 endifcmd() 691 { 692 693 if (cond == CANY) { 694 printf("\"Endif\" without matching \"if\"\n"); 695 return(1); 696 } 697 cond = CANY; 698 return(0); 699 } 700 701 /* 702 * Set the list of alternate names. 703 */ 704 int 705 alternates(namelist) 706 char **namelist; 707 { 708 register int c; 709 register char **ap, **ap2, *cp; 710 711 c = argcount(namelist) + 1; 712 if (c == 1) { 713 if (altnames == 0) 714 return(0); 715 for (ap = altnames; *ap; ap++) 716 printf("%s ", *ap); 717 printf("\n"); 718 return(0); 719 } 720 if (altnames != 0) 721 free((char *) altnames); 722 altnames = (char **) calloc((unsigned) c, sizeof (char *)); 723 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) { 724 cp = (char *) calloc((unsigned) strlen(*ap) + 1, sizeof (char)); 725 strcpy(cp, *ap); 726 *ap2 = cp; 727 } 728 *ap2 = 0; 729 return(0); 730 } 731