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