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