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