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