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