cmd3.c revision 1.1
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 35static char sccsid[] = "@(#)cmd3.c 5.24 (Berkeley) 6/25/90"; 36#endif /* not lint */ 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 */ 50shell(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, 0, -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*/ 72dosh(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, 0, -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 91char lastbang[128]; 92 93bangexp(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)) { 107overf: 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 144help() 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 */ 162schdir(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 179respond(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 */ 255char * 256reedit(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 278preserve(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 */ 301unread(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 318messize(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 337rexit(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 350set(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 397unset(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 435group(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 492sort(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 510diction(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*/ 521null(e) 522{ 523 return 0; 524} 525 526/* 527 * Change to another file. With no argument, print information about 528 * the current file. 529 */ 530file(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 */ 547echo(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 565Respond(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 614ifcmd(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 646elsecmd() 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 674endifcmd() 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 */ 688alternates(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} 714