home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / mail / list.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-18  |  15.4 KB  |  775 lines

  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. #ifndef lint
  35. static char sccsid[] = "@(#)list.c    5.14 (Berkeley) 6/1/90";
  36. #endif /* not lint */
  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. }
  775.