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