1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 #include "rcv.h" 43 #include <locale.h> 44 45 /* 46 * mailx -- a modified version of a University of California at Berkeley 47 * mail program 48 * 49 * More user commands. 50 */ 51 52 static int igshow(void); 53 static int igcomp(const void *l, const void *r); 54 static int save1(char str[], int mark); 55 static int Save1(int *msgvec, int mark); 56 static void savemsglist(char *file, int *msgvec, int flag); 57 static int put1(char str[], int doign); 58 static int svputs(const char *line, FILE *obuf); 59 static int wrputs(const char *line, FILE *obuf); 60 static int retshow(void); 61 62 /* flags for savemsglist() */ 63 #define S_MARK 1 /* mark the message as saved */ 64 #define S_NOHEADER 2 /* don't write out the header */ 65 #define S_SAVING 4 /* doing save/copy */ 66 #define S_NOIGNORE 8 /* don't do ignore processing */ 67 68 /* 69 * If any arguments were given, print the first message 70 * identified by the first argument. If no arguments are given, 71 * print the next applicable message after dot. 72 */ 73 74 int 75 next(int *msgvec) 76 { 77 register struct message *mp; 78 int list[2]; 79 80 if (*msgvec != NULL) { 81 if (*msgvec < 0) { 82 printf((gettext("Negative message given\n"))); 83 return (1); 84 } 85 mp = &message[*msgvec - 1]; 86 if ((mp->m_flag & MDELETED) == 0) { 87 dot = mp; 88 goto hitit; 89 } 90 printf(gettext("No applicable message\n")); 91 return (1); 92 } 93 94 /* 95 * If this is the first command, select message 1. 96 * Note that this must exist for us to get here at all. 97 */ 98 if (!sawcom) 99 goto hitit; 100 101 /* 102 * Just find the next good message after dot, no 103 * wraparound. 104 */ 105 for (mp = dot+1; mp < &message[msgCount]; mp++) 106 if ((mp->m_flag & (MDELETED|MSAVED)) == 0) 107 break; 108 if (mp >= &message[msgCount]) { 109 printf(gettext("At EOF\n")); 110 return (0); 111 } 112 dot = mp; 113 hitit: 114 /* 115 * Print dot. 116 */ 117 list[0] = dot - &message[0] + 1; 118 list[1] = NULL; 119 return (type(list)); 120 } 121 122 /* 123 * Save a message in a file. Mark the message as saved 124 * so we can discard when the user quits. 125 */ 126 int 127 save(char str[]) 128 { 129 return (save1(str, S_MARK)); 130 } 131 132 /* 133 * Copy a message to a file without affected its saved-ness 134 */ 135 int 136 copycmd(char str[]) 137 { 138 return (save1(str, 0)); 139 } 140 141 /* 142 * Save/copy the indicated messages at the end of the passed file name. 143 * If mark is true, mark the message "saved." 144 */ 145 static int 146 save1(char str[], int mark) 147 { 148 char *file, *cmd; 149 int f, *msgvec; 150 151 cmd = mark ? "save" : "copy"; 152 msgvec = (int *)salloc((msgCount + 2) * sizeof (*msgvec)); 153 if ((file = snarf(str, &f, 0)) == NOSTR) 154 file = Getf("MBOX"); 155 if (f == -1) 156 return (1); 157 if (!f) { 158 *msgvec = first(0, MMNORM); 159 if (*msgvec == NULL) { 160 printf(gettext("No messages to %s.\n"), cmd); 161 return (1); 162 } 163 msgvec[1] = NULL; 164 } 165 if (f && getmsglist(str, msgvec, 0) < 0) 166 return (1); 167 if ((file = expand(file)) == NOSTR) 168 return (1); 169 savemsglist(file, msgvec, mark | S_SAVING); 170 return (0); 171 } 172 173 int 174 Save(int *msgvec) 175 { 176 return (Save1(msgvec, S_MARK)); 177 } 178 179 int 180 Copy(int *msgvec) 181 { 182 return (Save1(msgvec, 0)); 183 } 184 185 /* 186 * save/copy the indicated messages at the end of a file named 187 * by the sender of the first message in the msglist. 188 */ 189 static int 190 Save1(int *msgvec, int mark) 191 { 192 register char *from; 193 char recfile[BUFSIZ]; 194 195 #ifdef notdef 196 from = striphosts(nameof(&message[*msgvec-1], 0)); 197 #else 198 from = nameof(&message[*msgvec-1]); 199 #endif 200 getrecf(from, recfile, 1, sizeof (recfile)); 201 if (*recfile != '\0') 202 savemsglist(safeexpand(recfile), msgvec, mark | S_SAVING); 203 return (0); 204 } 205 206 int 207 sput(char str[]) 208 { 209 return (put1(str, 0)); 210 } 211 212 int 213 Sput(char str[]) 214 { 215 return (put1(str, S_NOIGNORE)); 216 } 217 218 /* 219 * Put the indicated messages at the end of the passed file name. 220 */ 221 static int 222 put1(char str[], int doign) 223 { 224 char *file; 225 int f, *msgvec; 226 227 msgvec = (int *)salloc((msgCount + 2) * sizeof (*msgvec)); 228 if ((file = snarf(str, &f, 0)) == NOSTR) 229 file = Getf("MBOX"); 230 if (f == -1) 231 return (1); 232 if (!f) { 233 *msgvec = first(0, MMNORM); 234 if (*msgvec == NULL) { 235 printf(gettext("No messages to put.\n")); 236 return (1); 237 } 238 msgvec[1] = NULL; 239 } 240 if (f && getmsglist(str, msgvec, 0) < 0) 241 return (1); 242 if ((file = expand(file)) == NOSTR) 243 return (1); 244 savemsglist(file, msgvec, doign); 245 return (0); 246 } 247 248 /* 249 * save a message list in a file. 250 * if wr set, doing "write" instead 251 * of "save" or "copy" so don't put 252 * out header. 253 */ 254 255 static int wr_linecount; /* count of lines written */ 256 static int wr_charcount; /* char count of lines written */ 257 static int wr_inlines; /* count of lines read */ 258 static long wr_maxlines; /* total lines in message */ 259 static int wr_inhead; /* in header of message */ 260 261 static void 262 savemsglist(char *file, int *msgvec, int flag) 263 { 264 register int *ip, mesg; 265 register struct message *mp; 266 char *disp; 267 FILE *obuf; 268 struct stat statb; 269 long lc, cc, t; 270 int bnry, mflag; 271 272 printf("\"%s\" ", file); 273 flush(); 274 if (stat(file, &statb) >= 0) 275 disp = "[Appended]"; 276 else 277 disp = "[New file]"; 278 if ((obuf = fopen(file, "a")) == NULL) { 279 perror(""); 280 return; 281 } 282 lc = cc = 0; 283 bnry = 0; 284 if (flag & S_SAVING) 285 mflag = (int)value("alwaysignore")?(M_IGNORE|M_SAVING):M_SAVING; 286 else if (flag & S_NOIGNORE) 287 mflag = 0; 288 else 289 mflag = M_IGNORE; 290 for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) { 291 mesg = *ip; 292 mp = &message[mesg-1]; 293 if (!mp->m_text) { 294 bnry = 1; 295 } 296 wr_linecount = 0; 297 wr_charcount = 0; 298 if (flag & S_NOHEADER) { 299 wr_inhead = 1; 300 wr_maxlines = mp->m_lines; 301 wr_inlines = 0; 302 t = msend(mp, obuf, 0, wrputs); 303 } else { 304 t = msend(mp, obuf, mflag, svputs); 305 } 306 if (t < 0) { 307 perror(file); 308 fclose(obuf); 309 return; 310 } 311 touch(mesg); 312 dot = mp; 313 lc += wr_linecount; 314 cc += wr_charcount; 315 if (flag & S_MARK) 316 mp->m_flag |= MSAVED; 317 } 318 fflush(obuf); 319 if (fferror(obuf)) 320 perror(file); 321 fclose(obuf); 322 if (!bnry) { 323 printf("%s %ld/%ld\n", disp, lc, cc); 324 } else { 325 printf("%s binary/%ld\n", disp, cc); 326 } 327 } 328 329 static int 330 svputs(const char *line, FILE *obuf) 331 { 332 wr_linecount++; 333 wr_charcount += strlen(line); 334 return (fputs(line, obuf)); 335 } 336 337 static int 338 wrputs(const char *line, FILE *obuf) 339 { 340 /* 341 * If this is a header line or 342 * the last line, don't write it out. Since we may add a 343 * "Status" line the line count may be off by one so insist 344 * that the last line is blank before we skip it. 345 */ 346 wr_inlines++; 347 if (wr_inhead) { 348 if (strcmp(line, "\n") == 0) 349 wr_inhead = 0; 350 return (0); 351 } 352 if (wr_inlines >= wr_maxlines && strcmp(line, "\n") == 0) 353 return (0); 354 wr_linecount++; 355 wr_charcount += strlen(line); 356 return (fputs(line, obuf)); 357 } 358 359 /* 360 * Write the indicated messages at the end of the passed 361 * file name, minus header and trailing blank line. 362 */ 363 364 int 365 swrite(char str[]) 366 { 367 register char *file; 368 int f, *msgvec; 369 370 msgvec = (int *)salloc((msgCount + 2) * sizeof (*msgvec)); 371 if ((file = snarf(str, &f, 1)) == NOSTR) 372 return (1); 373 if (f == -1) 374 return (1); 375 if ((file = expand(file)) == NOSTR) 376 return (1); 377 if (!f) { 378 *msgvec = first(0, MMNORM); 379 if (*msgvec == NULL) { 380 printf(gettext("No messages to write.\n")); 381 return (1); 382 } 383 msgvec[1] = NULL; 384 } 385 if (f && getmsglist(str, msgvec, 0) < 0) 386 return (1); 387 savemsglist(file, msgvec, S_MARK|S_NOHEADER); 388 return (0); 389 } 390 391 /* 392 * Snarf the file from the end of the command line and 393 * return a pointer to it. If there is no file attached, 394 * just return NOSTR. Put a null in front of the file 395 * name so that the message list processing won't see it, 396 * unless the file name is the only thing on the line, in 397 * which case, return 0 in the reference flag variable. 398 */ 399 400 /* 401 * The following definitions are used to characterize the syntactic 402 * category of the preceding character in the following parse procedure. 403 * The variable pc_type assumes these values. 404 */ 405 406 #define SN_DELIM 1 /* Delimiter (<blank> or line beginning) */ 407 #define SN_TOKEN 2 /* A part of a token */ 408 #define SN_QUOTE 4 /* An entire quoted string (ie, "...") */ 409 410 char * 411 snarf(char linebuf[], int *flag, int erf) 412 { 413 register char *p; /* utility pointer */ 414 register char qc; /* quotation character to match */ 415 register unsigned int pc_type; /* preceding character type */ 416 register char *tok_beg; /* beginning of last token */ 417 register char *tok_end; /* end of last token */ 418 char *line_beg; /* beginning of line, after */ 419 /* leading whitespace */ 420 421 /* 422 * Skip leading whitespace. 423 */ 424 for (line_beg = linebuf; 425 *line_beg && any(*line_beg, " \t"); 426 line_beg++) { 427 /* empty body */ 428 } 429 if (!*line_beg) { 430 if (erf) { 431 printf(gettext("No file specified\n.")); 432 } 433 *flag = 0; 434 return (NOSTR); 435 } 436 /* 437 * Process line from left-to-right, 1 char at a time. 438 */ 439 pc_type = SN_DELIM; 440 tok_beg = tok_end = NOSTR; 441 p = line_beg; 442 while (*p != '\0') { 443 if (any(*p, " \t")) { 444 /* This character is a DELIMITER */ 445 if (pc_type & (SN_TOKEN|SN_QUOTE)) { 446 tok_end = p - 1; 447 } 448 pc_type = SN_DELIM; 449 p++; 450 } else if ((qc = *p) == '"' || qc == '\'') { 451 /* This character is a QUOTE character */ 452 if (pc_type == SN_TOKEN) { 453 /* embedded quotation symbols are simply */ 454 /* token characters. */ 455 p++; 456 continue; 457 } 458 /* Search for the matching QUOTE character */ 459 for (tok_beg = p, tok_end = NOSTR, p++; 460 *p != '\0' && *p != qc; 461 p++) { 462 if (*p == '\\' && *(p+1) == qc) { 463 p++; 464 } 465 } 466 if (*p == '\0') { 467 printf(gettext("Syntax error: missing " 468 "%c.\n"), qc); 469 *flag = -1; 470 return (NOSTR); 471 } 472 tok_end = p; 473 pc_type = SN_QUOTE; 474 p++; 475 } else { 476 /* This character should be a TOKEN character */ 477 if (pc_type & (SN_DELIM|SN_TOKEN)) { 478 if (pc_type & SN_DELIM) { 479 tok_beg = p; 480 tok_end = NOSTR; 481 } 482 } else { 483 printf(gettext("improper quotes" 484 " at \"%s\".\n"), p); 485 *flag = -1; 486 return (NOSTR); 487 } 488 if (*p == '\\' && *++p == '\0') { 489 printf(gettext("\'\\\' at " 490 "end of line.\n")); 491 *flag = -1; 492 return (NOSTR); 493 } 494 pc_type = SN_TOKEN; 495 p++; 496 } 497 } 498 if (pc_type == SN_TOKEN) { 499 tok_end = p - 1; 500 } 501 if (tok_beg != NOSTR && tok_end != NOSTR) { 502 if (tok_beg == line_beg) { 503 *flag = 0; 504 } else { 505 tok_beg[-1] = '\0'; 506 *flag = 1; 507 } 508 tok_end[1] = '\0'; 509 return (tok_beg); 510 } else { 511 if (erf) { 512 printf(gettext("No file specified\n.")); 513 } 514 *flag = 0; 515 return (NOSTR); 516 } 517 } 518 519 /* 520 * Delete messages, then type the new dot. 521 */ 522 523 int 524 deltype(int msgvec[]) 525 { 526 int list[2]; 527 int lastdot; 528 529 lastdot = dot - &message[0] + 1; 530 if (delm(msgvec) >= 0) { 531 list[0] = dot - &message[0]; 532 list[0]++; 533 if (list[0] > lastdot) { 534 touch(list[0]); 535 list[1] = NULL; 536 return (type(list)); 537 } 538 printf(gettext("At EOF\n")); 539 return (0); 540 } else { 541 printf(gettext("No more messages\n")); 542 return (0); 543 } 544 } 545 546 /* 547 * Delete the indicated messages. 548 * Set dot to some nice place afterwards. 549 */ 550 int 551 delm(int *msgvec) 552 { 553 register struct message *mp; 554 int *ip, mesg; 555 int last; 556 557 last = NULL; 558 for (ip = msgvec; *ip != NULL; ip++) { 559 mesg = *ip; 560 touch(mesg); 561 mp = &message[mesg-1]; 562 mp->m_flag |= MDELETED|MTOUCH; 563 mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX); 564 last = mesg; 565 } 566 if (last != NULL) { 567 dot = &message[last-1]; 568 last = first(0, MDELETED); 569 if (last != NULL) { 570 dot = &message[last-1]; 571 return (0); 572 } else { 573 dot = &message[0]; 574 return (-1); 575 } 576 } 577 578 /* 579 * Following can't happen -- it keeps lint happy 580 */ 581 582 return (-1); 583 } 584 585 /* 586 * Undelete the indicated messages. 587 */ 588 int 589 undelete(int *msgvec) 590 { 591 register struct message *mp; 592 int *ip, mesg; 593 594 for (ip = msgvec; ip-msgvec < msgCount; ip++) { 595 mesg = *ip; 596 if (mesg == 0) 597 return (0); 598 touch(mesg); 599 mp = &message[mesg-1]; 600 dot = mp; 601 mp->m_flag &= ~MDELETED; 602 } 603 return (0); 604 } 605 606 /* 607 * Add the given header fields to the retained list. 608 * If no arguments, print the current list of retained fields. 609 */ 610 int 611 retfield(char *list[]) 612 { 613 char field[BUFSIZ]; 614 register int h; 615 register struct ignore *igp; 616 char **ap; 617 618 if (argcount(list) == 0) 619 return (retshow()); 620 for (ap = list; *ap != 0; ap++) { 621 istrcpy(field, sizeof (field), *ap); 622 623 if (member(field, retain)) 624 continue; 625 626 h = hash(field); 627 if ((igp = (struct ignore *) 628 calloc(1, sizeof (struct ignore))) == NULL) { 629 panic("Couldn't allocate memory"); 630 } 631 if ((igp->i_field = (char *) 632 calloc(strlen(field) + 1, sizeof (char))) == NULL) { 633 panic("Couldn't allocate memory"); 634 } 635 strcpy(igp->i_field, field); 636 igp->i_link = retain[h]; 637 retain[h] = igp; 638 nretained++; 639 } 640 return (0); 641 } 642 643 /* 644 * Print out all currently retained fields. 645 */ 646 static int 647 retshow(void) 648 { 649 register int h, count; 650 struct ignore *igp; 651 char **ap, **ring; 652 653 count = 0; 654 for (h = 0; h < HSHSIZE; h++) 655 for (igp = retain[h]; igp != 0; igp = igp->i_link) 656 count++; 657 if (count == 0) { 658 printf(gettext("No fields currently being retained.\n")); 659 return (0); 660 } 661 ring = (char **)salloc((count + 1) * sizeof (char *)); 662 ap = ring; 663 for (h = 0; h < HSHSIZE; h++) 664 for (igp = retain[h]; igp != 0; igp = igp->i_link) 665 *ap++ = igp->i_field; 666 *ap = 0; 667 qsort(ring, count, sizeof (char *), igcomp); 668 for (ap = ring; *ap != 0; ap++) 669 printf("%s\n", *ap); 670 return (0); 671 } 672 673 /* 674 * Remove a list of fields from the retain list. 675 */ 676 int 677 unretfield(char *list[]) 678 { 679 char **ap, field[BUFSIZ]; 680 register int h, count = 0; 681 register struct ignore *ig1, *ig2; 682 683 if (argcount(list) == 0) { 684 for (h = 0; h < HSHSIZE; h++) { 685 ig1 = retain[h]; 686 while (ig1) { 687 free(ig1->i_field); 688 ig2 = ig1->i_link; 689 free((char *)ig1); 690 ig1 = ig2; 691 count++; 692 } 693 retain[h] = NULL; 694 } 695 if (count == 0) 696 printf(gettext( 697 "No fields currently being retained.\n")); 698 nretained = 0; 699 return (0); 700 } 701 for (ap = list; *ap; ap++) { 702 istrcpy(field, sizeof (field), *ap); 703 h = hash(field); 704 for (ig1 = retain[h]; ig1; ig2 = ig1, ig1 = ig1->i_link) 705 if (strcmp(ig1->i_field, field) == 0) { 706 if (ig1 == retain[h]) 707 retain[h] = ig1->i_link; 708 else 709 ig2->i_link = ig1->i_link; 710 free(ig1->i_field); 711 free((char *)ig1); 712 nretained--; 713 break; 714 } 715 } 716 return (0); 717 } 718 719 /* 720 * Add the given header fields to the ignored list. 721 * If no arguments, print the current list of ignored fields. 722 */ 723 int 724 igfield(char *list[]) 725 { 726 char field[BUFSIZ]; 727 register int h; 728 register struct ignore *igp; 729 char **ap; 730 731 if (argcount(list) == 0) 732 return (igshow()); 733 for (ap = list; *ap != 0; ap++) { 734 if (isign(*ap, 0)) 735 continue; 736 istrcpy(field, sizeof (field), *ap); 737 h = hash(field); 738 if ((igp = (struct ignore *) 739 calloc(1, sizeof (struct ignore))) == NULL) { 740 panic("Couldn't allocate memory"); 741 } 742 if ((igp->i_field = (char *) 743 calloc((unsigned)strlen(field) + 1, 744 sizeof (char))) == NULL) { 745 panic("Couldn't allocate memory"); 746 } 747 strcpy(igp->i_field, field); 748 igp->i_link = ignore[h]; 749 ignore[h] = igp; 750 } 751 return (0); 752 } 753 754 /* 755 * Print out all currently ignored fields. 756 */ 757 static int 758 igshow(void) 759 { 760 register int h, count; 761 struct ignore *igp; 762 char **ap, **ring; 763 764 count = 0; 765 for (h = 0; h < HSHSIZE; h++) 766 for (igp = ignore[h]; igp != 0; igp = igp->i_link) 767 count++; 768 if (count == 0) { 769 printf(gettext("No fields currently being ignored.\n")); 770 return (0); 771 } 772 ring = (char **)salloc((count + 1) * sizeof (char *)); 773 ap = ring; 774 for (h = 0; h < HSHSIZE; h++) 775 for (igp = ignore[h]; igp != 0; igp = igp->i_link) 776 *ap++ = igp->i_field; 777 *ap = 0; 778 qsort((char *)ring, (unsigned)count, sizeof (char *), igcomp); 779 for (ap = ring; *ap != 0; ap++) 780 printf("%s\n", *ap); 781 return (0); 782 } 783 784 /* 785 * Compare two names for sorting ignored field list. 786 */ 787 static int 788 igcomp(const void *l, const void *r) 789 { 790 return (strcmp(*(char **)l, *(char **)r)); 791 } 792 793 /* 794 * Remove a list of fields from the ignore list. 795 */ 796 int 797 unigfield(char *list[]) 798 { 799 char **ap, field[BUFSIZ]; 800 register int h, count = 0; 801 register struct ignore *ig1, *ig2; 802 803 if (argcount(list) == 0) { 804 for (h = 0; h < HSHSIZE; h++) { 805 ig1 = ignore[h]; 806 while (ig1) { 807 free(ig1->i_field); 808 ig2 = ig1->i_link; 809 free((char *)ig1); 810 ig1 = ig2; 811 count++; 812 } 813 ignore[h] = NULL; 814 } 815 if (count == 0) 816 printf(gettext("No fields currently being ignored.\n")); 817 return (0); 818 } 819 for (ap = list; *ap; ap++) { 820 istrcpy(field, sizeof (field), *ap); 821 h = hash(field); 822 for (ig1 = ignore[h]; ig1; ig2 = ig1, ig1 = ig1->i_link) 823 if (strcmp(ig1->i_field, field) == 0) { 824 if (ig1 == ignore[h]) 825 ignore[h] = ig1->i_link; 826 else 827 ig2->i_link = ig1->i_link; 828 free(ig1->i_field); 829 free((char *)ig1); 830 break; 831 } 832 } 833 return (0); 834 } 835