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