1 /* $OpenBSD: cmd3.c,v 1.23 2009/09/05 10:49:45 tobias 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.23 2009/09/05 10:49:45 tobias 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 /* 222 * Delete my name from the reply list, 223 * and with it, all my alternate names. 224 */ 225 np = delname(np, myname); 226 if (altnames) 227 for (ap = altnames; *ap; ap++) 228 np = delname(np, *ap); 229 if (np != NULL && replyto == NULL) 230 np = cat(np, extract(rcv, GTO)); 231 else if (np == NULL) { 232 if (replyto != NULL) 233 puts("Empty reply-to field -- replying to author"); 234 np = extract(rcv, GTO); 235 } 236 np = elide(np); 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 (strncasecmp(subj, "re:", 3) == 0) 269 return(subj); 270 len = strlen(subj) + 5; 271 newsubj = salloc(len); 272 strlcpy(newsubj, "Re: ", len); 273 strlcat(newsubj, subj, len); 274 return(newsubj); 275 } 276 277 /* 278 * Mark new the named messages, so that they will be left in the system 279 * mailbox as unread. 280 */ 281 int 282 marknew(void *v) 283 { 284 int *msgvec = v; 285 int *ip; 286 287 for (ip = msgvec; *ip != NULL; ip++) { 288 dot = &message[*ip-1]; 289 dot->m_flag &= ~(MBOX|MREAD|MTOUCH); 290 dot->m_flag |= MNEW|MSTATUS; 291 } 292 return(0); 293 } 294 295 /* 296 * Preserve the named messages, so that they will be sent 297 * back to the system mailbox. 298 */ 299 int 300 preserve(void *v) 301 { 302 int *msgvec = v; 303 int *ip, mesg; 304 struct message *mp; 305 306 if (edit) { 307 puts("Cannot \"preserve\" in edit mode"); 308 return(1); 309 } 310 for (ip = msgvec; *ip != NULL; ip++) { 311 mesg = *ip; 312 mp = &message[mesg-1]; 313 mp->m_flag |= MPRESERVE; 314 mp->m_flag &= ~MBOX; 315 dot = mp; 316 } 317 return(0); 318 } 319 320 /* 321 * Mark all given messages as unread. 322 */ 323 int 324 unread(void *v) 325 { 326 int *msgvec = v; 327 int *ip; 328 329 for (ip = msgvec; *ip != NULL; 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(void *v) 342 { 343 int *msgvec = v; 344 struct message *mp; 345 int *ip, mesg; 346 347 for (ip = msgvec; *ip != NULL; ip++) { 348 mesg = *ip; 349 mp = &message[mesg-1]; 350 printf("%d: %d/%d\n", mesg, mp->m_lines, mp->m_size); 351 } 352 return(0); 353 } 354 355 /* 356 * Quit quickly. If we are sourcing, just pop the input level 357 * by returning an error. 358 */ 359 int 360 rexit(void *v) 361 { 362 363 if (sourcing) 364 return(1); 365 exit(0); 366 /*NOTREACHED*/ 367 } 368 369 /* 370 * Set or display a variable value. Syntax is similar to that 371 * of csh. 372 */ 373 int 374 set(void *v) 375 { 376 char **arglist = v; 377 struct var *vp; 378 char *cp, *cp2; 379 char varbuf[BUFSIZ], **ap, **p; 380 int errs, h, s; 381 382 if (*arglist == NULL) { 383 for (h = 0, s = 1; h < HSHSIZE; h++) 384 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 385 s++; 386 ap = (char **)salloc(s * sizeof(*ap)); 387 for (h = 0, p = ap; h < HSHSIZE; h++) 388 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 389 *p++ = vp->v_name; 390 *p = NULL; 391 sort(ap); 392 for (p = ap; *p != NULL; p++) 393 printf("%s\t%s\n", *p, value(*p)); 394 return(0); 395 } 396 errs = 0; 397 for (ap = arglist; *ap != NULL; ap++) { 398 cp = *ap; 399 cp2 = varbuf; 400 while (*cp != '=' && *cp != '\0') 401 *cp2++ = *cp++; 402 *cp2 = '\0'; 403 if (*cp == '\0') 404 cp = ""; 405 else 406 cp++; 407 if (equal(varbuf, "")) { 408 puts("Non-null variable name required"); 409 errs++; 410 continue; 411 } 412 assign(varbuf, cp); 413 } 414 return(errs); 415 } 416 417 /* 418 * Unset a bunch of variable values. 419 */ 420 int 421 unset(void *v) 422 { 423 char **arglist = v; 424 struct var *vp, *vp2; 425 int errs, h; 426 char **ap; 427 428 errs = 0; 429 for (ap = arglist; *ap != NULL; ap++) { 430 if ((vp2 = lookup(*ap)) == NULL) { 431 if (!sourcing) { 432 printf("\"%s\": undefined variable\n", *ap); 433 errs++; 434 } 435 continue; 436 } 437 h = hash(*ap); 438 if (vp2 == variables[h]) { 439 variables[h] = variables[h]->v_link; 440 vfree(vp2->v_name); 441 vfree(vp2->v_value); 442 (void)free(vp2); 443 continue; 444 } 445 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link) 446 ; 447 vp->v_link = vp2->v_link; 448 vfree(vp2->v_name); 449 vfree(vp2->v_value); 450 (void)free(vp2); 451 } 452 return(errs); 453 } 454 455 /* 456 * Put add users to a group. 457 */ 458 int 459 group(void *v) 460 { 461 char **argv = v; 462 struct grouphead *gh; 463 struct group *gp; 464 char **ap, *gname, **p; 465 int h, s; 466 467 if (*argv == NULL) { 468 for (h = 0, s = 1; h < HSHSIZE; h++) 469 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 470 s++; 471 ap = (char **)salloc(s * sizeof(*ap)); 472 for (h = 0, p = ap; h < HSHSIZE; h++) 473 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 474 *p++ = gh->g_name; 475 *p = NULL; 476 sort(ap); 477 for (p = ap; *p != NULL; p++) 478 printgroup(*p); 479 return(0); 480 } 481 if (argv[1] == NULL) { 482 printgroup(*argv); 483 return(0); 484 } 485 gname = *argv; 486 h = hash(gname); 487 if ((gh = findgroup(gname)) == NULL) { 488 if ((gh = (struct grouphead *)calloc(1, sizeof(*gh))) == NULL) 489 errx(1, "Out of memory"); 490 gh->g_name = vcopy(gname); 491 gh->g_list = NULL; 492 gh->g_link = groups[h]; 493 groups[h] = gh; 494 } 495 496 /* 497 * Insert names from the command list into the group. 498 * Who cares if there are duplicates? They get tossed 499 * later anyway. 500 */ 501 502 for (ap = argv+1; *ap != NULL; ap++) { 503 if ((gp = (struct group *)calloc(1, sizeof(*gp))) == NULL) 504 errx(1, "Out of memory"); 505 gp->ge_name = vcopy(*ap); 506 gp->ge_link = gh->g_list; 507 gh->g_list = gp; 508 } 509 return(0); 510 } 511 512 /* 513 * Sort the passed string vector into ascending dictionary 514 * order. 515 */ 516 void 517 sort(char **list) 518 { 519 char **ap; 520 521 for (ap = list; *ap != NULL; ap++) 522 ; 523 if (ap-list < 2) 524 return; 525 qsort(list, ap-list, sizeof(*list), diction); 526 } 527 528 /* 529 * Do a dictionary order comparison of the arguments from 530 * qsort. 531 */ 532 static int 533 diction(const void *a, const void *b) 534 { 535 536 return(strcmp(*(char **)a, *(char **)b)); 537 } 538 539 /* 540 * The do nothing command for comments. 541 */ 542 /*ARGSUSED*/ 543 int 544 null(void *v) 545 { 546 547 return(0); 548 } 549 550 /* 551 * Change to another file. With no argument, print information about 552 * the current file. 553 */ 554 int 555 file(void *v) 556 { 557 char **argv = v; 558 559 if (argv[0] == NULL) { 560 newfileinfo(0); 561 clearnew(); 562 return(0); 563 } 564 if (setfile(*argv) < 0) 565 return(1); 566 announce(); 567 return(0); 568 } 569 570 /* 571 * Expand file names like echo 572 */ 573 int 574 echo(void *v) 575 { 576 char **argv = v; 577 char **ap, *cp; 578 579 for (ap = argv; *ap != NULL; ap++) { 580 cp = *ap; 581 if ((cp = expand(cp)) != NULL) { 582 if (ap != argv) 583 putchar(' '); 584 fputs(cp, stdout); 585 } 586 } 587 putchar('\n'); 588 return(0); 589 } 590 591 int 592 Respond(void *v) 593 { 594 int *msgvec = v; 595 596 if (value("Replyall") == NULL) 597 return(_Respond(msgvec)); 598 else 599 return(_respond(msgvec)); 600 } 601 602 /* 603 * Reply to a series of messages by simply mailing to the senders 604 * and not messing around with the To: and Cc: lists as in normal 605 * reply. 606 */ 607 int 608 _Respond(int *msgvec) 609 { 610 struct header head; 611 struct message *mp; 612 int *ap; 613 char *cp; 614 615 head.h_to = NULL; 616 for (ap = msgvec; *ap != 0; ap++) { 617 mp = &message[*ap - 1]; 618 touch(mp); 619 dot = mp; 620 if ((cp = skin(hfield("from", mp))) == NULL) 621 cp = skin(nameof(mp, 2)); 622 head.h_to = cat(head.h_to, extract(cp, GTO)); 623 } 624 if (head.h_to == NULL) 625 return(0); 626 mp = &message[msgvec[0] - 1]; 627 if ((head.h_subject = hfield("subject", mp)) == NULL) 628 head.h_subject = hfield("subj", mp); 629 head.h_subject = reedit(head.h_subject); 630 head.h_cc = NULL; 631 head.h_bcc = NULL; 632 head.h_smopts = NULL; 633 mail1(&head, 1); 634 return(0); 635 } 636 637 /* 638 * Conditional commands. These allow one to parameterize one's 639 * .mailrc and do some things if sending, others if receiving. 640 */ 641 int 642 ifcmd(void *v) 643 { 644 char **argv = v; 645 char *cp; 646 647 if (cond != CANY) { 648 puts("Illegal nested \"if\""); 649 return(1); 650 } 651 cond = CANY; 652 cp = argv[0]; 653 switch (*cp) { 654 case 'r': case 'R': 655 cond = CRCV; 656 break; 657 658 case 's': case 'S': 659 cond = CSEND; 660 break; 661 662 default: 663 printf("Unrecognized if-keyword: \"%s\"\n", cp); 664 return(1); 665 } 666 return(0); 667 } 668 669 /* 670 * Implement 'else'. This is pretty simple -- we just 671 * flip over the conditional flag. 672 */ 673 int 674 elsecmd(void *v) 675 { 676 677 switch (cond) { 678 case CANY: 679 puts("\"Else\" without matching \"if\""); 680 return(1); 681 682 case CSEND: 683 cond = CRCV; 684 break; 685 686 case CRCV: 687 cond = CSEND; 688 break; 689 690 default: 691 puts("mail's idea of conditions is screwed up"); 692 cond = CANY; 693 break; 694 } 695 return(0); 696 } 697 698 /* 699 * End of if statement. Just set cond back to anything. 700 */ 701 int 702 endifcmd(void *v) 703 { 704 705 if (cond == CANY) { 706 puts("\"Endif\" without matching \"if\""); 707 return(1); 708 } 709 cond = CANY; 710 return(0); 711 } 712 713 /* 714 * Set the list of alternate names. 715 */ 716 int 717 alternates(void *v) 718 { 719 char **namelist = v; 720 char **ap, **ap2; 721 int c; 722 723 c = argcount(namelist) + 1; 724 if (c == 1) { 725 if (altnames == 0) 726 return(0); 727 for (ap = altnames; *ap; ap++) 728 printf("%s ", *ap); 729 putchar('\n'); 730 return(0); 731 } 732 if (altnames != 0) 733 (void)free(altnames); 734 if ((altnames = (char **)calloc(c, sizeof(char *))) == NULL) 735 errx(1, "Out of memory"); 736 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) { 737 if ((*ap2 = strdup(*ap)) == NULL) 738 errx(1, "Out of memory"); 739 } 740 *ap2 = 0; 741 return(0); 742 } 743