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