cmd3.c revision 1.26
1/* $NetBSD: cmd3.c,v 1.26 2005/07/19 01:38:38 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.26 2005/07/19 01:38:38 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 diction(const void *, const void *); 50 51/* 52 * Process a shell escape by saving signals, ignoring signals, 53 * and forking a sh -c 54 */ 55int 56shell(void *v) 57{ 58 char *str = v; 59 sig_t sigint = signal(SIGINT, SIG_IGN); 60 const char *shellcmd; 61 char cmd[BUFSIZ]; 62 63 (void)strcpy(cmd, str); 64 if (bangexp(cmd) < 0) 65 return 1; 66 if ((shellcmd = value("SHELL")) == NULL) 67 shellcmd = _PATH_CSHELL; 68 (void)run_command(shellcmd, 0, 0, 1, "-c", cmd, NULL); 69 (void)signal(SIGINT, sigint); 70 printf("!\n"); 71 return 0; 72} 73 74/* 75 * Fork an interactive shell. 76 */ 77/*ARGSUSED*/ 78int 79dosh(void *v) 80{ 81 sig_t sigint = signal(SIGINT, SIG_IGN); 82 const char *shellcmd; 83 84 if ((shellcmd = value("SHELL")) == NULL) 85 shellcmd = _PATH_CSHELL; 86 (void)run_command(shellcmd, 0, 0, 1, NULL); 87 (void)signal(SIGINT, sigint); 88 putchar('\n'); 89 return 0; 90} 91 92/* 93 * Expand the shell escape by expanding unescaped !'s into the 94 * last issued command where possible. 95 */ 96 97char lastbang[128]; 98 99int 100bangexp(char *str) 101{ 102 char bangbuf[BUFSIZ]; 103 char *cp, *cp2; 104 int n; 105 int changed = 0; 106 107 cp = str; 108 cp2 = bangbuf; 109 n = BUFSIZ; 110 while (*cp) { 111 if (*cp == '!') { 112 if (n < strlen(lastbang)) { 113overf: 114 printf("Command buffer overflow\n"); 115 return(-1); 116 } 117 changed++; 118 strcpy(cp2, lastbang); 119 cp2 += strlen(lastbang); 120 n -= strlen(lastbang); 121 cp++; 122 continue; 123 } 124 if (*cp == '\\' && cp[1] == '!') { 125 if (--n <= 1) 126 goto overf; 127 *cp2++ = '!'; 128 cp += 2; 129 changed++; 130 } 131 if (--n <= 1) 132 goto overf; 133 *cp2++ = *cp++; 134 } 135 *cp2 = 0; 136 if (changed) { 137 printf("!%s\n", bangbuf); 138 fflush(stdout); 139 } 140 strcpy(str, bangbuf); 141 strncpy(lastbang, bangbuf, 128); 142 lastbang[127] = 0; 143 return(0); 144} 145 146/* 147 * Print out a nice help message from some file or another. 148 */ 149 150int 151help(void *v) 152{ 153 int c; 154 FILE *f; 155 156 if ((f = Fopen(_PATH_HELP, "r")) == NULL) { 157 warn(_PATH_HELP); 158 return(1); 159 } 160 while ((c = getc(f)) != EOF) 161 putchar(c); 162 Fclose(f); 163 return(0); 164} 165 166/* 167 * Change user's working directory. 168 */ 169int 170schdir(void *v) 171{ 172 char **arglist = v; 173 const char *cp; 174 175 if (*arglist == NULL) 176 cp = homedir; 177 else 178 if ((cp = expand(*arglist)) == NULL) 179 return(1); 180 if (chdir(cp) < 0) { 181 warn("%s", cp); 182 return(1); 183 } 184 return 0; 185} 186 187int 188respond(void *v) 189{ 190 int *msgvec = v; 191 if (value("Replyall") == NULL) 192 return (_respond(msgvec)); 193 else 194 return (_Respond(msgvec)); 195} 196 197/* 198 * Reply to a list of messages. Extract each name from the 199 * message header and send them off to mail1() 200 */ 201int 202_respond(int *msgvec) 203{ 204 struct message *mp; 205 char *cp, *rcv, *replyto; 206 char **ap; 207 struct name *np; 208 struct header head; 209 210 if (msgvec[1] != 0) { 211 printf("Sorry, can't reply to multiple messages at once\n"); 212 return(1); 213 } 214 mp = &message[msgvec[0] - 1]; 215 touch(mp); 216 dot = mp; 217 if ((rcv = skin(hfield("from", mp))) == NULL) 218 rcv = skin(nameof(mp, 1)); 219 if ((replyto = skin(hfield("reply-to", mp))) != NULL) 220 np = extract(replyto, GTO); 221 else if ((cp = skin(hfield("to", mp))) != NULL) 222 np = extract(cp, GTO); 223 else 224 np = NULL; 225 np = elide(np); 226 /* 227 * Delete my name from the reply list, 228 * and with it, all my alternate names. 229 */ 230 np = delname(np, myname); 231 if (altnames) 232 for (ap = altnames; *ap; ap++) 233 np = delname(np, *ap); 234 if (np != NULL && replyto == NULL) 235 np = cat(np, extract(rcv, GTO)); 236 else if (np == NULL) { 237 if (replyto != NULL) 238 printf("Empty reply-to field -- replying to author\n"); 239 np = extract(rcv, GTO); 240 } 241 head.h_to = np; 242 if ((head.h_subject = hfield("subject", mp)) == NULL) 243 head.h_subject = hfield("subj", mp); 244 head.h_subject = reedit(head.h_subject); 245 if (replyto == NULL && (cp = skin(hfield("cc", mp))) != NULL) { 246 np = elide(extract(cp, GCC)); 247 np = delname(np, myname); 248 if (altnames != 0) 249 for (ap = altnames; *ap; ap++) 250 np = delname(np, *ap); 251 head.h_cc = np; 252 } else 253 head.h_cc = NULL; 254 head.h_bcc = NULL; 255 head.h_smopts = NULL; 256 mail1(&head, 1); 257 return(0); 258} 259 260/* 261 * Modify the subject we are replying to to begin with Re: if 262 * it does not already. 263 */ 264char * 265reedit(char *subj) 266{ 267 char *newsubj; 268 269 if (subj == NULL) 270 return NULL; 271 if ((subj[0] == 'r' || subj[0] == 'R') && 272 (subj[1] == 'e' || subj[1] == 'E') && 273 subj[2] == ':') 274 return subj; 275 newsubj = salloc(strlen(subj) + 5); 276 strcpy(newsubj, "Re: "); 277 strcpy(newsubj + 4, subj); 278 return newsubj; 279} 280 281/* 282 * Preserve the named messages, so that they will be sent 283 * back to the system mailbox. 284 */ 285int 286preserve(void *v) 287{ 288 int *msgvec = v; 289 struct message *mp; 290 int *ip, mesg; 291 292 if (edit) { 293 printf("Cannot \"preserve\" in edit mode\n"); 294 return(1); 295 } 296 for (ip = msgvec; *ip != 0; ip++) { 297 mesg = *ip; 298 mp = &message[mesg-1]; 299 mp->m_flag |= MPRESERVE; 300 mp->m_flag &= ~MBOX; 301 dot = mp; 302 } 303 return(0); 304} 305 306/* 307 * Mark all given messages as unread. 308 */ 309int 310unread(void *v) 311{ 312 int *msgvec = v; 313 int *ip; 314 315 for (ip = msgvec; *ip != 0; ip++) { 316 dot = &message[*ip-1]; 317 dot->m_flag &= ~(MREAD|MTOUCH); 318 dot->m_flag |= MSTATUS; 319 } 320 return(0); 321} 322 323/* 324 * Print the size of each message. 325 */ 326int 327messize(void *v) 328{ 329 int *msgvec = v; 330 struct message *mp; 331 int *ip, mesg; 332 333 for (ip = msgvec; *ip != 0; ip++) { 334 mesg = *ip; 335 mp = &message[mesg-1]; 336 printf("%d: %ld/%ld\n", mesg, mp->m_blines, mp->m_size); 337 } 338 return(0); 339} 340 341/* 342 * Quit quickly. If we are sourcing, just pop the input level 343 * by returning an error. 344 */ 345int 346rexit(void *v) 347{ 348 if (sourcing) 349 return(1); 350 exit(0); 351 /*NOTREACHED*/ 352} 353 354/* 355 * Set or display a variable value. Syntax is similar to that 356 * of csh. 357 */ 358int 359set(void *v) 360{ 361 char **arglist = v; 362 struct var *vp; 363 const char *cp; 364 char varbuf[BUFSIZ], **ap, **p; 365 int errs, h, s; 366 size_t l; 367 368 if (*arglist == NULL) { 369 for (h = 0, s = 1; h < HSHSIZE; h++) 370 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 371 s++; 372 ap = salloc(s * sizeof *ap); 373 for (h = 0, p = ap; h < HSHSIZE; h++) 374 for (vp = variables[h]; vp != NULL; vp = vp->v_link) 375 *p++ = vp->v_name; 376 *p = NULL; 377 sort(ap); 378 for (p = ap; *p != NULL; p++) 379 printf("%s\t%s\n", *p, value(*p)); 380 return(0); 381 } 382 errs = 0; 383 for (ap = arglist; *ap != NULL; ap++) { 384 cp = *ap; 385 while (*cp != '=' && *cp != '\0') 386 ++cp; 387 l = cp - *ap; 388 if (l >= sizeof varbuf) 389 l = sizeof varbuf - 1; 390 strncpy(varbuf, *ap, l); 391 varbuf[l] = '\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 != NULL; ap++) { 419 if ((vp2 = lookup(*ap)) == NULL) { 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 == NULL) { 460 for (h = 0, s = 1; h < HSHSIZE; h++) 461 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 462 s++; 463 ap = salloc(s * sizeof *ap); 464 for (h = 0, p = ap; h < HSHSIZE; h++) 465 for (gh = groups[h]; gh != NULL; gh = gh->g_link) 466 *p++ = gh->g_name; 467 *p = NULL; 468 sort(ap); 469 for (p = ap; *p != NULL; p++) 470 printgroup(*p); 471 return(0); 472 } 473 if (argv[1] == NULL) { 474 printgroup(*argv); 475 return(0); 476 } 477 gname = *argv; 478 h = hash(gname); 479 if ((gh = findgroup(gname)) == NULL) { 480 gh = (struct grouphead *) calloc(1, sizeof *gh); 481 gh->g_name = vcopy(gname); 482 gh->g_list = NULL; 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 != NULL; ap++) { 494 gp = (struct group *) calloc(1, sizeof *gp); 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 != NULL; 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(*(const char *const *)a, *(const char *const *)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] == NULL) { 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 const char *cp; 567 568 for (ap = argv; *ap != NULL; ap++) { 569 cp = *ap; 570 if ((cp = expand(cp)) != NULL) { 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") == NULL) 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 = NULL; 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))) == NULL) 609 cp = skin(nameof(mp, 2)); 610 head.h_to = cat(head.h_to, extract(cp, GTO)); 611 } 612 if (head.h_to == NULL) 613 return 0; 614 mp = &message[msgvec[0] - 1]; 615 if ((head.h_subject = hfield("subject", mp)) == NULL) 616 head.h_subject = hfield("subj", mp); 617 head.h_subject = reedit(head.h_subject); 618 head.h_cc = NULL; 619 head.h_bcc = NULL; 620 head.h_smopts = NULL; 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