1 /* $OpenBSD: cmd3.c,v 1.10 1997/11/14 00:23:44 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.10 1997/11/14 00:23:44 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 if ((shell = value("SHELL")) == NULL) 73 shell = _PATH_CSHELL; 74 (void)run_command(shell, 0, -1, -1, "-c", cmd, NULL); 75 (void)signal(SIGINT, sigint); 76 puts("!"); 77 return(0); 78 } 79 80 /* 81 * Fork an interactive shell. 82 */ 83 /*ARGSUSED*/ 84 int 85 dosh(v) 86 void *v; 87 { 88 sig_t sigint = signal(SIGINT, SIG_IGN); 89 char *shell; 90 91 if ((shell = value("SHELL")) == NULL) 92 shell = _PATH_CSHELL; 93 (void)run_command(shell, 0, -1, -1, NULL, NULL, NULL); 94 (void)signal(SIGINT, sigint); 95 putchar('\n'); 96 return(0); 97 } 98 99 /* 100 * Expand the shell escape by expanding unescaped !'s into the 101 * last issued command where possible. 102 */ 103 int 104 bangexp(str, strsize) 105 char *str; 106 size_t strsize; 107 { 108 char bangbuf[BUFSIZ]; 109 static char lastbang[BUFSIZ]; 110 char *cp, *cp2; 111 int n, changed = 0; 112 113 cp = str; 114 cp2 = bangbuf; 115 n = BUFSIZ; 116 while (*cp) { 117 if (*cp == '!') { 118 if (n < strlen(lastbang)) { 119 overf: 120 puts("Command buffer overflow"); 121 return(-1); 122 } 123 changed++; 124 strncpy(cp2, lastbang, sizeof(bangbuf) - (cp2 - bangbuf) - 1); 125 bangbuf[sizeof(bangbuf) - 1] = '\0'; 126 cp2 += strlen(lastbang); 127 n -= strlen(lastbang); 128 cp++; 129 continue; 130 } 131 if (*cp == '\\' && cp[1] == '!') { 132 if (--n <= 1) 133 goto overf; 134 *cp2++ = '!'; 135 cp += 2; 136 changed++; 137 } 138 if (--n <= 1) 139 goto overf; 140 *cp2++ = *cp++; 141 } 142 *cp2 = 0; 143 if (changed) { 144 (void)printf("!%s\n", bangbuf); 145 (void)fflush(stdout); 146 } 147 (void)strncpy(str, bangbuf, strsize - 1); 148 str[strsize - 1] = '\0'; 149 (void)strncpy(lastbang, bangbuf, sizeof(lastbang) - 1); 150 lastbang[sizeof(lastbang) - 1] = '\0'; 151 return(0); 152 } 153 154 /* 155 * Print out a nice help message from some file or another. 156 */ 157 158 int 159 help(v) 160 void *v; 161 { 162 int c; 163 FILE *f; 164 165 if ((f = Fopen(_PATH_HELP, "r")) == NULL) { 166 warn(_PATH_HELP); 167 return(1); 168 } 169 while ((c = getc(f)) != EOF) 170 putchar(c); 171 (void)Fclose(f); 172 return(0); 173 } 174 175 /* 176 * Change user's working directory. 177 */ 178 int 179 schdir(v) 180 void *v; 181 { 182 char **arglist = v; 183 char *cp; 184 185 if (*arglist == NULL) 186 cp = homedir; 187 else 188 if ((cp = expand(*arglist)) == NULL) 189 return(1); 190 if (chdir(cp) < 0) { 191 warn(cp); 192 return(1); 193 } 194 return(0); 195 } 196 197 int 198 respond(v) 199 void *v; 200 { 201 int *msgvec = v; 202 if (value("Replyall") == NULL) 203 return(_respond(msgvec)); 204 else 205 return(_Respond(msgvec)); 206 } 207 208 /* 209 * Reply to a list of messages. Extract each name from the 210 * message header and send them off to mail1() 211 */ 212 int 213 _respond(msgvec) 214 int *msgvec; 215 { 216 struct message *mp; 217 char *cp, *rcv, *replyto; 218 char **ap; 219 struct name *np; 220 struct header head; 221 222 if (msgvec[1] != 0) { 223 puts("Sorry, can't reply to multiple messages at once"); 224 return(1); 225 } 226 mp = &message[msgvec[0] - 1]; 227 touch(mp); 228 dot = mp; 229 if ((rcv = skin(hfield("from", mp))) == NULL) 230 rcv = skin(nameof(mp, 1)); 231 if ((replyto = skin(hfield("reply-to", mp))) != NULL) 232 np = extract(replyto, GTO); 233 else if ((cp = skin(hfield("to", mp))) != NULL) 234 np = extract(cp, GTO); 235 else 236 np = NIL; 237 np = elide(np); 238 /* 239 * Delete my name from the reply list, 240 * and with it, all my alternate names. 241 */ 242 np = delname(np, myname); 243 if (altnames) 244 for (ap = altnames; *ap; ap++) 245 np = delname(np, *ap); 246 if (np != NIL && replyto == NULL) 247 np = cat(np, extract(rcv, GTO)); 248 else if (np == NIL) { 249 if (replyto != NULL) 250 puts("Empty reply-to field -- replying to author"); 251 np = extract(rcv, GTO); 252 } 253 head.h_to = np; 254 if ((head.h_subject = hfield("subject", mp)) == NULL) 255 head.h_subject = hfield("subj", mp); 256 head.h_subject = reedit(head.h_subject); 257 if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) { 258 np = elide(extract(cp, GCC)); 259 np = delname(np, myname); 260 if (altnames != 0) 261 for (ap = altnames; *ap; ap++) 262 np = delname(np, *ap); 263 head.h_cc = np; 264 } else 265 head.h_cc = NIL; 266 head.h_bcc = NIL; 267 head.h_smopts = NIL; 268 mail1(&head, 1); 269 return(0); 270 } 271 272 /* 273 * Modify the subject we are replying to to begin with Re: if 274 * it does not already. 275 */ 276 char * 277 reedit(subj) 278 char *subj; 279 { 280 char *newsubj; 281 282 if (subj == NULL) 283 return(NULL); 284 if ((subj[0] == 'r' || subj[0] == 'R') && 285 (subj[1] == 'e' || subj[1] == 'E') && 286 subj[2] == ':') 287 return(subj); 288 newsubj = salloc(strlen(subj) + 5); 289 strcpy(newsubj, "Re: "); 290 strcpy(newsubj + 4, subj); 291 return(newsubj); 292 } 293 294 /* 295 * Preserve the named messages, so that they will be sent 296 * back to the system mailbox. 297 */ 298 int 299 preserve(v) 300 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(v) 325 void *v; 326 { 327 int *msgvec = v; 328 int *ip; 329 330 for (ip = msgvec; *ip != NULL; ip++) { 331 dot = &message[*ip-1]; 332 dot->m_flag &= ~(MREAD|MTOUCH); 333 dot->m_flag |= MSTATUS; 334 } 335 return(0); 336 } 337 338 /* 339 * Print the size of each message. 340 */ 341 int 342 messize(v) 343 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(v) 363 void *v; 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(v) 377 void *v; 378 { 379 char **arglist = v; 380 struct var *vp; 381 char *cp, *cp2; 382 char varbuf[BUFSIZ], **ap, **p; 383 int errs, h, s; 384 385 if (*arglist == NULL) { 386 for (h = 0, s = 1; h < HSHSIZE; h++) 387 for (vp = variables[h]; vp != NOVAR; vp = vp->v_link) 388 s++; 389 ap = (char **)salloc(s * sizeof(*ap)); 390 for (h = 0, p = ap; h < HSHSIZE; h++) 391 for (vp = variables[h]; vp != NOVAR; vp = vp->v_link) 392 *p++ = vp->v_name; 393 *p = NULL; 394 sort(ap); 395 for (p = ap; *p != NULL; p++) 396 printf("%s\t%s\n", *p, value(*p)); 397 return(0); 398 } 399 errs = 0; 400 for (ap = arglist; *ap != NULL; ap++) { 401 cp = *ap; 402 cp2 = varbuf; 403 while (*cp != '=' && *cp != '\0') 404 *cp2++ = *cp++; 405 *cp2 = '\0'; 406 if (*cp == '\0') 407 cp = ""; 408 else 409 cp++; 410 if (equal(varbuf, "")) { 411 puts("Non-null variable name required"); 412 errs++; 413 continue; 414 } 415 assign(varbuf, cp); 416 } 417 return(errs); 418 } 419 420 /* 421 * Unset a bunch of variable values. 422 */ 423 int 424 unset(v) 425 void *v; 426 { 427 char **arglist = v; 428 struct var *vp, *vp2; 429 int errs, h; 430 char **ap; 431 432 errs = 0; 433 for (ap = arglist; *ap != NULL; ap++) { 434 if ((vp2 = lookup(*ap)) == NOVAR) { 435 if (!sourcing) { 436 printf("\"%s\": undefined variable\n", *ap); 437 errs++; 438 } 439 continue; 440 } 441 h = hash(*ap); 442 if (vp2 == variables[h]) { 443 variables[h] = variables[h]->v_link; 444 vfree(vp2->v_name); 445 vfree(vp2->v_value); 446 (void)free(vp2); 447 continue; 448 } 449 for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link) 450 ; 451 vp->v_link = vp2->v_link; 452 vfree(vp2->v_name); 453 vfree(vp2->v_value); 454 (void)free(vp2); 455 } 456 return(errs); 457 } 458 459 /* 460 * Put add users to a group. 461 */ 462 int 463 group(v) 464 void *v; 465 { 466 char **argv = v; 467 struct grouphead *gh; 468 struct group *gp; 469 char **ap, *gname, **p; 470 int h, s; 471 472 if (*argv == NULL) { 473 for (h = 0, s = 1; h < HSHSIZE; h++) 474 for (gh = groups[h]; gh != NOGRP; gh = gh->g_link) 475 s++; 476 ap = (char **)salloc(s * sizeof(*ap)); 477 for (h = 0, p = ap; h < HSHSIZE; h++) 478 for (gh = groups[h]; gh != NOGRP; gh = gh->g_link) 479 *p++ = gh->g_name; 480 *p = NULL; 481 sort(ap); 482 for (p = ap; *p != NULL; p++) 483 printgroup(*p); 484 return(0); 485 } 486 if (argv[1] == NULL) { 487 printgroup(*argv); 488 return(0); 489 } 490 gname = *argv; 491 h = hash(gname); 492 if ((gh = findgroup(gname)) == NOGRP) { 493 gh = (struct grouphead *)calloc(sizeof(*gh), 1); 494 gh->g_name = vcopy(gname); 495 gh->g_list = NOGE; 496 gh->g_link = groups[h]; 497 groups[h] = gh; 498 } 499 500 /* 501 * Insert names from the command list into the group. 502 * Who cares if there are duplicates? They get tossed 503 * later anyway. 504 */ 505 506 for (ap = argv+1; *ap != NULL; ap++) { 507 gp = (struct group *)calloc(sizeof(*gp), 1); 508 gp->ge_name = vcopy(*ap); 509 gp->ge_link = gh->g_list; 510 gh->g_list = gp; 511 } 512 return(0); 513 } 514 515 /* 516 * Sort the passed string vecotor into ascending dictionary 517 * order. 518 */ 519 void 520 sort(list) 521 char **list; 522 { 523 char **ap; 524 525 for (ap = list; *ap != NULL; ap++) 526 ; 527 if (ap-list < 2) 528 return; 529 qsort(list, ap-list, sizeof(*list), diction); 530 } 531 532 /* 533 * Do a dictionary order comparison of the arguments from 534 * qsort. 535 */ 536 static int 537 diction(a, b) 538 const void *a, *b; 539 { 540 return(strcmp(*(char **)a, *(char **)b)); 541 } 542 543 /* 544 * The do nothing command for comments. 545 */ 546 547 /*ARGSUSED*/ 548 int 549 null(v) 550 void *v; 551 { 552 return(0); 553 } 554 555 /* 556 * Change to another file. With no argument, print information about 557 * the current file. 558 */ 559 int 560 file(v) 561 void *v; 562 { 563 char **argv = v; 564 565 if (argv[0] == NULL) { 566 newfileinfo(0); 567 return(0); 568 } 569 if (setfile(*argv) < 0) 570 return(1); 571 announce(); 572 return(0); 573 } 574 575 /* 576 * Expand file names like echo 577 */ 578 int 579 echo(v) 580 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(v) 599 void *v; 600 { 601 int *msgvec = v; 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(msgvec) 615 int msgvec[]; 616 { 617 struct header head; 618 struct message *mp; 619 int *ap; 620 char *cp; 621 622 head.h_to = NIL; 623 for (ap = msgvec; *ap != 0; ap++) { 624 mp = &message[*ap - 1]; 625 touch(mp); 626 dot = mp; 627 if ((cp = skin(hfield("from", mp))) == NULL) 628 cp = skin(nameof(mp, 2)); 629 head.h_to = cat(head.h_to, extract(cp, GTO)); 630 } 631 if (head.h_to == NIL) 632 return(0); 633 mp = &message[msgvec[0] - 1]; 634 if ((head.h_subject = hfield("subject", mp)) == NULL) 635 head.h_subject = hfield("subj", mp); 636 head.h_subject = reedit(head.h_subject); 637 head.h_cc = NIL; 638 head.h_bcc = NIL; 639 head.h_smopts = NIL; 640 mail1(&head, 1); 641 return(0); 642 } 643 644 /* 645 * Conditional commands. These allow one to parameterize one's 646 * .mailrc and do some things if sending, others if receiving. 647 */ 648 int 649 ifcmd(v) 650 void *v; 651 { 652 char **argv = v; 653 char *cp; 654 655 if (cond != CANY) { 656 puts("Illegal nested \"if\""); 657 return(1); 658 } 659 cond = CANY; 660 cp = argv[0]; 661 switch (*cp) { 662 case 'r': case 'R': 663 cond = CRCV; 664 break; 665 666 case 's': case 'S': 667 cond = CSEND; 668 break; 669 670 default: 671 printf("Unrecognized if-keyword: \"%s\"\n", cp); 672 return(1); 673 } 674 return(0); 675 } 676 677 /* 678 * Implement 'else'. This is pretty simple -- we just 679 * flip over the conditional flag. 680 */ 681 int 682 elsecmd(v) 683 void *v; 684 { 685 686 switch (cond) { 687 case CANY: 688 puts("\"Else\" without matching \"if\""); 689 return(1); 690 691 case CSEND: 692 cond = CRCV; 693 break; 694 695 case CRCV: 696 cond = CSEND; 697 break; 698 699 default: 700 puts("mail's idea of conditions is screwed up"); 701 cond = CANY; 702 break; 703 } 704 return(0); 705 } 706 707 /* 708 * End of if statement. Just set cond back to anything. 709 */ 710 int 711 endifcmd(v) 712 void *v; 713 { 714 715 if (cond == CANY) { 716 puts("\"Endif\" without matching \"if\""); 717 return(1); 718 } 719 cond = CANY; 720 return(0); 721 } 722 723 /* 724 * Set the list of alternate names. 725 */ 726 int 727 alternates(v) 728 void *v; 729 { 730 char **namelist = v; 731 char **ap, **ap2, *cp; 732 int c; 733 734 c = argcount(namelist) + 1; 735 if (c == 1) { 736 if (altnames == 0) 737 return(0); 738 for (ap = altnames; *ap; ap++) 739 printf("%s ", *ap); 740 putchar('\n'); 741 return(0); 742 } 743 if (altnames != 0) 744 (void)free(altnames); 745 altnames = (char **)calloc(c, sizeof(char *)); 746 for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) { 747 cp = (char *)calloc(strlen(*ap) + 1, sizeof(char)); 748 strcpy(cp, *ap); 749 *ap2 = cp; 750 } 751 *ap2 = 0; 752 return(0); 753 } 754