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