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