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