1 /* $OpenBSD: cmd3.c,v 1.18 2001/11/21 20:41:55 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 const char sccsid[] = "@(#)cmd3.c 8.2 (Berkeley) 4/20/95"; 40 #else 41 static const char rcsid[] = "$OpenBSD: cmd3.c,v 1.18 2001/11/21 20:41:55 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(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(void *v) 61 { 62 char *str = v; 63 char *shell; 64 char cmd[BUFSIZ]; 65 struct sigaction oact; 66 sigset_t oset; 67 68 (void)ignoresig(SIGINT, &oact, &oset); 69 (void)strlcpy(cmd, str, sizeof(cmd)); 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)sigprocmask(SIG_SETMASK, &oset, NULL); 75 (void)sigaction(SIGINT, &oact, NULL); 76 puts("!"); 77 return(0); 78 } 79 80 /* 81 * Fork an interactive shell. 82 */ 83 /*ARGSUSED*/ 84 int 85 dosh(void *v) 86 { 87 char *shell; 88 struct sigaction oact; 89 sigset_t oset; 90 91 shell = value("SHELL"); 92 (void)ignoresig(SIGINT, &oact, &oset); 93 (void)run_command(shell, 0, 0, -1, NULL, NULL, NULL); 94 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 95 (void)sigaction(SIGINT, &oact, NULL); 96 putchar('\n'); 97 return(0); 98 } 99 100 /* 101 * Expand the shell escape by expanding unescaped !'s into the 102 * last issued command where possible. 103 */ 104 int 105 bangexp(char *str, size_t strsize) 106 { 107 char bangbuf[BUFSIZ]; 108 static char lastbang[BUFSIZ]; 109 char *cp, *cp2; 110 int n, changed = 0; 111 112 cp = str; 113 cp2 = bangbuf; 114 n = BUFSIZ; 115 while (*cp) { 116 if (*cp == '!') { 117 if (n < strlen(lastbang)) { 118 overf: 119 puts("Command buffer overflow"); 120 return(-1); 121 } 122 changed++; 123 strlcpy(cp2, lastbang, sizeof(bangbuf) - (cp2 - bangbuf)); 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)strlcpy(str, bangbuf, strsize); 146 (void)strlcpy(lastbang, bangbuf, sizeof(lastbang)); 147 return(0); 148 } 149 150 /* 151 * Print out a nice help message from some file or another. 152 */ 153 int 154 help(void *v) 155 { 156 157 (void)run_command(value("PAGER"), 0, -1, -1, _PATH_HELP, NULL); 158 return(0); 159 } 160 161 /* 162 * Change user's working directory. 163 */ 164 int 165 schdir(void *v) 166 { 167 char **arglist = v; 168 char *cp; 169 170 if (*arglist == NULL) { 171 if (homedir == NULL) 172 return(1); 173 cp = homedir; 174 } else { 175 if ((cp = expand(*arglist)) == NULL) 176 return(1); 177 } 178 if (chdir(cp) < 0) { 179 warn("%s", cp); 180 return(1); 181 } 182 return(0); 183 } 184 185 int 186 respond(void *v) 187 { 188 int *msgvec = v; 189 190 if (value("Replyall") == NULL) 191 return(_respond(msgvec)); 192 else 193 return(_Respond(msgvec)); 194 } 195 196 /* 197 * Reply to a list of messages. Extract each name from the 198 * message header and send them off to mail1() 199 */ 200 int 201 _respond(msgvec) 202 int *msgvec; 203 { 204 struct message *mp; 205 char *cp, *rcv, *replyto; 206 char **ap; 207 struct name *np; 208 struct header head; 209 210 if (msgvec[1] != 0) { 211 puts("Sorry, can't reply to multiple messages at once"); 212 return(1); 213 } 214 mp = &message[msgvec[0] - 1]; 215 touch(mp); 216 dot = mp; 217 if ((rcv = skin(hfield("from", mp))) == NULL) 218 rcv = skin(nameof(mp, 1)); 219 if ((replyto = skin(hfield("reply-to", mp))) != NULL) 220 np = extract(replyto, GTO); 221 else if ((cp = skin(hfield("to", mp))) != NULL) 222 np = extract(cp, GTO); 223 else 224 np = NULL; 225 np = elide(np); 226 /* 227 * Delete my name from the reply list, 228 * and with it, all my alternate names. 229 */ 230 np = delname(np, myname); 231 if (altnames) 232 for (ap = altnames; *ap; ap++) 233 np = delname(np, *ap); 234 if (np != NULL && replyto == NULL) 235 np = cat(np, extract(rcv, GTO)); 236 else if (np == NULL) { 237 if (replyto != NULL) 238 puts("Empty reply-to field -- replying to author"); 239 np = extract(rcv, GTO); 240 } 241 head.h_to = np; 242 if ((head.h_subject = hfield("subject", mp)) == NULL) 243 head.h_subject = hfield("subj", mp); 244 head.h_subject = reedit(head.h_subject); 245 if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) { 246 np = elide(extract(cp, GCC)); 247 np = delname(np, myname); 248 if (altnames != 0) 249 for (ap = altnames; *ap; ap++) 250 np = delname(np, *ap); 251 head.h_cc = np; 252 } else 253 head.h_cc = NULL; 254 head.h_bcc = NULL; 255 head.h_smopts = NULL; 256 mail1(&head, 1); 257 return(0); 258 } 259 260 /* 261 * Modify the subject we are replying to to begin with Re: if 262 * it does not already. 263 */ 264 char * 265 reedit(char *subj) 266 { 267 char *newsubj; 268 size_t len; 269 270 if (subj == NULL) 271 return(NULL); 272 if ((subj[0] == 'r' || subj[0] == 'R') && 273 (subj[1] == 'e' || subj[1] == 'E') && 274 subj[2] == ':') 275 return(subj); 276 len = strlen(subj) + 5; 277 newsubj = salloc(len); 278 strlcpy(newsubj, "Re: ", len); 279 strlcat(newsubj, subj, len); 280 return(newsubj); 281 } 282 283 /* 284 * Mark new the named messages, so that they will be left in the system 285 * mailbox as unread. 286 */ 287 int 288 marknew(void *v) 289 { 290 int *msgvec = v; 291 int *ip; 292 293 for (ip = msgvec; *ip != NULL; ip++) { 294 dot = &message[*ip-1]; 295 dot->m_flag &= ~(MBOX|MREAD|MTOUCH); 296 dot->m_flag |= MNEW|MSTATUS; 297 } 298 return(0); 299 } 300 301 /* 302 * Preserve the named messages, so that they will be sent 303 * back to the system mailbox. 304 */ 305 int 306 preserve(void *v) 307 { 308 int *msgvec = v; 309 int *ip, mesg; 310 struct message *mp; 311 312 if (edit) { 313 puts("Cannot \"preserve\" in edit mode"); 314 return(1); 315 } 316 for (ip = msgvec; *ip != NULL; ip++) { 317 mesg = *ip; 318 mp = &message[mesg-1]; 319 mp->m_flag |= MPRESERVE; 320 mp->m_flag &= ~MBOX; 321 dot = mp; 322 } 323 return(0); 324 } 325 326 /* 327 * Mark all given messages as unread. 328 */ 329 int 330 unread(void *v) 331 { 332 int *msgvec = v; 333 int *ip; 334 335 for (ip = msgvec; *ip != NULL; ip++) { 336 dot = &message[*ip-1]; 337 dot->m_flag &= ~(MREAD|MTOUCH); 338 dot->m_flag |= MSTATUS; 339 } 340 return(0); 341 } 342 343 /* 344 * Print the size of each message. 345 */ 346 int 347 messize(void *v) 348 { 349 int *msgvec = v; 350 struct message *mp; 351 int *ip, mesg; 352 353 for (ip = msgvec; *ip != NULL; ip++) { 354 mesg = *ip; 355 mp = &message[mesg-1]; 356 printf("%d: %d/%d\n", mesg, mp->m_lines, mp->m_size); 357 } 358 return(0); 359 } 360 361 /* 362 * Quit quickly. If we are sourcing, just pop the input level 363 * by returning an error. 364 */ 365 int 366 rexit(void *v) 367 { 368 369 if (sourcing) 370 return(1); 371 exit(0); 372 /*NOTREACHED*/ 373 } 374 375 /* 376 * Set or display a variable value. Syntax is similar to that 377 * of csh. 378 */ 379 int 380 set(void *v) 381 { 382 char **arglist = v; 383 struct var *vp; 384 char *cp, *cp2; 385 char varbuf[BUFSIZ], **ap, **p; 386 int errs, h, s; 387 388 if (*arglist == NULL) { 389 for (h = 0, s = 1; h < HSHSIZE; h++) 390 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 391 s++; 392 ap = (char **)salloc(s * sizeof(*ap)); 393 for (h = 0, p = ap; h < HSHSIZE; h++) 394 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 395 *p++ = vp->v_name; 396 *p = NULL; 397 sort(ap); 398 for (p = ap; *p != NULL; p++) 399 printf("%s\t%s\n", *p, value(*p)); 400 return(0); 401 } 402 errs = 0; 403 for (ap = arglist; *ap != NULL; ap++) { 404 cp = *ap; 405 cp2 = varbuf; 406 while (*cp != '=' && *cp != '\0') 407 *cp2++ = *cp++; 408 *cp2 = '\0'; 409 if (*cp == '\0') 410 cp = ""; 411 else 412 cp++; 413 if (equal(varbuf, "")) { 414 puts("Non-null variable name required"); 415 errs++; 416 continue; 417 } 418 assign(varbuf, cp); 419 } 420 return(errs); 421 } 422 423 /* 424 * Unset a bunch of variable values. 425 */ 426 int 427 unset(void *v) 428 { 429 char **arglist = v; 430 struct var *vp, *vp2; 431 int errs, h; 432 char **ap; 433 434 errs = 0; 435 for (ap = arglist; *ap != NULL; ap++) { 436 if ((vp2 = lookup(*ap)) == NULL) { 437 if (!sourcing) { 438 printf("\"%s\": undefined variable\n", *ap); 439 errs++; 440 } 441 continue; 442 } 443 h = hash(*ap); 444 if (vp2 == variables[h]) { 445 variables[h] = variables[h]->v_link; 446 vfree(vp2->v_name); 447 vfree(vp2->v_value); 448 (void)free(vp2); 449 continue; 450 } 451 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link) 452 ; 453 vp->v_link = vp2->v_link; 454 vfree(vp2->v_name); 455 vfree(vp2->v_value); 456 (void)free(vp2); 457 } 458 return(errs); 459 } 460 461 /* 462 * Put add users to a group. 463 */ 464 int 465 group(void *v) 466 { 467 char **argv = v; 468 struct grouphead *gh; 469 struct group *gp; 470 char **ap, *gname, **p; 471 int h, s; 472 473 if (*argv == NULL) { 474 for (h = 0, s = 1; h < HSHSIZE; h++) 475 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 476 s++; 477 ap = (char **)salloc(s * sizeof(*ap)); 478 for (h = 0, p = ap; h < HSHSIZE; h++) 479 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 480 *p++ = gh->g_name; 481 *p = NULL; 482 sort(ap); 483 for (p = ap; *p != NULL; p++) 484 printgroup(*p); 485 return(0); 486 } 487 if (argv[1] == NULL) { 488 printgroup(*argv); 489 return(0); 490 } 491 gname = *argv; 492 h = hash(gname); 493 if ((gh = findgroup(gname)) == NULL) { 494 if ((gh = (struct grouphead *)calloc(sizeof(*gh), 1)) == NULL) 495 errx(1, "Out of memory"); 496 gh->g_name = vcopy(gname); 497 gh->g_list = NULL; 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 != NULL; ap++) { 509 if ((gp = (struct group *)calloc(sizeof(*gp), 1)) == NULL) 510 errx(1, "Out of memory"); 511 gp->ge_name = vcopy(*ap); 512 gp->ge_link = gh->g_list; 513 gh->g_list = gp; 514 } 515 return(0); 516 } 517 518 /* 519 * Sort the passed string vecotor into ascending dictionary 520 * order. 521 */ 522 void 523 sort(char **list) 524 { 525 char **ap; 526 527 for (ap = list; *ap != NULL; 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(const void *a, const void *b) 540 { 541 542 return(strcmp(*(char **)a, *(char **)b)); 543 } 544 545 /* 546 * The do nothing command for comments. 547 */ 548 /*ARGSUSED*/ 549 int 550 null(void *v) 551 { 552 553 return(0); 554 } 555 556 /* 557 * Change to another file. With no argument, print information about 558 * the current file. 559 */ 560 int 561 file(void *v) 562 { 563 char **argv = v; 564 565 if (argv[0] == NULL) { 566 newfileinfo(0); 567 clearnew(); 568 return(0); 569 } 570 if (setfile(*argv) < 0) 571 return(1); 572 announce(); 573 return(0); 574 } 575 576 /* 577 * Expand file names like echo 578 */ 579 int 580 echo(void *v) 581 { 582 char **argv = v; 583 char **ap, *cp; 584 585 for (ap = argv; *ap != NULL; ap++) { 586 cp = *ap; 587 if ((cp = expand(cp)) != NULL) { 588 if (ap != argv) 589 putchar(' '); 590 fputs(cp, stdout); 591 } 592 } 593 putchar('\n'); 594 return(0); 595 } 596 597 int 598 Respond(void *v) 599 { 600 int *msgvec = v; 601 602 if (value("Replyall") == NULL) 603 return(_Respond(msgvec)); 604 else 605 return(_respond(msgvec)); 606 } 607 608 /* 609 * Reply to a series of messages by simply mailing to the senders 610 * and not messing around with the To: and Cc: lists as in normal 611 * reply. 612 */ 613 int 614 _Respond(int *msgvec) 615 { 616 struct header head; 617 struct message *mp; 618 int *ap; 619 char *cp; 620 621 head.h_to = NULL; 622 for (ap = msgvec; *ap != 0; ap++) { 623 mp = &message[*ap - 1]; 624 touch(mp); 625 dot = mp; 626 if ((cp = skin(hfield("from", mp))) == NULL) 627 cp = skin(nameof(mp, 2)); 628 head.h_to = cat(head.h_to, extract(cp, GTO)); 629 } 630 if (head.h_to == NULL) 631 return(0); 632 mp = &message[msgvec[0] - 1]; 633 if ((head.h_subject = hfield("subject", mp)) == NULL) 634 head.h_subject = hfield("subj", mp); 635 head.h_subject = reedit(head.h_subject); 636 head.h_cc = NULL; 637 head.h_bcc = NULL; 638 head.h_smopts = NULL; 639 mail1(&head, 1); 640 return(0); 641 } 642 643 /* 644 * Conditional commands. These allow one to parameterize one's 645 * .mailrc and do some things if sending, others if receiving. 646 */ 647 int 648 ifcmd(void *v) 649 { 650 char **argv = v; 651 char *cp; 652 653 if (cond != CANY) { 654 puts("Illegal nested \"if\""); 655 return(1); 656 } 657 cond = CANY; 658 cp = argv[0]; 659 switch (*cp) { 660 case 'r': case 'R': 661 cond = CRCV; 662 break; 663 664 case 's': case 'S': 665 cond = CSEND; 666 break; 667 668 default: 669 printf("Unrecognized if-keyword: \"%s\"\n", cp); 670 return(1); 671 } 672 return(0); 673 } 674 675 /* 676 * Implement 'else'. This is pretty simple -- we just 677 * flip over the conditional flag. 678 */ 679 int 680 elsecmd(void *v) 681 { 682 683 switch (cond) { 684 case CANY: 685 puts("\"Else\" without matching \"if\""); 686 return(1); 687 688 case CSEND: 689 cond = CRCV; 690 break; 691 692 case CRCV: 693 cond = CSEND; 694 break; 695 696 default: 697 puts("mail's idea of conditions is screwed up"); 698 cond = CANY; 699 break; 700 } 701 return(0); 702 } 703 704 /* 705 * End of if statement. Just set cond back to anything. 706 */ 707 int 708 endifcmd(void *v) 709 { 710 711 if (cond == CANY) { 712 puts("\"Endif\" without matching \"if\""); 713 return(1); 714 } 715 cond = CANY; 716 return(0); 717 } 718 719 /* 720 * Set the list of alternate names. 721 */ 722 int 723 alternates(void *v) 724 { 725 char **namelist = v; 726 char **ap, **ap2; 727 int c; 728 729 c = argcount(namelist) + 1; 730 if (c == 1) { 731 if (altnames == 0) 732 return(0); 733 for (ap = altnames; *ap; ap++) 734 printf("%s ", *ap); 735 putchar('\n'); 736 return(0); 737 } 738 if (altnames != 0) 739 (void)free(altnames); 740 if ((altnames = (char **)calloc(c, sizeof(char *))) == NULL) 741 errx(1, "Out of memory"); 742 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) { 743 if ((*ap2 = strdup(*ap)) == NULL) 744 errx(1, "Out of memory"); 745 } 746 *ap2 = 0; 747 return(0); 748 } 749