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