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