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.2 (Berkeley) 4/20/95"; 37 #endif 38 #endif /* not lint */ 39 #include <sys/cdefs.h> 40 __FBSDID("$FreeBSD$"); 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 && value("flipr") == 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 head.h_replyto = value("REPLYTO"); 264 head.h_inreplyto = skin(hfield("message-id", mp)); 265 mail1(&head, 1); 266 return (0); 267 } 268 269 /* 270 * Modify the subject we are replying to to begin with Re: if 271 * it does not already. 272 */ 273 char * 274 reedit(subj) 275 char *subj; 276 { 277 char *newsubj; 278 279 if (subj == NULL) 280 return (NULL); 281 if ((subj[0] == 'r' || subj[0] == 'R') && 282 (subj[1] == 'e' || subj[1] == 'E') && 283 subj[2] == ':') 284 return (subj); 285 newsubj = salloc(strlen(subj) + 5); 286 sprintf(newsubj, "Re: %s", subj); 287 return (newsubj); 288 } 289 290 /* 291 * Preserve the named messages, so that they will be sent 292 * back to the system mailbox. 293 */ 294 int 295 preserve(msgvec) 296 int *msgvec; 297 { 298 int *ip, mesg; 299 struct message *mp; 300 301 if (edit) { 302 printf("Cannot \"preserve\" in edit mode\n"); 303 return (1); 304 } 305 for (ip = msgvec; *ip != 0; ip++) { 306 mesg = *ip; 307 mp = &message[mesg-1]; 308 mp->m_flag |= MPRESERVE; 309 mp->m_flag &= ~MBOX; 310 dot = mp; 311 } 312 return (0); 313 } 314 315 /* 316 * Mark all given messages as unread. 317 */ 318 int 319 unread(msgvec) 320 int msgvec[]; 321 { 322 int *ip; 323 324 for (ip = msgvec; *ip != 0; ip++) { 325 dot = &message[*ip-1]; 326 dot->m_flag &= ~(MREAD|MTOUCH); 327 dot->m_flag |= MSTATUS; 328 } 329 return (0); 330 } 331 332 /* 333 * Print the size of each message. 334 */ 335 int 336 messize(msgvec) 337 int *msgvec; 338 { 339 struct message *mp; 340 int *ip, mesg; 341 342 for (ip = msgvec; *ip != 0; ip++) { 343 mesg = *ip; 344 mp = &message[mesg-1]; 345 printf("%d: %ld/%ld\n", mesg, mp->m_lines, mp->m_size); 346 } 347 return (0); 348 } 349 350 /* 351 * Quit quickly. If we are sourcing, just pop the input level 352 * by returning an error. 353 */ 354 int 355 rexit(e) 356 int e; 357 { 358 if (sourcing) 359 return (1); 360 exit(0); 361 /*NOTREACHED*/ 362 } 363 364 /* 365 * Set or display a variable value. Syntax is similar to that 366 * of csh. 367 */ 368 int 369 set(arglist) 370 char **arglist; 371 { 372 struct var *vp; 373 char *cp, *cp2; 374 char varbuf[BUFSIZ], **ap, **p; 375 int errs, h, s; 376 377 if (*arglist == NULL) { 378 for (h = 0, s = 1; h < HSHSIZE; h++) 379 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 380 s++; 381 ap = (char **)salloc(s * sizeof(*ap)); 382 for (h = 0, p = ap; h < HSHSIZE; h++) 383 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 384 *p++ = vp->v_name; 385 *p = NULL; 386 sort(ap); 387 for (p = ap; *p != NULL; p++) 388 printf("%s\t%s\n", *p, value(*p)); 389 return (0); 390 } 391 errs = 0; 392 for (ap = arglist; *ap != NULL; ap++) { 393 cp = *ap; 394 cp2 = varbuf; 395 while (cp2 < varbuf + sizeof(varbuf) - 1 && *cp != '=' && *cp != '\0') 396 *cp2++ = *cp++; 397 *cp2 = '\0'; 398 if (*cp == '\0') 399 cp = ""; 400 else 401 cp++; 402 if (equal(varbuf, "")) { 403 printf("Non-null variable name required\n"); 404 errs++; 405 continue; 406 } 407 assign(varbuf, cp); 408 } 409 return (errs); 410 } 411 412 /* 413 * Unset a bunch of variable values. 414 */ 415 int 416 unset(arglist) 417 char **arglist; 418 { 419 struct var *vp, *vp2; 420 int errs, h; 421 char **ap; 422 423 errs = 0; 424 for (ap = arglist; *ap != NULL; ap++) { 425 if ((vp2 = lookup(*ap)) == NULL) { 426 if (getenv(*ap)) 427 unsetenv(*ap); 428 else 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 (void)free(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 (void)free(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 struct grouphead *gh; 460 struct group *gp; 461 char **ap, *gname, **p; 462 int h, s; 463 464 if (*argv == NULL) { 465 for (h = 0, s = 1; h < HSHSIZE; h++) 466 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 467 s++; 468 ap = (char **)salloc(s * sizeof(*ap)); 469 for (h = 0, p = ap; h < HSHSIZE; h++) 470 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 471 *p++ = gh->g_name; 472 *p = NULL; 473 sort(ap); 474 for (p = ap; *p != NULL; p++) 475 printgroup(*p); 476 return (0); 477 } 478 if (argv[1] == NULL) { 479 printgroup(*argv); 480 return (0); 481 } 482 gname = *argv; 483 h = hash(gname); 484 if ((gh = findgroup(gname)) == NULL) { 485 gh = calloc(sizeof(*gh), 1); 486 gh->g_name = vcopy(gname); 487 gh->g_list = NULL; 488 gh->g_link = groups[h]; 489 groups[h] = gh; 490 } 491 492 /* 493 * Insert names from the command list into the group. 494 * Who cares if there are duplicates? They get tossed 495 * later anyway. 496 */ 497 498 for (ap = argv+1; *ap != NULL; ap++) { 499 gp = calloc(sizeof(*gp), 1); 500 gp->ge_name = vcopy(*ap); 501 gp->ge_link = gh->g_list; 502 gh->g_list = gp; 503 } 504 return (0); 505 } 506 507 /* 508 * Sort the passed string vecotor into ascending dictionary 509 * order. 510 */ 511 void 512 sort(list) 513 char **list; 514 { 515 char **ap; 516 517 for (ap = list; *ap != NULL; ap++) 518 ; 519 if (ap-list < 2) 520 return; 521 qsort(list, ap-list, sizeof(*list), diction); 522 } 523 524 /* 525 * Do a dictionary order comparison of the arguments from 526 * qsort. 527 */ 528 int 529 diction(a, b) 530 const void *a, *b; 531 { 532 return (strcmp(*(const char **)a, *(const char **)b)); 533 } 534 535 /* 536 * The do nothing command for comments. 537 */ 538 539 /*ARGSUSED*/ 540 int 541 null(e) 542 int e; 543 { 544 return (0); 545 } 546 547 /* 548 * Change to another file. With no argument, print information about 549 * the current file. 550 */ 551 int 552 file(argv) 553 char **argv; 554 { 555 556 if (argv[0] == NULL) { 557 newfileinfo(0); 558 return (0); 559 } 560 if (setfile(*argv) < 0) 561 return (1); 562 announce(); 563 return (0); 564 } 565 566 /* 567 * Expand file names like echo 568 */ 569 int 570 echo(argv) 571 char **argv; 572 { 573 char **ap, *cp; 574 575 for (ap = argv; *ap != NULL; ap++) { 576 cp = *ap; 577 if ((cp = expand(cp)) != NULL) { 578 if (ap != argv) 579 printf(" "); 580 printf("%s", cp); 581 } 582 } 583 printf("\n"); 584 return (0); 585 } 586 587 int 588 Respond(msgvec) 589 int *msgvec; 590 { 591 if (value("Replyall") == NULL && value("flipr") == NULL) 592 return (doRespond(msgvec)); 593 else 594 return (dorespond(msgvec)); 595 } 596 597 /* 598 * Reply to a series of messages by simply mailing to the senders 599 * and not messing around with the To: and Cc: lists as in normal 600 * reply. 601 */ 602 int 603 doRespond(msgvec) 604 int msgvec[]; 605 { 606 struct header head; 607 struct message *mp; 608 int *ap; 609 char *cp, *mid; 610 611 head.h_to = NULL; 612 for (ap = msgvec; *ap != 0; ap++) { 613 mp = &message[*ap - 1]; 614 touch(mp); 615 dot = mp; 616 if ((cp = skin(hfield("from", mp))) == NULL) 617 cp = skin(nameof(mp, 2)); 618 head.h_to = cat(head.h_to, extract(cp, GTO)); 619 mid = skin(hfield("message-id", mp)); 620 } 621 if (head.h_to == NULL) 622 return (0); 623 mp = &message[msgvec[0] - 1]; 624 if ((head.h_subject = hfield("subject", mp)) == NULL) 625 head.h_subject = hfield("subj", mp); 626 head.h_subject = reedit(head.h_subject); 627 head.h_cc = NULL; 628 head.h_bcc = NULL; 629 head.h_smopts = NULL; 630 head.h_replyto = value("REPLYTO"); 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