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