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