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