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