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