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 #pragma ident "%Z%%M% %I% %E% SMI" 40 41 #include "rcv.h" 42 #include <locale.h> 43 #include <stdlib.h> 44 #include <string.h> 45 46 /* 47 * mailx -- a modified version of a University of California at Berkeley 48 * mail program 49 * 50 * Message list handling. 51 */ 52 53 static int check(int mesg, int f); 54 static int evalcol(int col); 55 static int isinteger(char *buf); 56 static void mark(int mesg); 57 static int markall(char buf[], int f); 58 static int matchsubj(char *str, int mesg); 59 static int metamess(int meta, int f); 60 static void regret(int token); 61 static int scan(char **sp); 62 static void scaninit(void); 63 static int sender(char *str, int mesg); 64 static void unmark(int mesg); 65 66 /* 67 * Process message operand list. 68 * Convert the user string of message numbers and 69 * store the numbers into vector. 70 * 71 * Returns the count of messages picked up or -1 on error. 72 */ 73 int 74 getmessage(char *buf, int *vector, int flags) 75 { 76 register int *ip; 77 register struct message *mp; 78 int firstmsg = -1; 79 char delims[] = "\t- "; 80 char *result = NULL; 81 82 if (markall(buf, flags) < 0) 83 return (-1); 84 ip = vector; 85 86 /* 87 * Check for first message number and make sure it is 88 * at the beginning of the vector. 89 */ 90 result = strtok(buf, delims); 91 if (result != NULL && isinteger(result)) { 92 firstmsg = atoi(result); 93 *ip++ = firstmsg; 94 } 95 96 /* 97 * Add marked messages to vector and skip first 98 * message number because it is already at the 99 * beginning of the vector 100 */ 101 for (mp = &message[0]; mp < &message[msgCount]; mp++) { 102 if (firstmsg == mp - &message[0] + 1) 103 continue; 104 if (mp->m_flag & MMARK) 105 *ip++ = mp - &message[0] + 1; 106 } 107 *ip = NULL; 108 return (ip - vector); 109 } 110 111 /* 112 * Check to see if string is an integer 113 * 114 * Returns 1 if is an integer and 0 if it is not 115 */ 116 static int 117 isinteger(char *buf) 118 { 119 int i, result = 1; 120 121 /* check for empty string */ 122 if (strcmp(buf, "") == 0) { 123 result = 0; 124 return (result); 125 } 126 127 i = 0; 128 while (buf[i] != '\0') { 129 if (!isdigit(buf[i])) { 130 result = 0; 131 break; 132 } 133 i++; 134 } 135 return (result); 136 } 137 138 /* 139 * Process msglist operand list. 140 * Convert the user string of message numbers and 141 * store the numbers into vector. 142 * 143 * Returns the count of messages picked up or -1 on error. 144 */ 145 146 int 147 getmsglist(char *buf, int *vector, int flags) 148 { 149 register int *ip; 150 register struct message *mp; 151 152 if (markall(buf, flags) < 0) 153 return (-1); 154 ip = vector; 155 for (mp = &message[0]; mp < &message[msgCount]; mp++) 156 if (mp->m_flag & MMARK) 157 *ip++ = mp - &message[0] + 1; 158 *ip = NULL; 159 return (ip - vector); 160 } 161 162 163 /* 164 * Mark all messages that the user wanted from the command 165 * line in the message structure. Return 0 on success, -1 166 * on error. 167 */ 168 169 /* 170 * Bit values for colon modifiers. 171 */ 172 173 #define CMNEW 01 /* New messages */ 174 #define CMOLD 02 /* Old messages */ 175 #define CMUNREAD 04 /* Unread messages */ 176 #define CMDELETED 010 /* Deleted messages */ 177 #define CMREAD 020 /* Read messages */ 178 179 /* 180 * The following table describes the letters which can follow 181 * the colon and gives the corresponding modifier bit. 182 */ 183 184 static struct coltab { 185 char co_char; /* What to find past : */ 186 int co_bit; /* Associated modifier bit */ 187 int co_mask; /* m_status bits to mask */ 188 int co_equal; /* ... must equal this */ 189 } coltab[] = { 190 'n', CMNEW, MNEW, MNEW, 191 'o', CMOLD, MNEW, 0, 192 'u', CMUNREAD, MREAD, 0, 193 'd', CMDELETED, MDELETED, MDELETED, 194 'r', CMREAD, MREAD, MREAD, 195 0, 0, 0, 0 196 }; 197 198 static int lastcolmod; 199 200 static int 201 markall(char buf[], int f) 202 { 203 register char **np; 204 register int i; 205 register struct message *mp; 206 char *namelist[NMLSIZE], *bufp; 207 int tok, beg, mc, star, other, colmod, colresult; 208 209 colmod = 0; 210 for (i = 1; i <= msgCount; i++) 211 unmark(i); 212 bufp = buf; 213 mc = 0; 214 np = &namelist[0]; 215 scaninit(); 216 tok = scan(&bufp); 217 star = 0; 218 other = 0; 219 beg = 0; 220 while (tok != TEOL) { 221 switch (tok) { 222 case TNUMBER: 223 number: 224 if (star) { 225 printf(gettext("No numbers mixed with *\n")); 226 return (-1); 227 } 228 mc++; 229 other++; 230 if (beg != 0) { 231 if (check(lexnumber, f)) 232 return (-1); 233 for (i = beg; i <= lexnumber; i++) 234 if ((message[i-1].m_flag&MDELETED) == f) 235 mark(i); 236 beg = 0; 237 break; 238 } 239 beg = lexnumber; 240 if (check(beg, f)) 241 return (-1); 242 tok = scan(&bufp); 243 if (tok != TDASH) { 244 regret(tok); 245 mark(beg); 246 beg = 0; 247 } 248 break; 249 250 case TSTRING: 251 if (beg != 0) { 252 printf(gettext( 253 "Non-numeric second argument\n")); 254 return (-1); 255 } 256 other++; 257 if (lexstring[0] == ':') { 258 colresult = evalcol(lexstring[1]); 259 if (colresult == 0) { 260 printf(gettext( 261 "Unknown colon modifier \"%s\"\n"), 262 lexstring); 263 return (-1); 264 } 265 colmod |= colresult; 266 } 267 else 268 *np++ = savestr(lexstring); 269 break; 270 271 case TDASH: 272 case TPLUS: 273 case TDOLLAR: 274 case TUP: 275 case TDOT: 276 lexnumber = metamess(lexstring[0], f); 277 if (lexnumber == -1) 278 return (-1); 279 goto number; 280 281 case TSTAR: 282 if (other) { 283 printf(gettext( 284 "Can't mix \"*\" with anything\n")); 285 return (-1); 286 } 287 star++; 288 break; 289 } 290 tok = scan(&bufp); 291 } 292 lastcolmod = colmod; 293 *np = NOSTR; 294 mc = 0; 295 if (star) { 296 for (i = 0; i < msgCount; i++) 297 if ((message[i].m_flag & MDELETED) == f) { 298 mark(i+1); 299 mc++; 300 } 301 if (mc == 0) { 302 printf(gettext("No applicable messages\n")); 303 return (-1); 304 } 305 return (0); 306 } 307 308 /* 309 * If no numbers were given, mark all of the messages, 310 * so that we can unmark any whose sender was not selected 311 * if any user names were given. 312 */ 313 314 if ((np > namelist || colmod != 0) && mc == 0) 315 for (i = 1; i <= msgCount; i++) 316 if ((message[i-1].m_flag & MDELETED) == f) 317 mark(i); 318 319 /* 320 * If any names were given, go through and eliminate any 321 * messages whose senders were not requested. 322 */ 323 324 if (np > namelist) { 325 for (i = 1; i <= msgCount; i++) { 326 for (mc = 0, np = &namelist[0]; *np != NOSTR; np++) 327 if (**np == '/') { 328 if (matchsubj(*np, i)) { 329 mc++; 330 break; 331 } 332 } else { 333 if (sender(*np, i)) { 334 mc++; 335 break; 336 } 337 } 338 if (mc == 0) 339 unmark(i); 340 } 341 342 /* 343 * Make sure we got some decent messages. 344 */ 345 346 mc = 0; 347 for (i = 1; i <= msgCount; i++) 348 if (message[i-1].m_flag & MMARK) { 349 mc++; 350 break; 351 } 352 if (mc == 0) { 353 printf(gettext("No applicable messages from {%s"), 354 namelist[0]); 355 for (np = &namelist[1]; *np != NOSTR; np++) 356 printf(", %s", *np); 357 printf("}\n"); 358 return (-1); 359 } 360 } 361 362 /* 363 * If any colon modifiers were given, go through and 364 * unmark any messages which do not satisfy the modifiers. 365 */ 366 367 if (colmod != 0) { 368 for (i = 1; i <= msgCount; i++) { 369 register struct coltab *colp; 370 371 mp = &message[i - 1]; 372 for (colp = &coltab[0]; colp->co_char; colp++) 373 if (colp->co_bit & colmod) 374 if ((mp->m_flag & colp->co_mask) 375 != colp->co_equal) 376 unmark(i); 377 378 } 379 for (mp = &message[0]; mp < &message[msgCount]; mp++) 380 if (mp->m_flag & MMARK) 381 break; 382 if (mp >= &message[msgCount]) { 383 register struct coltab *colp; 384 385 printf(gettext("No messages satisfy")); 386 for (colp = &coltab[0]; colp->co_char; colp++) 387 if (colp->co_bit & colmod) 388 printf(" :%c", colp->co_char); 389 printf("\n"); 390 return (-1); 391 } 392 } 393 return (0); 394 } 395 396 /* 397 * Turn the character after a colon modifier into a bit 398 * value. 399 */ 400 static int 401 evalcol(int col) 402 { 403 register struct coltab *colp; 404 405 if (col == 0) 406 return (lastcolmod); 407 for (colp = &coltab[0]; colp->co_char; colp++) 408 if (colp->co_char == col) 409 return (colp->co_bit); 410 return (0); 411 } 412 413 /* 414 * Check the passed message number for legality and proper flags. 415 */ 416 static int 417 check(int mesg, int f) 418 { 419 register struct message *mp; 420 421 if (mesg < 1 || mesg > msgCount) { 422 printf(gettext("%d: Invalid message number\n"), mesg); 423 return (-1); 424 } 425 mp = &message[mesg-1]; 426 if ((mp->m_flag & MDELETED) != f) { 427 printf(gettext("%d: Inappropriate message\n"), mesg); 428 return (-1); 429 } 430 return (0); 431 } 432 433 /* 434 * Scan out the list of string arguments, shell style 435 * for a RAWLIST. 436 */ 437 438 int 439 getrawlist(char line[], char **argv, int argc) 440 { 441 register char **ap, *cp, *cp2; 442 char linebuf[LINESIZE], quotec; 443 register char **last; 444 445 ap = argv; 446 cp = line; 447 last = argv + argc - 1; 448 while (*cp != '\0') { 449 while (any(*cp, " \t")) 450 cp++; 451 cp2 = linebuf; 452 quotec = 0; 453 while (*cp != '\0') { 454 if (quotec) { 455 if (*cp == quotec) { 456 quotec = 0; 457 cp++; 458 } else 459 *cp2++ = *cp++; 460 } else { 461 if (*cp == '\\') { 462 if (*(cp+1) != '\0') { 463 *cp2++ = *++cp; 464 cp++; 465 } else { 466 printf(gettext( 467 "Trailing \\; ignoring\n")); 468 break; 469 } 470 } 471 if (any(*cp, " \t")) 472 break; 473 if (any(*cp, "'\"")) 474 quotec = *cp++; 475 else 476 *cp2++ = *cp++; 477 } 478 } 479 *cp2 = '\0'; 480 if (cp2 == linebuf) 481 break; 482 if (ap >= last) { 483 printf(gettext("Too many elements in the list;" 484 " excess discarded\n")); 485 break; 486 } 487 *ap++ = savestr(linebuf); 488 } 489 *ap = NOSTR; 490 return (ap-argv); 491 } 492 493 /* 494 * scan out a single lexical item and return its token number, 495 * updating the string pointer passed **p. Also, store the value 496 * of the number or string scanned in lexnumber or lexstring as 497 * appropriate. In any event, store the scanned `thing' in lexstring. 498 */ 499 500 static struct lex { 501 char l_char; 502 char l_token; 503 } singles[] = { 504 '$', TDOLLAR, 505 '.', TDOT, 506 '^', TUP, 507 '*', TSTAR, 508 '-', TDASH, 509 '+', TPLUS, 510 '(', TOPEN, 511 ')', TCLOSE, 512 0, 0 513 }; 514 515 static int 516 scan(char **sp) 517 { 518 register char *cp, *cp2; 519 register char c; 520 register struct lex *lp; 521 int quotec; 522 523 if (regretp >= 0) { 524 copy(stringstack[regretp], lexstring); 525 lexnumber = numberstack[regretp]; 526 return (regretstack[regretp--]); 527 } 528 cp = *sp; 529 cp2 = lexstring; 530 c = *cp++; 531 532 /* 533 * strip away leading white space. 534 */ 535 536 while (any(c, " \t")) 537 c = *cp++; 538 539 /* 540 * If no characters remain, we are at end of line, 541 * so report that. 542 */ 543 544 if (c == '\0') { 545 *sp = --cp; 546 return (TEOL); 547 } 548 549 /* 550 * If the leading character is a digit, scan 551 * the number and convert it on the fly. 552 * Return TNUMBER when done. 553 */ 554 555 if (isdigit(c)) { 556 lexnumber = 0; 557 while (isdigit(c)) { 558 lexnumber = lexnumber*10 + c - '0'; 559 *cp2++ = c; 560 c = *cp++; 561 } 562 *cp2 = '\0'; 563 *sp = --cp; 564 return (TNUMBER); 565 } 566 567 /* 568 * Check for single character tokens; return such 569 * if found. 570 */ 571 572 for (lp = &singles[0]; lp->l_char != 0; lp++) 573 if (c == lp->l_char) { 574 lexstring[0] = c; 575 lexstring[1] = '\0'; 576 *sp = cp; 577 return (lp->l_token); 578 } 579 580 /* 581 * We've got a string! Copy all the characters 582 * of the string into lexstring, until we see 583 * a null, space, or tab. 584 * If the lead character is a " or ', save it 585 * and scan until you get another. 586 */ 587 588 quotec = 0; 589 if (any(c, "'\"")) { 590 quotec = c; 591 c = *cp++; 592 } 593 while (c != '\0') { 594 if (quotec == 0 && c == '\\') { 595 if (*cp != '\0') { 596 c = *cp++; 597 } else { 598 fprintf(stderr, gettext("Trailing \\; " 599 "ignoring\n")); 600 } 601 } 602 if (c == quotec) { 603 cp++; 604 break; 605 } 606 if (quotec == 0 && any(c, " \t")) 607 break; 608 if (cp2 - lexstring < STRINGLEN-1) 609 *cp2++ = c; 610 c = *cp++; 611 } 612 if (quotec && c == 0) 613 fprintf(stderr, gettext("Missing %c\n"), quotec); 614 *sp = --cp; 615 *cp2 = '\0'; 616 return (TSTRING); 617 } 618 619 /* 620 * Unscan the named token by pushing it onto the regret stack. 621 */ 622 623 static void 624 regret(int token) 625 { 626 if (++regretp >= REGDEP) 627 panic("Too many regrets"); 628 regretstack[regretp] = token; 629 lexstring[STRINGLEN-1] = '\0'; 630 stringstack[regretp] = savestr(lexstring); 631 numberstack[regretp] = lexnumber; 632 } 633 634 /* 635 * Reset all the scanner global variables. 636 */ 637 638 static void 639 scaninit(void) 640 { 641 regretp = -1; 642 } 643 644 /* 645 * Find the first message whose flags & m == f and return 646 * its message number. 647 */ 648 649 int 650 first(int f, int m) 651 { 652 register int mesg; 653 register struct message *mp; 654 655 mesg = dot - &message[0] + 1; 656 f &= MDELETED; 657 m &= MDELETED; 658 for (mp = dot; mp < &message[msgCount]; mp++) { 659 if ((mp->m_flag & m) == f) 660 return (mesg); 661 mesg++; 662 } 663 mesg = dot - &message[0]; 664 for (mp = dot-1; mp >= &message[0]; mp--) { 665 if ((mp->m_flag & m) == f) 666 return (mesg); 667 mesg--; 668 } 669 return (NULL); 670 } 671 672 /* 673 * See if the passed name sent the passed message number. Return true 674 * if so. 675 */ 676 static int 677 sender(char *str, int mesg) 678 { 679 return (samebody(str, skin(nameof(&message[mesg-1])), TRUE)); 680 } 681 682 /* 683 * See if the given string matches inside the subject field of the 684 * given message. For the purpose of the scan, we ignore case differences. 685 * If it does, return true. The string search argument is assumed to 686 * have the form "/search-string." If it is of the form "/," we use the 687 * previous search string. 688 */ 689 690 static char lastscan[128]; 691 692 static int 693 matchsubj(char *str, int mesg) 694 { 695 register struct message *mp; 696 register char *cp, *cp2, *backup; 697 698 str++; 699 if (strlen(str) == 0) 700 str = lastscan; 701 else 702 nstrcpy(lastscan, sizeof (lastscan), str); 703 mp = &message[mesg-1]; 704 705 /* 706 * Now look, ignoring case, for the word in the string. 707 */ 708 709 cp = str; 710 cp2 = hfield("subject", mp, addone); 711 if (cp2 == NOSTR) 712 return (0); 713 backup = cp2; 714 while (*cp2) { 715 if (*cp == 0) 716 return (1); 717 if (toupper(*cp++) != toupper(*cp2++)) { 718 cp2 = ++backup; 719 cp = str; 720 } 721 } 722 return (*cp == 0); 723 } 724 725 /* 726 * Mark the named message by setting its mark bit. 727 */ 728 729 static void 730 mark(int mesg) 731 { 732 register int i; 733 734 i = mesg; 735 if (i < 1 || i > msgCount) 736 panic("Bad message number to mark"); 737 message[i-1].m_flag |= MMARK; 738 } 739 740 /* 741 * Unmark the named message. 742 */ 743 744 static void 745 unmark(int mesg) 746 { 747 register int i; 748 749 i = mesg; 750 if (i < 1 || i > msgCount) 751 panic("Bad message number to unmark"); 752 message[i-1].m_flag &= ~MMARK; 753 } 754 755 /* 756 * Return the message number corresponding to the passed meta character. 757 */ 758 static int 759 metamess(int meta, int f) 760 { 761 register int c, m; 762 register struct message *mp; 763 764 c = meta; 765 switch (c) { 766 case '^': 767 /* 768 * First 'good' message left. 769 */ 770 for (mp = &message[0]; mp < &message[msgCount]; mp++) 771 if ((mp->m_flag & MDELETED) == f) 772 return (mp - &message[0] + 1); 773 printf(gettext("No applicable messages\n")); 774 return (-1); 775 776 case '+': 777 /* 778 * Next 'good' message left. 779 */ 780 for (mp = dot + 1; mp < &message[msgCount]; mp++) 781 if ((mp->m_flag & MDELETED) == f) 782 return (mp - &message[0] + 1); 783 printf(gettext("Referencing beyond last message\n")); 784 return (-1); 785 786 case '-': 787 /* 788 * Previous 'good' message. 789 */ 790 for (mp = dot - 1; mp >= &message[0]; mp--) 791 if ((mp->m_flag & MDELETED) == f) 792 return (mp - &message[0] + 1); 793 printf(gettext("Referencing before first message\n")); 794 return (-1); 795 796 case '$': 797 /* 798 * Last 'good message left. 799 */ 800 for (mp = &message[msgCount-1]; mp >= &message[0]; mp--) 801 if ((mp->m_flag & MDELETED) == f) 802 return (mp - &message[0] + 1); 803 printf(gettext("No applicable messages\n")); 804 return (-1); 805 806 case '.': 807 /* 808 * Current message. 809 */ 810 m = dot - &message[0] + 1; 811 if ((dot->m_flag & MDELETED) != f) { 812 printf(gettext("%d: Inappropriate message\n"), m); 813 return (-1); 814 } 815 return (m); 816 817 default: 818 printf(gettext("Unknown metachar (%c)\n"), c); 819 return (-1); 820 } 821 } 822