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