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