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