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