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