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