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