1: /* 2: * Copyright (c) 1980 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #ifndef lint 8: static char *sccsid = "@(#)cmd3.c 5.3 (Berkeley) 9/15/85"; 9: #endif not lint 10: 11: #include "rcv.h" 12: #include <sys/stat.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: 25: shell(str) 26: char *str; 27: { 28: int (*sig[2])(), stat[1]; 29: register int t; 30: char *Shell; 31: char cmd[BUFSIZ]; 32: 33: strcpy(cmd, str); 34: if (bangexp(cmd) < 0) 35: return(-1); 36: if ((Shell = value("SHELL")) == NOSTR) 37: Shell = SHELL; 38: for (t = 2; t < 4; t++) 39: sig[t-2] = sigset(t, SIG_IGN); 40: t = vfork(); 41: if (t == 0) { 42: sigchild(); 43: for (t = 2; t < 4; t++) 44: if (sig[t-2] != SIG_IGN) 45: sigsys(t, SIG_DFL); 46: execl(Shell, Shell, "-c", cmd, (char *)0); 47: perror(Shell); 48: _exit(1); 49: } 50: while (wait(stat) != t) 51: ; 52: if (t == -1) 53: perror("fork"); 54: for (t = 2; t < 4; t++) 55: sigset(t, sig[t-2]); 56: printf("!\n"); 57: return(0); 58: } 59: 60: /* 61: * Fork an interactive shell. 62: */ 63: 64: dosh(str) 65: char *str; 66: { 67: int (*sig[2])(), stat[1]; 68: register int t; 69: char *Shell; 70: if ((Shell = value("SHELL")) == NOSTR) 71: Shell = SHELL; 72: for (t = 2; t < 4; t++) 73: sig[t-2] = sigset(t, SIG_IGN); 74: t = vfork(); 75: if (t == 0) { 76: sigchild(); 77: for (t = 2; t < 4; t++) 78: if (sig[t-2] != SIG_IGN) 79: sigsys(t, SIG_DFL); 80: execl(Shell, Shell, (char *)0); 81: perror(Shell); 82: _exit(1); 83: } 84: while (wait(stat) != t) 85: ; 86: if (t == -1) 87: perror("fork"); 88: for (t = 2; t < 4; t++) 89: sigsys(t, sig[t-2]); 90: putchar('\n'); 91: return(0); 92: } 93: 94: /* 95: * Expand the shell escape by expanding unescaped !'s into the 96: * last issued command where possible. 97: */ 98: 99: char lastbang[128]; 100: 101: bangexp(str) 102: char *str; 103: { 104: char bangbuf[BUFSIZ]; 105: register char *cp, *cp2; 106: register int n; 107: int changed = 0; 108: 109: cp = str; 110: cp2 = bangbuf; 111: n = BUFSIZ; 112: while (*cp) { 113: if (*cp == '!') { 114: if (n < strlen(lastbang)) { 115: overf: 116: printf("Command buffer overflow\n"); 117: return(-1); 118: } 119: changed++; 120: strcpy(cp2, lastbang); 121: cp2 += strlen(lastbang); 122: n -= strlen(lastbang); 123: cp++; 124: continue; 125: } 126: if (*cp == '\\' && cp[1] == '!') { 127: if (--n <= 1) 128: goto overf; 129: *cp2++ = '!'; 130: cp += 2; 131: changed++; 132: } 133: if (--n <= 1) 134: goto overf; 135: *cp2++ = *cp++; 136: } 137: *cp2 = 0; 138: if (changed) { 139: printf("!%s\n", bangbuf); 140: fflush(stdout); 141: } 142: strcpy(str, bangbuf); 143: strncpy(lastbang, bangbuf, 128); 144: lastbang[127] = 0; 145: return(0); 146: } 147: 148: /* 149: * Print out a nice help message from some file or another. 150: */ 151: 152: help() 153: { 154: register c; 155: register FILE *f; 156: 157: if ((f = fopen(HELPFILE, "r")) == NULL) { 158: perror(HELPFILE); 159: return(1); 160: } 161: while ((c = getc(f)) != EOF) 162: putchar(c); 163: fclose(f); 164: return(0); 165: } 166: 167: /* 168: * Change user's working directory. 169: */ 170: 171: schdir(str) 172: char *str; 173: { 174: register char *cp; 175: 176: for (cp = str; *cp == ' '; cp++) 177: ; 178: if (*cp == '\0') 179: cp = homedir; 180: else 181: if ((cp = expand(cp)) == NOSTR) 182: return(1); 183: if (chdir(cp) < 0) { 184: perror(cp); 185: return(1); 186: } 187: return(0); 188: } 189: 190: respond(msgvec) 191: int *msgvec; 192: { 193: if (value("Replyall") == NOSTR) 194: return (_respond(msgvec)); 195: else 196: return (_Respond(msgvec)); 197: } 198: 199: /* 200: * Reply to a list of messages. Extract each name from the 201: * message header and send them off to mail1() 202: */ 203: 204: _respond(msgvec) 205: int *msgvec; 206: { 207: struct message *mp; 208: char *cp, *cp2, *cp3, *rcv, *replyto; 209: char buf[2 * LINESIZE], **ap; 210: struct name *np; 211: struct header head; 212: 213: if (msgvec[1] != 0) { 214: printf("Sorry, can't reply to multiple messages at once\n"); 215: return(1); 216: } 217: mp = &message[msgvec[0] - 1]; 218: dot = mp; 219: rcv = NOSTR; 220: cp = skin(nameof(mp, 1)); 221: if (cp != NOSTR) 222: rcv = cp; 223: cp = skin(hfield("from", mp)); 224: if (cp != NOSTR) 225: rcv = cp; 226: replyto = skin(hfield("reply-to", mp)); 227: strcpy(buf, ""); 228: if (replyto != NOSTR) 229: strcpy(buf, replyto); 230: else { 231: cp = skin(hfield("to", mp)); 232: if (cp != NOSTR) 233: strcpy(buf, cp); 234: } 235: np = elide(extract(buf, GTO)); 236: /* rcv = rename(rcv); */ 237: mapf(np, rcv); 238: /* 239: * Delete my name from the reply list, 240: * and with it, all my alternate names. 241: */ 242: np = delname(np, myname, icequal); 243: if (altnames) 244: for (ap = altnames; *ap; ap++) 245: np = delname(np, *ap, icequal); 246: head.h_seq = 1; 247: cp = detract(np, 0); 248: if (cp != NOSTR && replyto == NOSTR) { 249: strcpy(buf, cp); 250: strcat(buf, " "); 251: strcat(buf, rcv); 252: } 253: else { 254: if (cp == NOSTR && replyto != NOSTR) 255: printf("Empty reply-to field -- replying to author\n"); 256: if (cp == NOSTR) 257: strcpy(buf, rcv); 258: else 259: strcpy(buf, cp); 260: } 261: head.h_to = buf; 262: head.h_subject = hfield("subject", mp); 263: if (head.h_subject == NOSTR) 264: head.h_subject = hfield("subj", mp); 265: head.h_subject = reedit(head.h_subject); 266: head.h_cc = NOSTR; 267: if (replyto == NOSTR) { 268: cp = hfield("cc", mp); 269: if (cp != NOSTR) { 270: np = elide(extract(cp, GCC)); 271: mapf(np, rcv); 272: np = delname(np, myname, icequal); 273: if (altnames != 0) 274: for (ap = altnames; *ap; ap++) 275: np = delname(np, *ap, icequal); 276: head.h_cc = detract(np, 0); 277: } 278: } 279: head.h_bcc = NOSTR; 280: mail1(&head); 281: return(0); 282: } 283: 284: /* 285: * Modify the subject we are replying to to begin with Re: if 286: * it does not already. 287: */ 288: 289: char * 290: reedit(subj) 291: char *subj; 292: { 293: char sbuf[10]; 294: register char *newsubj; 295: 296: if (subj == NOSTR) 297: return(NOSTR); 298: strncpy(sbuf, subj, 3); 299: sbuf[3] = 0; 300: if (icequal(sbuf, "re:")) 301: return(subj); 302: newsubj = salloc(strlen(subj) + 6); 303: sprintf(newsubj, "Re: %s", subj); 304: return(newsubj); 305: } 306: 307: /* 308: * Preserve the named messages, so that they will be sent 309: * back to the system mailbox. 310: */ 311: 312: preserve(msgvec) 313: int *msgvec; 314: { 315: register struct message *mp; 316: register int *ip, mesg; 317: 318: if (edit) { 319: printf("Cannot \"preserve\" in edit mode\n"); 320: return(1); 321: } 322: for (ip = msgvec; *ip != NULL; ip++) { 323: mesg = *ip; 324: mp = &message[mesg-1]; 325: mp->m_flag |= MPRESERVE; 326: mp->m_flag &= ~MBOX; 327: dot = mp; 328: } 329: return(0); 330: } 331: 332: /* 333: * Mark all given messages as unread. 334: */ 335: unread(msgvec) 336: int msgvec[]; 337: { 338: register int *ip; 339: 340: for (ip = msgvec; *ip != NULL; ip++) { 341: dot = &message[*ip-1]; 342: dot->m_flag &= ~(MREAD|MTOUCH); 343: dot->m_flag |= MSTATUS; 344: } 345: return(0); 346: } 347: 348: /* 349: * Print the size of each message. 350: */ 351: 352: messize(msgvec) 353: int *msgvec; 354: { 355: register struct message *mp; 356: register int *ip, mesg; 357: 358: for (ip = msgvec; *ip != NULL; ip++) { 359: mesg = *ip; 360: mp = &message[mesg-1]; 361: printf("%d: %d/%ld\n", mesg, mp->m_lines, mp->m_size); 362: } 363: return(0); 364: } 365: 366: /* 367: * Quit quickly. If we are sourcing, just pop the input level 368: * by returning an error. 369: */ 370: 371: rexit(e) 372: { 373: if (sourcing) 374: return(1); 375: if (Tflag != NOSTR) 376: close(creat(Tflag, 0600)); 377: exit(e); 378: } 379: 380: /* 381: * Set or display a variable value. Syntax is similar to that 382: * of csh. 383: */ 384: 385: set(arglist) 386: char **arglist; 387: { 388: register struct var *vp; 389: register char *cp, *cp2; 390: char varbuf[BUFSIZ], **ap, **p; 391: int errs, h, s; 392: 393: if (argcount(arglist) == 0) { 394: for (h = 0, s = 1; h < HSHSIZE; h++) 395: for (vp = variables[h]; vp != NOVAR; vp = vp->v_link) 396: s++; 397: ap = (char **) salloc(s * sizeof *ap); 398: for (h = 0, p = ap; h < HSHSIZE; h++) 399: for (vp = variables[h]; vp != NOVAR; vp = vp->v_link) 400: *p++ = vp->v_name; 401: *p = NOSTR; 402: sort(ap); 403: for (p = ap; *p != NOSTR; p++) 404: printf("%s\t%s\n", *p, value(*p)); 405: return(0); 406: } 407: errs = 0; 408: for (ap = arglist; *ap != NOSTR; ap++) { 409: cp = *ap; 410: cp2 = varbuf; 411: while (*cp != '=' && *cp != '\0') 412: *cp2++ = *cp++; 413: *cp2 = '\0'; 414: if (*cp == '\0') 415: cp = ""; 416: else 417: cp++; 418: if (equal(varbuf, "")) { 419: printf("Non-null variable name required\n"); 420: errs++; 421: continue; 422: } 423: assign(varbuf, cp); 424: } 425: return(errs); 426: } 427: 428: /* 429: * Unset a bunch of variable values. 430: */ 431: 432: unset(arglist) 433: char **arglist; 434: { 435: register struct var *vp, *vp2; 436: register char *cp; 437: int errs, h; 438: char **ap; 439: 440: errs = 0; 441: for (ap = arglist; *ap != NOSTR; ap++) { 442: if ((vp2 = lookup(*ap)) == NOVAR) { 443: if (!sourcing) { 444: printf("\"%s\": undefined variable\n", *ap); 445: errs++; 446: } 447: continue; 448: } 449: h = hash(*ap); 450: if (vp2 == variables[h]) { 451: variables[h] = variables[h]->v_link; 452: vfree(vp2->v_name); 453: vfree(vp2->v_value); 454: cfree(vp2); 455: continue; 456: } 457: for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link) 458: ; 459: vp->v_link = vp2->v_link; 460: vfree(vp2->v_name); 461: vfree(vp2->v_value); 462: cfree(vp2); 463: } 464: return(errs); 465: } 466: 467: /* 468: * Put add users to a group. 469: */ 470: 471: group(argv) 472: char **argv; 473: { 474: register struct grouphead *gh; 475: register struct group *gp; 476: register int h; 477: int s; 478: char **ap, *gname, **p; 479: 480: if (argcount(argv) == 0) { 481: for (h = 0, s = 1; h < HSHSIZE; h++) 482: for (gh = groups[h]; gh != NOGRP; gh = gh->g_link) 483: s++; 484: ap = (char **) salloc(s * sizeof *ap); 485: for (h = 0, p = ap; h < HSHSIZE; h++) 486: for (gh = groups[h]; gh != NOGRP; gh = gh->g_link) 487: *p++ = gh->g_name; 488: *p = NOSTR; 489: sort(ap); 490: for (p = ap; *p != NOSTR; p++) 491: printgroup(*p); 492: return(0); 493: } 494: if (argcount(argv) == 1) { 495: printgroup(*argv); 496: return(0); 497: } 498: gname = *argv; 499: h = hash(gname); 500: if ((gh = findgroup(gname)) == NOGRP) { 501: gh = (struct grouphead *) calloc(sizeof *gh, 1); 502: gh->g_name = vcopy(gname); 503: gh->g_list = NOGE; 504: gh->g_link = groups[h]; 505: groups[h] = gh; 506: } 507: 508: /* 509: * Insert names from the command list into the group. 510: * Who cares if there are duplicates? They get tossed 511: * later anyway. 512: */ 513: 514: for (ap = argv+1; *ap != NOSTR; ap++) { 515: gp = (struct group *) calloc(sizeof *gp, 1); 516: gp->ge_name = vcopy(*ap); 517: gp->ge_link = gh->g_list; 518: gh->g_list = gp; 519: } 520: return(0); 521: } 522: 523: /* 524: * Sort the passed string vecotor into ascending dictionary 525: * order. 526: */ 527: 528: sort(list) 529: char **list; 530: { 531: register char **ap; 532: int diction(); 533: 534: for (ap = list; *ap != NOSTR; ap++) 535: ; 536: if (ap-list < 2) 537: return; 538: qsort(list, ap-list, sizeof *list, diction); 539: } 540: 541: /* 542: * Do a dictionary order comparison of the arguments from 543: * qsort. 544: */ 545: 546: diction(a, b) 547: register char **a, **b; 548: { 549: return(strcmp(*a, *b)); 550: } 551: 552: /* 553: * The do nothing command for comments. 554: */ 555: 556: null(e) 557: { 558: return(0); 559: } 560: 561: /* 562: * Print out the current edit file, if we are editing. 563: * Otherwise, print the name of the person who's mail 564: * we are reading. 565: */ 566: 567: file(argv) 568: char **argv; 569: { 570: register char *cp; 571: char fname[BUFSIZ]; 572: int edit; 573: 574: if (argv[0] == NOSTR) { 575: newfileinfo(); 576: return(0); 577: } 578: 579: /* 580: * Acker's! Must switch to the new file. 581: * We use a funny interpretation -- 582: * # -- gets the previous file 583: * % -- gets the invoker's post office box 584: * %user -- gets someone else's post office box 585: * & -- gets invoker's mbox file 586: * string -- reads the given file 587: */ 588: 589: cp = getfilename(argv[0], &edit); 590: if (cp == NOSTR) 591: return(-1); 592: if (setfile(cp, edit)) { 593: perror(cp); 594: return(-1); 595: } 596: announce(0); 597: } 598: 599: /* 600: * Evaluate the string given as a new mailbox name. 601: * Ultimately, we want this to support a number of meta characters. 602: * Possibly: 603: * % -- for my system mail box 604: * %user -- for user's system mail box 605: * # -- for previous file 606: * & -- get's invoker's mbox file 607: * file name -- for any other file 608: */ 609: 610: char prevfile[PATHSIZE]; 611: 612: char * 613: getfilename(name, aedit) 614: char *name; 615: int *aedit; 616: { 617: register char *cp; 618: char savename[BUFSIZ]; 619: char oldmailname[BUFSIZ]; 620: 621: /* 622: * Assume we will be in "edit file" mode, until 623: * proven wrong. 624: */ 625: *aedit = 1; 626: switch (*name) { 627: case '%': 628: *aedit = 0; 629: strcpy(prevfile, mailname); 630: if (name[1] != 0) { 631: strcpy(savename, myname); 632: strcpy(oldmailname, mailname); 633: strncpy(myname, name+1, PATHSIZE-1); 634: myname[PATHSIZE-1] = 0; 635: findmail(); 636: cp = savestr(mailname); 637: strcpy(myname, savename); 638: strcpy(mailname, oldmailname); 639: return(cp); 640: } 641: strcpy(oldmailname, mailname); 642: findmail(); 643: cp = savestr(mailname); 644: strcpy(mailname, oldmailname); 645: return(cp); 646: 647: case '#': 648: if (name[1] != 0) 649: goto regular; 650: if (prevfile[0] == 0) { 651: printf("No previous file\n"); 652: return(NOSTR); 653: } 654: cp = savestr(prevfile); 655: strcpy(prevfile, mailname); 656: return(cp); 657: 658: case '&': 659: strcpy(prevfile, mailname); 660: if (name[1] == 0) 661: return(mbox); 662: /* Fall into . . . */ 663: 664: default: 665: regular: 666: strcpy(prevfile, mailname); 667: cp = expand(name); 668: return(cp); 669: } 670: } 671: 672: /* 673: * Expand file names like echo 674: */ 675: 676: echo(argv) 677: char **argv; 678: { 679: register char **ap; 680: register char *cp; 681: 682: for (ap = argv; *ap != NOSTR; ap++) { 683: cp = *ap; 684: if ((cp = expand(cp)) != NOSTR) 685: printf("%s ", cp); 686: } 687: return(0); 688: } 689: 690: Respond(msgvec) 691: int *msgvec; 692: { 693: if (value("Replyall") == NOSTR) 694: return (_Respond(msgvec)); 695: else 696: return (_respond(msgvec)); 697: } 698: 699: /* 700: * Reply to a series of messages by simply mailing to the senders 701: * and not messing around with the To: and Cc: lists as in normal 702: * reply. 703: */ 704: 705: _Respond(msgvec) 706: int msgvec[]; 707: { 708: struct header head; 709: struct message *mp; 710: register int i, s, *ap; 711: register char *cp, *cp2, *subject; 712: 713: for (s = 0, ap = msgvec; *ap != 0; ap++) { 714: mp = &message[*ap - 1]; 715: dot = mp; 716: if ((cp = skin(hfield("from", mp))) != NOSTR) 717: s+= strlen(cp) + 1; 718: else 719: s += strlen(skin(nameof(mp, 2))) + 1; 720: } 721: if (s == 0) 722: return(0); 723: cp = salloc(s + 2); 724: head.h_to = cp; 725: for (ap = msgvec; *ap != 0; ap++) { 726: mp = &message[*ap - 1]; 727: if ((cp2 = skin(hfield("from", mp))) == NOSTR) 728: cp2 = skin(nameof(mp, 2)); 729: cp = copy(cp2, cp); 730: *cp++ = ' '; 731: } 732: *--cp = 0; 733: mp = &message[msgvec[0] - 1]; 734: subject = hfield("subject", mp); 735: head.h_seq = 0; 736: if (subject == NOSTR) 737: subject = hfield("subj", mp); 738: head.h_subject = reedit(subject); 739: if (subject != NOSTR) 740: head.h_seq++; 741: head.h_cc = NOSTR; 742: head.h_bcc = NOSTR; 743: mail1(&head); 744: return(0); 745: } 746: 747: /* 748: * Conditional commands. These allow one to parameterize one's 749: * .mailrc and do some things if sending, others if receiving. 750: */ 751: 752: ifcmd(argv) 753: char **argv; 754: { 755: register char *cp; 756: 757: if (cond != CANY) { 758: printf("Illegal nested \"if\"\n"); 759: return(1); 760: } 761: cond = CANY; 762: cp = argv[0]; 763: switch (*cp) { 764: case 'r': case 'R': 765: cond = CRCV; 766: break; 767: 768: case 's': case 'S': 769: cond = CSEND; 770: break; 771: 772: default: 773: printf("Unrecognized if-keyword: \"%s\"\n", cp); 774: return(1); 775: } 776: return(0); 777: } 778: 779: /* 780: * Implement 'else'. This is pretty simple -- we just 781: * flip over the conditional flag. 782: */ 783: 784: elsecmd() 785: { 786: 787: switch (cond) { 788: case CANY: 789: printf("\"Else\" without matching \"if\"\n"); 790: return(1); 791: 792: case CSEND: 793: cond = CRCV; 794: break; 795: 796: case CRCV: 797: cond = CSEND; 798: break; 799: 800: default: 801: printf("Mail's idea of conditions is screwed up\n"); 802: cond = CANY; 803: break; 804: } 805: return(0); 806: } 807: 808: /* 809: * End of if statement. Just set cond back to anything. 810: */ 811: 812: endifcmd() 813: { 814: 815: if (cond == CANY) { 816: printf("\"Endif\" without matching \"if\"\n"); 817: return(1); 818: } 819: cond = CANY; 820: return(0); 821: } 822: 823: /* 824: * Set the list of alternate names. 825: */ 826: alternates(namelist) 827: char **namelist; 828: { 829: register int c; 830: register char **ap, **ap2, *cp; 831: 832: c = argcount(namelist) + 1; 833: if (c == 1) { 834: if (altnames == 0) 835: return(0); 836: for (ap = altnames; *ap; ap++) 837: printf("%s ", *ap); 838: printf("\n"); 839: return(0); 840: } 841: if (altnames != 0) 842: cfree((char *) altnames); 843: altnames = (char **) calloc(c, sizeof (char *)); 844: for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) { 845: cp = (char *) calloc(strlen(*ap) + 1, sizeof (char)); 846: strcpy(cp, *ap); 847: *ap2 = cp; 848: } 849: *ap2 = 0; 850: return(0); 851: }