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