home *** CD-ROM | disk | FTP | other *** search
/ ftp.freefriends.org / ftp.freefriends.org.tar / ftp.freefriends.org / arnold / Source / mush.rstevens.tar.gz / mush.tar / pick.c < prev    next >
C/C++ Source or Header  |  1992-10-30  |  18KB  |  644 lines

  1. /* @(#)pick.c    7.2    (c) copyright 5/19/91 (Dan Heller) */
  2.  
  3. #include "mush.h"
  4.  
  5. static int before, after, search_from, search_subj, search_to, xflg, icase;
  6. static u_long match_priority;
  7. static char search_hdr[64];
  8. static int mdy[3];
  9. static int pick();
  10. static void month_day_year();
  11.  
  12. do_pick(n, argv, list)
  13. register int n;
  14. register char **argv, list[];
  15. {
  16.     char ret_list[MAXMSGS_BITS];
  17.  
  18.     if (n > 1 && !strcmp(argv[1], "-?"))
  19.     return help(0, "pick", cmd_help);
  20.  
  21.     clear_msg_list(ret_list);
  22.     /* if is_pipe, then the messages to search for are already set.
  23.      * if not piped, then reverse the bits for all message numbers.
  24.      * That is, search EACH message. only those matching will be returned.
  25.      */
  26.     if (isoff(glob_flags, IS_PIPE))
  27.     bitput(ret_list, list, msg_cnt, =~); /* macro, turn on all bits */
  28.     /* Use n temporarily as verbosity flag */
  29.     n = (!chk_option("quiet", "pick") &&
  30.         isoff(glob_flags, DO_PIPE));
  31.     if ((n = pick(argv, list, ret_list, n)) == -1)
  32.     return -1;
  33.     if (istool && isoff(glob_flags, DO_PIPE))
  34.     print("%d matches:\n", n);
  35.     for (n = 0; n < msg_cnt; n++)
  36.     if (msg_bit(ret_list, n)) {
  37.         if (isoff(glob_flags, DO_PIPE))
  38.         if (istool)
  39.             print_more("%d ", n+1);
  40.         else
  41.             print("%s\n", compose_hdr(n));
  42.         set_msg_bit(list, n);
  43.     } else
  44.         unset_msg_bit(list, n);
  45.     return 0;
  46. }
  47.  
  48. /*
  49.  * search for messages.  Return the number of matches.  Errors such
  50.  * as internal errors or syntax errors, return -1.
  51.  * "head" and "tail" are specified using +<num> or -<num> as args.
  52.  * Both can be specified and the order is significant.
  53.  *    pick +5 -3
  54.  * returns the last three of the first five matches.
  55.  *    pick -3 +2
  56.  * returns the first two of the last three matches.
  57.  */
  58. static int
  59. pick(argv, list, ret_list, verbose)
  60. register char **argv, list[], ret_list[];
  61. {
  62.     register char c;
  63.     int matches = 0;
  64.     char pattern[256];
  65.     short head_first, head_cnt, tail_cnt, search = TRUE;
  66.     int n;
  67.  
  68.     if (!msg_cnt) {
  69.     print("No Messages.\n");
  70.     return -1;
  71.     }
  72.  
  73.     head_first = TRUE;
  74.     head_cnt = tail_cnt = -1;
  75.     match_priority = 0;
  76.     icase = before = after = search_from = search_subj = search_to = xflg = 0;
  77.     pattern[0] = mdy[0] = mdy[1] = search_hdr[0] = 0;
  78.     while (*argv && *++argv && (**argv == '-' || **argv == '+'))
  79.     if (**argv == '+' || isdigit(argv[0][1])) {
  80.         if (**argv == '+')
  81.         head_cnt = atoi(&argv[0][1]);
  82.         else {
  83.         tail_cnt = atoi(&argv[0][1]);
  84.         if (head_cnt == -1)
  85.             head_first = FALSE;
  86.         }
  87.         if (head_cnt == 0 || tail_cnt == 0) {
  88.         print("pick: invalid head/tail number: %s\n", &argv[0][1]);
  89.         clear_msg_list(ret_list);
  90.         return -1;
  91.         }
  92.     } else if ((c = argv[0][1]) == 'e') {
  93.         if (!*++argv) {
  94.         print("usage: -e expression...\n");
  95.         return -1;
  96.         }
  97.         break;
  98.     } else switch (c) {
  99.         /* user specifies a range */
  100.         case 'r': {
  101.         int X = 2;
  102.         /* if not a pipe, then clear all bits cuz we only want
  103.          * to search the message specified here...
  104.          * If it is a pipe, then add to the messages searched for.
  105.          */
  106.         if (isoff(glob_flags, IS_PIPE))
  107.             clear_msg_list(list);
  108.         /*  "-r10-15"
  109.          *     ^argv[1][2]  if NULL, then
  110.          * list detached from "r" e.g. "-r" "5-20"
  111.          */
  112.         if (!argv[0][X])
  113.             argv++, X = 0;
  114.         (*argv) += X;
  115.         n = get_msg_list(argv, list);
  116.         (*argv) -= X;
  117.         if (n == -1)
  118.             return -1;
  119.         argv += (n-1); /* we're going to increment another up top */
  120.         }
  121.         when 'a': {
  122.         if ((n = ago_date(++argv)) == -1)
  123.             return -1;
  124.         argv += n;
  125.         }
  126.         when 'd':
  127.         if (!*++argv) {
  128.             print("Specify a date for -%c\n", c);
  129.             return -1;
  130.         }
  131.         if (!date1(*argv))
  132.             return -1;
  133.         when 's' : case 'f': case 't': case 'h':
  134.         if (search_subj + search_from + search_to + *search_hdr > 1) {
  135.             print("Specify one of `s', `f', `t' or `h' only\n");
  136.             return -1;
  137.             }
  138.             if (c == 's')
  139.             search_subj = 1;
  140.         else if (c == 'f')
  141.             search_from = 1;
  142.         else if (c == 'h')
  143.             if (!*++argv)
  144.             print("Specify header to search for.\n");
  145.             else
  146.             (void) lcase_strcpy(search_hdr, *argv);
  147.         else
  148.             search_to = 1;
  149.         when 'p' :    /* Select on priority field */
  150.         if (!*++argv || (c = upper(**argv)) < 'A' ||
  151.             c > MAX_PRIORITY + 'A') {
  152.             print("pick: invalid priority: %s\n", argv[0]);
  153.             clear_msg_list(ret_list);
  154.             return -1;
  155.         }
  156.         turnon(match_priority, M_PRIORITY(c - 'A' + 1));
  157.         when 'x' : xflg = 1;
  158.         when 'i' : icase = 1;
  159.         otherwise:
  160.         print("pick: unknown flag: %c\n", argv[0][1]);
  161.         clear_msg_list(ret_list);
  162.         return -1;
  163.     }
  164.     if (xflg && head_cnt + tail_cnt >= 0) {
  165.     print("Can't specify -x and head/tail options together.\n");
  166.     return -1;
  167.     }
  168.     if (!mdy[1]) {
  169.     (void) argv_to_string(pattern, argv);
  170.     if (pattern[0] == '\0' && match_priority == 0 &&
  171.         head_cnt + tail_cnt < 0) {
  172.         print("No pattern specified\n");
  173.         clear_msg_list(ret_list);  /* doesn't matter really */
  174.         return -1;
  175.     }
  176.     }
  177.     search = (pattern[0] || mdy[1] || match_priority > 0);
  178.     if (verbose) {
  179.     if (head_cnt + tail_cnt >= 0) {
  180.         print("Finding the ");
  181.         if (head_cnt > 0) {
  182.         if (head_first)
  183.             if (tail_cnt == -1)
  184.             print_more("first %d message%s",
  185.                 head_cnt, head_cnt > 1? "s" : "");
  186.             else
  187.             print_more("last %d message%s",
  188.                 tail_cnt, tail_cnt > 1? "s" : "");
  189.         else /* there must be a tail_cnt and it comes first */
  190.             print_more("first %d message%s",
  191.                 head_cnt, head_cnt > 1? "s" : "");
  192.         } else
  193.         print_more("last %d message%s",
  194.             tail_cnt, tail_cnt > 1? "s" : "");
  195.         if (tail_cnt > 0 && head_cnt > 0)
  196.         if (head_first)
  197.             print_more(" of the first %d", head_cnt);
  198.         else
  199.             print_more(" of the last %d", tail_cnt);
  200.     } else
  201.         print_more("Searching for %smessages",
  202.         match_priority > 0 ? "priority " : "");
  203.     if (!search) {
  204.         if (tail_cnt > 0 && head_cnt > 0)
  205.         print_more(" messages");
  206.         if (ison(glob_flags, IS_PIPE))
  207.         print_more(" from the input list");
  208.     } else if (pattern[0]) {
  209.         print_more(" that %scontain \"%s\"", (xflg)? "do not ": "",
  210.                 pattern);
  211.         if (search_subj)
  212.         print_more(" in subject line");
  213.         else if (search_from)
  214.         print_more(" from author names");
  215.         else if (search_to)
  216.         print_more(" from the To: field");
  217.         else if (search_hdr[0])
  218.         print_more(" from the message header \"%s:\"", search_hdr);
  219.     } else if (mdy[1]) {
  220.         extern char *month_names[]; /* from dates.c */
  221.         print_more("%s dated ", xflg && !(before || after)? " not" : "");
  222.         if (before || after)
  223.         if (xflg)
  224.             print_more("%s ", (!before)? "before": "after");
  225.         else
  226.             print_more("on or %s ", (before)? "before": "after");
  227.         print_more("%s. %d, %d",
  228.               month_names[mdy[0]], mdy[1], mdy[2] + 1900);
  229.     }
  230.     print_more(".\n");
  231.     }
  232.     if (mdy[1] > 0 && icase)
  233.     print("using date: -i flag ignored.\n");
  234.     if (!search) {
  235.     for (n = 0; n < msg_cnt && (!head_first || matches < head_cnt); n++)
  236.         if (msg_bit(list, n))
  237.         ++matches, set_msg_bit(ret_list, n);
  238.     } else
  239.     matches = find_pattern(head_first? head_cnt : msg_cnt,
  240.                pattern, list, ret_list);
  241.     if (xflg && matches >= 0) {
  242.     /* invert items in ret_list that also appear in list */
  243.     bitput(list, ret_list, msg_cnt, ^=);
  244.     /* there should be a faster way to do this count ... */
  245.     for (matches = n = 0; n < msg_cnt; n++)
  246.         if (msg_bit(ret_list, n))
  247.         ++matches;
  248.     }
  249.     Debug("matches = %d\n", matches);
  250.     if (!matches)
  251.     return 0;
  252.  
  253.     /* ok, the list we've got is a list of matched messages.  If "tailing"
  254.      * is set, reduce the number of matches to at least tail_cnt.
  255.      */
  256.     if (tail_cnt >= 0)
  257.     for (n = 0; n < msg_cnt && matches > tail_cnt; n++)
  258.         if (msg_bit(ret_list, n)) {
  259.         Debug("tail: dropping %d\n", n+1);
  260.         unset_msg_bit(ret_list, n);
  261.         matches--;
  262.         }
  263.  
  264.     /* if tailing came before heading, we need to do the heading now. */
  265.     if (!head_first && head_cnt >= 0)
  266.     for (n = 0; n < msg_cnt; n++)
  267.         if (msg_bit(ret_list, n))
  268.         if (head_cnt > 0)
  269.             head_cnt--;
  270.         else {
  271.             unset_msg_bit(ret_list, n);
  272.             matches--;
  273.         }
  274.     return matches;
  275. }
  276.  
  277. /*
  278.  * find_pattern will search thru all the messages set in the check_list
  279.  * until the list runs out or "cnt" has been exhasted.  ret_list contains
  280.  * the list of messages which have matched the pattern.
  281.  * return -1 for internal error or # of pattern matches.
  282.  */
  283. find_pattern(cnt, p, check_list, ret_list)
  284. int cnt;
  285. register char *p;
  286. char check_list[], ret_list[];
  287. {
  288.     register int n, val, i; /* val is return value from regex or re_exec */
  289.     int matches = 0;
  290.     long bytes = 0;
  291.     char buf[HDRSIZ];
  292.     char *err = NULL;
  293. #ifdef REGCMP
  294.     char *regcmp(), *regex();
  295. #else /* REGCMP */
  296.     char *re_comp();
  297. #endif /* REGCMP */
  298.  
  299.     if (p && *p == '\\')
  300.     p++;  /* take care of escaping special cases (`-', `\') */
  301.  
  302.     /* specify what we're looking for */
  303.     if (p && *p) {
  304.     if (icase)
  305.         p = lcase_strcpy(buf, p);
  306. #ifdef REGCMP
  307.     if (p && !(err = regcmp(p, NULL))) {
  308.         print("regcmp error: %s\n", p);
  309.         clear_msg_list(ret_list);
  310.         return -1;
  311.     }
  312. #else /* REGCMP */
  313.     if (err = re_comp(p)) {
  314.         print("re_comp error: %s\n", err);
  315.         clear_msg_list(ret_list);
  316.         return -1;
  317.     }
  318. #endif /* REGCMP */
  319.     } else if (err == NULL && mdy[1] <= 0 && match_priority == 0) {
  320.     print("No previous regular expression\n");
  321.     clear_msg_list(ret_list);  /* doesn't matter really */
  322.     return -1;
  323.     }
  324.     /* start searching: set bytes, and message number: n */
  325.     for (n = 0; cnt && n < msg_cnt; n++)
  326.     if (msg_bit(check_list, n)) {
  327.         if (match_priority > 0) {
  328.         if (msg[n].m_flags & match_priority)
  329.             ++matches, set_msg_bit(ret_list, n);
  330.         continue;
  331.         }
  332.         if (mdy[1] > 0) {
  333.         int msg_mdy[3];
  334.         if (ison(glob_flags, DATE_RECV))
  335.             p = msg[n].m_date_recv;
  336.         else
  337.             p = msg[n].m_date_sent;
  338.         /* Ick -- fix this mdy thing asap */
  339.         month_day_year(p, &msg_mdy[0], &msg_mdy[1], &msg_mdy[2]);
  340.         Debug("checking %d's date: %d-%d-%d  ",
  341.                  n+1, msg_mdy[0]+1, msg_mdy[1], msg_mdy[2]);
  342.         /* start at year and wrap around.
  343.          * only when match the day (4), check for == (match)
  344.          */
  345.         for (i = 2; i < 5; i++)
  346.             if (before && msg_mdy[i%3] < mdy[i%3]
  347.             ||  after  && msg_mdy[i%3] > mdy[i%3]
  348.             ||  i == 4 && (msg_mdy[i%3] == mdy[i%3])) {
  349.                 Debug("matched (%s).\n",
  350.                 (i == 2)? "year" : (i == 3)? "month" : "day");
  351.                 set_msg_bit(ret_list, n);
  352.                 cnt--, matches++;
  353.                 break;
  354.             } else if (msg_mdy[i%3] != mdy[i%3]) {
  355.             Debug("failed.\n");
  356.             break;
  357.             }
  358.         continue;
  359.         }
  360.         /* we must have the right date -- if we're searching for a
  361.          * string, find it.
  362.          */
  363.         (void) msg_get(n, NULL, 0);
  364.         bytes = 0;
  365.         while (bytes < msg[n].m_size) {
  366.         if (!search_subj && !search_from && !search_to &&
  367.             !*search_hdr && !(p = fgets(buf, sizeof buf, tmpf)))
  368.             break;
  369.         else if (search_subj) {
  370.             if (!(p = header_field(n, "subject")))
  371.             break;
  372.         } else if (search_from) {
  373.             if (!(p = header_field(n, "from"))) {
  374.             /*
  375.              * Check for MSG_SEPARATOR here?  Maybe not...
  376.              */
  377.             register char *p2;
  378.             (void) msg_get(n, NULL, 0);
  379.             if (!(p2 = fgets(buf, sizeof buf, tmpf)) ||
  380.                 !(p = index(p2, ' ')))
  381.                 continue;
  382.             p++;
  383.             if (p2 = any(p, " \t"))
  384.                 *p2 = 0;
  385.             }
  386.         } else if (search_to) {
  387.             if (!(p = header_field(n, "to")) &&
  388.                 !(p = header_field(n, "apparently-to")))
  389.             break;
  390.         } else if (*search_hdr) {
  391.             if (!(p = header_field(n, search_hdr)))
  392.             break;
  393.         }
  394.         if (icase)
  395.             p = lcase_strcpy(buf, p);
  396. #ifdef REGCMP
  397.         val = !!regex(err, p, NULL); /* convert value to a boolean */
  398. #else /* REGCMP */
  399.         val = re_exec(p);
  400. #endif /* REGCMP */
  401.         if (val == -1) {   /* doesn't apply in system V */
  402.             print("Internal error for pattern search.\n");
  403.             clear_msg_list(ret_list); /* it doesn't matter, really */
  404.             return -1;
  405.         }
  406.         if (val) {
  407.             set_msg_bit(ret_list, n);
  408.             cnt--, matches++;
  409.             break;
  410.         }
  411.         if (search_subj || search_from || search_to || *search_hdr)
  412.             break;
  413.         else
  414.             bytes += strlen(p);
  415.         }
  416.     }
  417. #ifdef REGCMP
  418.     if (err)
  419.     free(err);
  420. #endif /* REGCMP */
  421.     return matches;
  422. }
  423.  
  424. #ifdef CURSES
  425. /*
  426.  * search for a pattern in composed message headers -- also see next function
  427.  * flags ==  0   forward search (prompt).
  428.  * flags == -1   continue search (no prompt).
  429.  * flags ==  1   backward search (prompt).
  430.  */
  431. search(flags)
  432. register int flags;
  433. {
  434.     register char   *p;
  435.     char           pattern[128];
  436.     register int    this_msg = current_msg, val = 0;
  437.     static char     *err = (char *)-1, direction;
  438.     SIGRET        (*oldint)(), (*oldquit)();
  439. #ifdef REGCMP
  440.     char *regex(), *regcmp();
  441. #else /* REGCMP */
  442.     char *re_comp();
  443. #endif /* REGCMP */
  444.  
  445.     if (msg_cnt <= 1) {
  446.     print("Not enough messages to invoke a search.\n");
  447.     return 0;
  448.     }
  449.     pattern[0] = '\0';
  450.     if (flags == -1)
  451.     print("continue %s search...", direction? "forward" : "backward");
  452.     else
  453.     print("%s search: ", flags? "backward" : "forward");
  454.     if (flags > -1)
  455.     if (Getstr(pattern, COLS-18, 0) < 0)
  456.         return 0;
  457.     else
  458.         direction = !flags;
  459. #ifdef REGCMP
  460.     if (err != (char *)-1 && *pattern)
  461.     xfree(err);
  462.     else if (err == (char *)-1 && !*pattern) {
  463.     print("No previous regular expression.");
  464.     return 0;
  465.     }
  466.     if (*pattern && !(err = regcmp(pattern, NULL))) {
  467.     print("Error in regcmp in %s", pattern);
  468.     return 0;
  469.     }
  470. #else /* REGCMP */
  471.     if (err = re_comp(pattern)) {
  472.     print(err);
  473.     return 0;
  474.     }
  475. #endif /* REGCMP */
  476.     move(LINES-1, 0), refresh();
  477.     on_intr();
  478.  
  479.     do  {
  480.     if (direction)
  481.         current_msg = (current_msg+1) % msg_cnt;
  482.     else
  483.         if (--current_msg < 0)
  484.         current_msg = msg_cnt-1;
  485.     p = compose_hdr(current_msg);
  486. #ifdef REGCMP
  487.     val = !!regex(err, p, NULL); /* convert value to a boolean */
  488. #else /* REGCMP */
  489.     val = re_exec(p);
  490. #endif /* REGCMP */
  491.     if (val == -1)     /* doesn't apply in system V */
  492.         print("Internal error for pattern search.\n");
  493.     } while (!val && current_msg != this_msg && isoff(glob_flags, WAS_INTR));
  494.  
  495.     if (ison(glob_flags, WAS_INTR)) {
  496.     print("Pattern search interrupted.");
  497.     current_msg = this_msg;
  498.     } else if (val == 0)
  499.     print("Pattern not found.");
  500.  
  501.     off_intr();
  502.     return val;
  503. }
  504. #endif /* CURSES */
  505.  
  506. /*
  507.  * Get just the month, day, and year from a date.
  508.  * This is a temporary measure until the date compares in pick()
  509.  * can be overhauled.  It really should be in dates.c, but ...
  510.  */
  511. static
  512. void
  513. month_day_year(date, month, day, year)
  514. char *date;
  515. int *month, *day, *year;
  516. {
  517.     long gmt;
  518.     char unused[4], zone[8];
  519.     struct tm *t;
  520.     extern long getzoff();
  521.  
  522.     (void) sscanf(date, "%ld%3c%s", &gmt, unused, zone);
  523.     gmt += getzoff(zone);
  524.     t = gmtime(&gmt);
  525.     *month = t->tm_mon;
  526.     *day = t->tm_mday;
  527.     *year = t->tm_year;
  528. }
  529.  
  530. /*
  531.  * parse a user given date string and set mdy[] array with correct
  532.  * values.  Return 0 on failure.
  533.  */
  534. date1(p)
  535. register char *p;
  536. {
  537.     register char *p2;
  538.     long      t;
  539.     int       i;
  540.     struct tm       *today;
  541.  
  542.     if (*p == '-' || *p == '+') {
  543.     before = !(after = *p == '+');
  544.     skipspaces(1);
  545.     }
  546.     if (!isdigit(*p) && *p != '/') {
  547.     print("syntax error on date: \"%s\"\n", p);
  548.     return 0;
  549.     }
  550.     (void) time (&t);
  551.     today = localtime(&t);
  552.     for (i = 0; i < 3; i++)
  553.     if (!p || !*p || *p == '/') {
  554.         switch(i) {   /* default to today's date */
  555.         case 0: mdy[0] = today->tm_mon;
  556.         when 1: mdy[1] = today->tm_mday;
  557.         when 2: mdy[2] = today->tm_year;
  558.         }
  559.         if (p && *p)
  560.         p++;
  561.     } else {
  562.         p2 = (*p)? index(p+1, '/') : NULL;
  563.         mdy[i] = atoi(p); /* atoi will stop at the '/' */
  564.         if (i == 0 && (--(mdy[0]) < 0 || mdy[0] > 11)) {
  565.         print("Invalid month: %s\n", p);
  566.         return 0;
  567.         } else if (i == 1 && (mdy[1] < 1 || mdy[1] > 31)) {
  568.         print("Invalid day: %s\n", p);
  569.         return 0;
  570.         }
  571.         if (p = p2) /* set p to p2 and check to see if it's valid */
  572.         p++;
  573.     }
  574.     return 1;
  575. }
  576.  
  577. /*
  578.  * Parse arguments specifying days/months/years "ago" (relative to today).
  579.  * Legal syntax: -ago [+-][args]
  580.  *    where "args" is defined to be:
  581.  *    [0-9]+[ ]*[dD][a-Z]*[ ,]*[0-9]+[mM][a-Z]*[ ,]*[0-9]+[ ]*[yY][a-Z]*
  582.  *    1 or more digits, 0 or more spaces, d or D followed by 0 or more chars,
  583.  *    0 or more whitespaces or commas, repeat for months and years...
  584.  * Examples:
  585.  *    1 day, 2 months, 0 years
  586.  *    2 weeks 1 year
  587.  *    10d, 5m
  588.  *    3w
  589.  *    1d 1Y
  590.  *
  591.  * Return number of args parsed; -1 on error.
  592.  */
  593. ago_date(argv)
  594. char **argv;
  595. {
  596. #define SECS_PER_DAY   (60 * 60 * 24)
  597. #define SECS_PER_WEEK  (SECS_PER_DAY * 7)
  598. #define SECS_PER_MONTH ((int)(SECS_PER_DAY * 30.5))
  599. #define SECS_PER_YEAR  (SECS_PER_DAY * 365)
  600.     register char *p;
  601.     char       buf[256];
  602.     int           n = 0, value;
  603.     long       t;
  604.     struct tm       *today;
  605.  
  606.     (void) argv_to_string(buf, argv);
  607.     p = buf;
  608.     (void) time (&t); /* get current time in seconds and subtract new values */
  609.     if (*p == '-')
  610.     before = TRUE;
  611.     else if (*p == '+')
  612.     after = TRUE;
  613.     skipspaces(before || after);
  614.     while (*p) {
  615.     if (!isdigit(*p)) {
  616.         p -= 2;
  617.         break; /* really a syntax error, but it could be other pick args */
  618.     }
  619.     p = my_atoi(p, &value); /* get 1 or more digits */
  620.     skipspaces(0); /* 0 or more spaces */
  621.     switch (lower(*p)) {   /* d, m, or y */
  622.         case 'd' : t -= value * SECS_PER_DAY;
  623.         when 'w' : t -= value * SECS_PER_WEEK;
  624.         when 'm' : t -= value * SECS_PER_MONTH;
  625.         when 'y' : t -= value * SECS_PER_YEAR;
  626.         otherwise: return -1;
  627.     }
  628.     for (p++; Lower(*p) >= 'a' && *p <= 'z'; p++)
  629.         ; /* skip the rest of this token */
  630.     while (*p == ',' || isspace(*p))
  631.         ++p; /* 0 or more whitespaces or commas */
  632.     }
  633.     today = localtime(&t);
  634.     mdy[0] = today->tm_mon;
  635.     mdy[1] = today->tm_mday;
  636.     mdy[2] = today->tm_year;
  637.  
  638.     /* Count the number of args parsed */
  639.     for (n = 0; p > buf && *argv; n++)
  640.     p -= (strlen(*argv++)+1);
  641.     Debug("parsed %d args\n", n);
  642.     return n;
  643. }
  644.