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