cmd3.c revision 1.32
1/* $NetBSD: cmd3.c,v 1.32 2006/10/31 20:07:32 christos Exp $ */ 2 3/* 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#ifndef lint 34#if 0 35static char sccsid[] = "@(#)cmd3.c 8.2 (Berkeley) 4/20/95"; 36#else 37__RCSID("$NetBSD: cmd3.c,v 1.32 2006/10/31 20:07:32 christos Exp $"); 38#endif 39#endif /* not lint */ 40 41#include "rcv.h" 42#include <util.h> 43#include "extern.h" 44#include "mime.h" 45 46/* 47 * Mail -- a mail program 48 * 49 * Still more user commands. 50 */ 51static int delgroup(const char *); 52static int diction(const void *, const void *); 53 54/* 55 * Process a shell escape by saving signals, ignoring signals, 56 * and forking a sh -c 57 */ 58int 59shell(void *v) 60{ 61 char *str = v; 62 sig_t sigint = signal(SIGINT, SIG_IGN); 63 const char *shellcmd; 64 char cmd[BUFSIZ]; 65 66 (void)strcpy(cmd, str); 67 if (bangexp(cmd) < 0) 68 return 1; 69 if ((shellcmd = value("SHELL")) == NULL) 70 shellcmd = _PATH_CSHELL; 71 (void)run_command(shellcmd, 0, 0, 1, "-c", cmd, NULL); 72 (void)signal(SIGINT, sigint); 73 (void)printf("!\n"); 74 return 0; 75} 76 77/* 78 * Fork an interactive shell. 79 */ 80/*ARGSUSED*/ 81int 82dosh(void *v __unused) 83{ 84 sig_t sigint = signal(SIGINT, SIG_IGN); 85 const char *shellcmd; 86 87 if ((shellcmd = value("SHELL")) == NULL) 88 shellcmd = _PATH_CSHELL; 89 (void)run_command(shellcmd, 0, 0, 1, NULL); 90 (void)signal(SIGINT, sigint); 91 (void)putchar('\n'); 92 return 0; 93} 94 95/* 96 * Expand the shell escape by expanding unescaped !'s into the 97 * last issued command where possible. 98 */ 99 100char lastbang[128]; 101 102int 103bangexp(char *str) 104{ 105 char bangbuf[BUFSIZ]; 106 char *cp, *cp2; 107 int n; 108 int changed = 0; 109 110 cp = str; 111 cp2 = bangbuf; 112 n = BUFSIZ; 113 while (*cp) { 114 if (*cp == '!') { 115 if (n < strlen(lastbang)) { 116overf: 117 (void)printf("Command buffer overflow\n"); 118 return(-1); 119 } 120 changed++; 121 (void)strcpy(cp2, lastbang); 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 (void)printf("!%s\n", bangbuf); 141 (void)fflush(stdout); 142 } 143 (void)strcpy(str, bangbuf); 144 (void)strncpy(lastbang, bangbuf, 128); 145 lastbang[127] = 0; 146 return(0); 147} 148 149/* 150 * Print out a nice help message from some file or another. 151 */ 152 153int 154/*ARGSUSED*/ 155help(void *v __unused) 156{ 157 int c; 158 FILE *f; 159 160 if ((f = Fopen(_PATH_HELP, "r")) == NULL) { 161 warn(_PATH_HELP); 162 return(1); 163 } 164 while ((c = getc(f)) != EOF) 165 (void)putchar(c); 166 (void)Fclose(f); 167 return(0); 168} 169 170/* 171 * Change user's working directory. 172 */ 173int 174schdir(void *v) 175{ 176 char **arglist = v; 177 const char *cp; 178 179 if (*arglist == NULL) 180 cp = homedir; 181 else 182 if ((cp = expand(*arglist)) == NULL) 183 return(1); 184 if (chdir(cp) < 0) { 185 warn("%s", cp); 186 return(1); 187 } 188 return 0; 189} 190 191int 192respond(void *v) 193{ 194 int *msgvec = v; 195 if (value("Replyall") == NULL) 196 return (_respond(msgvec)); 197 else 198 return (_Respond(msgvec)); 199} 200 201static struct name * 202set_smopts(struct message *mp) 203{ 204 char *cp; 205 struct name *np = NULL; 206 char *reply_as_recipient = value("ReplyAsRecipient"); 207 208 if (reply_as_recipient && 209 (cp = skin(hfield("to", mp))) != NULL && 210 extract(cp, GTO)->n_flink == NULL) { /* check for one recipient */ 211 char *p, *q; 212 size_t len = strlen(cp); 213 /* 214 * XXX - perhaps we always want to ignore 215 * "undisclosed-recipients:;" ? 216 */ 217 for (p = q = reply_as_recipient; *p; p = q) { 218 while (*q != '\0' && *q != ',' && !isblank((unsigned char)*q)) 219 q++; 220 if (p + len == q && strncasecmp(cp, p, len) == 0) 221 return np; 222 while (*q == ',' || isblank((unsigned char)*q)) 223 q++; 224 } 225 np = extract(__UNCONST("-f"), GSMOPTS); 226 np = cat(np, extract(cp, GSMOPTS)); 227 } 228 229 return np; 230} 231 232/* 233 * Reply to a list of messages. Extract each name from the 234 * message header and send them off to mail1() 235 */ 236int 237_respond(int *msgvec) 238{ 239 struct message *mp; 240 char *cp, *rcv, *replyto; 241 char **ap; 242 struct name *np; 243 struct header head; 244 245 if (msgvec[1] != 0) { 246 (void)printf("Sorry, can't reply to multiple messages at once\n"); 247 return(1); 248 } 249 mp = &message[msgvec[0] - 1]; 250 touch(mp); 251 dot = mp; 252 if ((rcv = skin(hfield("from", mp))) == NULL) 253 rcv = skin(nameof(mp, 1)); 254 if ((replyto = skin(hfield("reply-to", mp))) != NULL) 255 np = extract(replyto, GTO); 256 else if ((cp = skin(hfield("to", mp))) != NULL) 257 np = extract(cp, GTO); 258 else 259 np = NULL; 260 np = elide(np); 261 /* 262 * Delete my name from the reply list, 263 * and with it, all my alternate names. 264 */ 265 np = delname(np, myname); 266 if (altnames) 267 for (ap = altnames; *ap; ap++) 268 np = delname(np, *ap); 269 if (np != NULL && replyto == NULL) 270 np = cat(np, extract(rcv, GTO)); 271 else if (np == NULL) { 272 if (replyto != NULL) 273 (void)printf("Empty reply-to field -- replying to author\n"); 274 np = extract(rcv, GTO); 275 } 276 head.h_to = np; 277 if ((head.h_subject = hfield("subject", mp)) == NULL) 278 head.h_subject = hfield("subj", mp); 279 head.h_subject = reedit(head.h_subject); 280 if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) { 281 np = elide(extract(cp, GCC)); 282 np = delname(np, myname); 283 if (altnames != 0) 284 for (ap = altnames; *ap; ap++) 285 np = delname(np, *ap); 286 head.h_cc = np; 287 } else 288 head.h_cc = NULL; 289 head.h_bcc = NULL; 290 head.h_smopts = set_smopts(mp); 291#ifdef MIME_SUPPORT 292 head.h_attach = NULL; 293#endif 294 mail1(&head, 1); 295 return(0); 296} 297 298/* 299 * Modify the subject we are replying to to begin with Re: if 300 * it does not already. 301 */ 302char * 303reedit(char *subj) 304{ 305 char *newsubj; 306 307 if (subj == NULL) 308 return NULL; 309 if ((subj[0] == 'r' || subj[0] == 'R') && 310 (subj[1] == 'e' || subj[1] == 'E') && 311 subj[2] == ':') 312 return subj; 313 newsubj = salloc(strlen(subj) + 5); 314 (void)strcpy(newsubj, "Re: "); 315 (void)strcpy(newsubj + 4, subj); 316 return newsubj; 317} 318 319/* 320 * Preserve the named messages, so that they will be sent 321 * back to the system mailbox. 322 */ 323int 324preserve(void *v) 325{ 326 int *msgvec = v; 327 struct message *mp; 328 int *ip, mesg; 329 330 if (edit) { 331 (void)printf("Cannot \"preserve\" in edit mode\n"); 332 return(1); 333 } 334 for (ip = msgvec; *ip != 0; ip++) { 335 mesg = *ip; 336 mp = &message[mesg - 1]; 337 mp->m_flag |= MPRESERVE; 338 mp->m_flag &= ~MBOX; 339 dot = mp; 340 } 341 return(0); 342} 343 344/* 345 * Mark all given messages as unread. 346 */ 347int 348unread(void *v) 349{ 350 int *msgvec = v; 351 int *ip; 352 353 for (ip = msgvec; *ip != 0; ip++) { 354 dot = &message[*ip - 1]; 355 dot->m_flag &= ~(MREAD|MTOUCH); 356 dot->m_flag |= MSTATUS; 357 } 358 return(0); 359} 360 361/* 362 * Mark all given messages as read. 363 */ 364int 365markread(void *v) 366{ 367 int *msgvec = v; 368 int *ip; 369 370 for (ip = msgvec; *ip != 0; ip++) { 371 dot = &message[*ip - 1]; 372 dot->m_flag &= ~(MNEW|MTOUCH); 373 dot->m_flag |= MREAD|MSTATUS; 374 } 375 return(0); 376} 377 378/* 379 * Print the size of each message. 380 */ 381int 382messize(void *v) 383{ 384 int *msgvec = v; 385 struct message *mp; 386 int *ip, mesg; 387 388 for (ip = msgvec; *ip != 0; ip++) { 389 mesg = *ip; 390 mp = &message[mesg - 1]; 391 (void)printf("%d: %ld/%llu\n", mesg, mp->m_blines, 392 (unsigned long long)mp->m_size); 393 } 394 return(0); 395} 396 397/* 398 * Quit quickly. If we are sourcing, just pop the input level 399 * by returning an error. 400 */ 401int 402/*ARGSUSED*/ 403rexit(void *v __unused) 404{ 405 if (sourcing) 406 return(1); 407 exit(0); 408 /*NOTREACHED*/ 409} 410 411/* 412 * Set or display a variable value. Syntax is similar to that 413 * of csh. 414 */ 415int 416set(void *v) 417{ 418 char **arglist = v; 419 struct var *vp; 420 const char *cp; 421 char varbuf[BUFSIZ], **ap, **p; 422 int errs, h, s; 423 size_t l; 424 425 if (*arglist == NULL) { 426 for (h = 0, s = 1; h < HSHSIZE; h++) 427 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 428 s++; 429 ap = salloc(s * sizeof *ap); 430 for (h = 0, p = ap; h < HSHSIZE; h++) 431 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 432 *p++ = vp->v_name; 433 *p = NULL; 434 sort(ap); 435 for (p = ap; *p != NULL; p++) 436 (void)printf("%s\t%s\n", *p, value(*p)); 437 return(0); 438 } 439 errs = 0; 440 for (ap = arglist; *ap != NULL; ap++) { 441 cp = *ap; 442 while (*cp != '=' && *cp != '\0') 443 ++cp; 444 l = cp - *ap; 445 if (l >= sizeof varbuf) 446 l = sizeof varbuf - 1; 447 (void)strncpy(varbuf, *ap, l); 448 varbuf[l] = '\0'; 449 if (*cp == '\0') 450 cp = ""; 451 else 452 cp++; 453 if (equal(varbuf, "")) { 454 (void)printf("Non-null variable name required\n"); 455 errs++; 456 continue; 457 } 458 assign(varbuf, cp); 459 } 460 return(errs); 461} 462 463/* 464 * Unset a bunch of variable values. 465 */ 466int 467unset(void *v) 468{ 469 char **arglist = v; 470 struct var *vp, *vp2; 471 int errs, h; 472 char **ap; 473 474 errs = 0; 475 for (ap = arglist; *ap != NULL; ap++) { 476 if ((vp2 = lookup(*ap)) == NULL) { 477 if (getenv(*ap)) { 478 (void)unsetenv(*ap); 479 } else if (!sourcing) { 480 (void)printf("\"%s\": undefined variable\n", *ap); 481 errs++; 482 } 483 continue; 484 } 485 h = hash(*ap); 486 if (vp2 == variables[h]) { 487 variables[h] = variables[h]->v_link; 488 v_free(vp2->v_name); 489 v_free(vp2->v_value); 490 free(vp2); 491 continue; 492 } 493 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link) 494 ; 495 vp->v_link = vp2->v_link; 496 v_free(vp2->v_name); 497 v_free(vp2->v_value); 498 free(vp2); 499 } 500 return(errs); 501} 502 503/* 504 * Show a variable value. 505 */ 506int 507show(void *v) 508{ 509 char **arglist = v; 510 struct var *vp; 511 char **ap, **p; 512 int h, s; 513 514 if (*arglist == NULL) { 515 for (h = 0, s = 1; h < HSHSIZE; h++) 516 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 517 s++; 518 ap = salloc(s * sizeof *ap); 519 for (h = 0, p = ap; h < HSHSIZE; h++) 520 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 521 *p++ = vp->v_name; 522 *p = NULL; 523 sort(ap); 524 for (p = ap; *p != NULL; p++) 525 (void)printf("%s=%s\n", *p, value(*p)); 526 return(0); 527 } 528 529 for (ap = arglist; *ap != NULL; ap++) { 530 char *val = value(*ap); 531 (void)printf("%s=%s\n", *ap, val ? val : "<null>"); 532 } 533 return 0; 534} 535 536 537/* 538 * Put add users to a group. 539 */ 540int 541group(void *v) 542{ 543 char **argv = v; 544 struct grouphead *gh; 545 struct group *gp; 546 int h; 547 int s; 548 char **ap, *gname, **p; 549 550 if (*argv == NULL) { 551 for (h = 0, s = 1; h < HSHSIZE; h++) 552 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 553 s++; 554 ap = salloc(s * sizeof *ap); 555 for (h = 0, p = ap; h < HSHSIZE; h++) 556 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 557 *p++ = gh->g_name; 558 *p = NULL; 559 sort(ap); 560 for (p = ap; *p != NULL; p++) 561 printgroup(*p); 562 return(0); 563 } 564 if (argv[1] == NULL) { 565 printgroup(*argv); 566 return(0); 567 } 568 gname = *argv; 569 h = hash(gname); 570 if ((gh = findgroup(gname)) == NULL) { 571 gh = (struct grouphead *) ecalloc(1, sizeof *gh); 572 gh->g_name = vcopy(gname); 573 gh->g_list = NULL; 574 gh->g_link = groups[h]; 575 groups[h] = gh; 576 } 577 578 /* 579 * Insert names from the command list into the group. 580 * Who cares if there are duplicates? They get tossed 581 * later anyway. 582 */ 583 584 for (ap = argv + 1; *ap != NULL; ap++) { 585 gp = (struct group *) ecalloc(1, sizeof *gp); 586 gp->ge_name = vcopy(*ap); 587 gp->ge_link = gh->g_list; 588 gh->g_list = gp; 589 } 590 return(0); 591} 592 593/* 594 * The unalias command takes a list of alises 595 * and discards the remembered groups of users. 596 */ 597int 598unalias(void *v) 599{ 600 char **ap; 601 602 for (ap = v; *ap != NULL; ap++) 603 (void)delgroup(*ap); 604 return 0; 605} 606 607/* 608 * Delete the named group alias. Return zero if the group was 609 * successfully deleted, or -1 if there was no such group. 610 */ 611static int 612delgroup(const char *name) 613{ 614 struct grouphead *gh, *p; 615 struct group *g; 616 int h; 617 618 h = hash(name); 619 for (gh = groups[h], p = NULL; gh != NULL; p = gh, gh = gh->g_link) 620 if (strcmp(gh->g_name, name) == 0) { 621 if (p == NULL) 622 groups[h] = gh->g_link; 623 else 624 p->g_link = gh->g_link; 625 while (gh->g_list != NULL) { 626 g = gh->g_list; 627 gh->g_list = g->ge_link; 628 free(g->ge_name); 629 free(g); 630 } 631 free(gh->g_name); 632 free(gh); 633 return 0; 634 } 635 return -1; 636} 637 638/* 639 * Sort the passed string vecotor into ascending dictionary 640 * order. 641 */ 642void 643sort(char **list) 644{ 645 char **ap; 646 647 for (ap = list; *ap != NULL; ap++) 648 ; 649 if (ap-list < 2) 650 return; 651 qsort(list, (size_t)(ap-list), sizeof(*list), diction); 652} 653 654/* 655 * Do a dictionary order comparison of the arguments from 656 * qsort. 657 */ 658static int 659diction(const void *a, const void *b) 660{ 661 return(strcmp(*(const char *const *)a, *(const char *const *)b)); 662} 663 664/* 665 * The do nothing command for comments. 666 */ 667 668/*ARGSUSED*/ 669int 670null(void *v __unused) 671{ 672 return 0; 673} 674 675/* 676 * Change to another file. With no argument, print information about 677 * the current file. 678 */ 679int 680file(void *v) 681{ 682 char **argv = v; 683 684 if (argv[0] == NULL) { 685 (void)newfileinfo(0); 686 return 0; 687 } 688 if (setfile(*argv) < 0) 689 return 1; 690 announce(); 691 return 0; 692} 693 694/* 695 * Expand file names like echo 696 */ 697int 698echo(void *v) 699{ 700 char **argv = v; 701 char **ap; 702 const char *cp; 703 704 for (ap = argv; *ap != NULL; ap++) { 705 cp = *ap; 706 if ((cp = expand(cp)) != NULL) { 707 if (ap != argv) 708 (void)putchar(' '); 709 (void)printf("%s", cp); 710 } 711 } 712 (void)putchar('\n'); 713 return 0; 714} 715 716int 717Respond(void *v) 718{ 719 int *msgvec = v; 720 if (value("Replyall") == NULL) 721 return (_Respond(msgvec)); 722 else 723 return (_respond(msgvec)); 724} 725 726/* 727 * Reply to a series of messages by simply mailing to the senders 728 * and not messing around with the To: and Cc: lists as in normal 729 * reply. 730 */ 731int 732_Respond(int msgvec[]) 733{ 734 struct header head; 735 struct message *mp; 736 int *ap; 737 char *cp; 738 739 head.h_to = NULL; 740 for (ap = msgvec; *ap != 0; ap++) { 741 mp = &message[*ap - 1]; 742 touch(mp); 743 dot = mp; 744 if ((cp = skin(hfield("from", mp))) == NULL) 745 cp = skin(nameof(mp, 2)); 746 head.h_to = cat(head.h_to, extract(cp, GTO)); 747 } 748 if (head.h_to == NULL) 749 return 0; 750 mp = &message[msgvec[0] - 1]; 751 if ((head.h_subject = hfield("subject", mp)) == NULL) 752 head.h_subject = hfield("subj", mp); 753 head.h_subject = reedit(head.h_subject); 754 head.h_cc = NULL; 755 head.h_bcc = NULL; 756 head.h_smopts = set_smopts(mp); 757#ifdef MIME_SUPPORT 758 head.h_attach = NULL; 759#endif 760 mail1(&head, 1); 761 return 0; 762} 763 764/* 765 * Conditional commands. These allow one to parameterize one's 766 * .mailrc and do some things if sending, others if receiving. 767 */ 768int 769ifcmd(void *v) 770{ 771 char **argv = v; 772 char *cp; 773 774 if (cond != CANY) { 775 (void)printf("Illegal nested \"if\"\n"); 776 return(1); 777 } 778 cond = CANY; 779 cp = argv[0]; 780 switch (*cp) { 781 case 'r': case 'R': 782 cond = CRCV; 783 break; 784 785 case 's': case 'S': 786 cond = CSEND; 787 break; 788 789 default: 790 (void)printf("Unrecognized if-keyword: \"%s\"\n", cp); 791 return(1); 792 } 793 return(0); 794} 795 796/* 797 * Implement 'else'. This is pretty simple -- we just 798 * flip over the conditional flag. 799 */ 800int 801/*ARGSUSED*/ 802elsecmd(void *v __unused) 803{ 804 805 switch (cond) { 806 case CANY: 807 (void)printf("\"Else\" without matching \"if\"\n"); 808 return(1); 809 810 case CSEND: 811 cond = CRCV; 812 break; 813 814 case CRCV: 815 cond = CSEND; 816 break; 817 818 default: 819 (void)printf("Mail's idea of conditions is screwed up\n"); 820 cond = CANY; 821 break; 822 } 823 return(0); 824} 825 826/* 827 * End of if statement. Just set cond back to anything. 828 */ 829int 830/*ARGSUSED*/ 831endifcmd(void *v __unused) 832{ 833 834 if (cond == CANY) { 835 (void)printf("\"Endif\" without matching \"if\"\n"); 836 return(1); 837 } 838 cond = CANY; 839 return(0); 840} 841 842/* 843 * Set the list of alternate names. 844 */ 845int 846alternates(void *v) 847{ 848 char **namelist = v; 849 int c; 850 char **ap, **ap2, *cp; 851 852 c = argcount(namelist) + 1; 853 if (c == 1) { 854 if (altnames == 0) 855 return(0); 856 for (ap = altnames; *ap; ap++) 857 (void)printf("%s ", *ap); 858 (void)printf("\n"); 859 return(0); 860 } 861 if (altnames != 0) 862 free(altnames); 863 altnames = (char **) ecalloc((unsigned) c, sizeof (char *)); 864 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) { 865 cp = ecalloc((unsigned) strlen(*ap) + 1, sizeof (char)); 866 (void)strcpy(cp, *ap); 867 *ap2 = cp; 868 } 869 *ap2 = 0; 870 return(0); 871} 872