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