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