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