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