home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume18 / mush6.4 / part06 / pick.c < prev    next >
C/C++ Source or Header  |  1989-03-12  |  13KB  |  492 lines

  1. /* @(#)pick.c    2.4    (c) copyright 10/18/86 (Dan Heller) */
  2.  
  3. #include "mush.h"
  4.  
  5. static int before, after, search_from, search_subj, search_to, xflg, icase;
  6. static mdy[3], search_hdr[64];
  7. static int pick();
  8.  
  9. do_pick(n, argv, list)
  10. register int n;
  11. register char **argv, list[];
  12. {
  13.     char ret_list[MAXMSGS_BITS];
  14.  
  15.     if (n > 1 && !strcmp(argv[1], "-?"))
  16.     return help(0, "pick", cmd_help);
  17.  
  18.     /* if is_pipe, then the messages to search for are already set.
  19.      * if not piped, then reverse the bits for all message numbers.
  20.      * That is, search EACH message. only those matching will be returned.
  21.      */
  22.     if (isoff(glob_flags, IS_PIPE))
  23.     bitput(list, list, msg_cnt, =~) /* macro, turn on all bits */
  24.     clear_msg_list(ret_list);
  25.     if (pick(n, argv, list, ret_list, isoff(glob_flags, DO_PIPE)) == -1)
  26.     return -1;
  27.     if (istool && isoff(glob_flags, DO_PIPE))
  28.     print("Messages: ");
  29.     for (n = 0; n < msg_cnt; n++)
  30.     if (msg_bit(ret_list, n) && !xflg ||
  31.         !msg_bit(ret_list, n) && xflg) {
  32.         if (isoff(glob_flags, DO_PIPE))
  33.         if (istool)
  34.             print_more("%d ", n+1);
  35.         else
  36.             print("%s\n", compose_hdr(n));
  37.         set_msg_bit(list, n);
  38.     } else
  39.         unset_msg_bit(list, n);
  40.     return 0;
  41. }
  42.  
  43. /*
  44.  * search for messages.  Even if no messages match, return 0.  Errors such
  45.  * as internal errors or syntax errors, return -1.
  46.  */
  47. static int
  48. pick(ret, argv, list, ret_list, verbose)
  49. register int ret;
  50. register char **argv, list[], ret_list[];
  51. {
  52.     register char c;
  53.     char pattern[256];
  54.     int o_before = before, o_after = after, o_search_from = search_from,
  55.     o_search_subj = search_subj, o_search_to = search_to, o_mdy[3], n;
  56.  
  57.     for (c = 0; c < 3; c++)
  58.     o_mdy[c] = mdy[c];
  59.  
  60.     ret = -1;
  61.     if (!msg_cnt) {
  62.     print("No Messages.\n");
  63.     goto bad;
  64.     }
  65.  
  66.     icase = before = after = search_from = search_subj = xflg = 0;
  67.     mdy[0] = search_hdr[0] = 0;
  68.     while (*argv && *++argv && **argv == '-')
  69.     switch(c = argv[0][1]) {
  70.         /* users specifies a range */
  71.         case 'r': {
  72.         int X = 2;
  73.         /* if not a pipe, then clear all bits cuz we only want
  74.          * to search the message specified here...
  75.          * If it is a pipe, then add to the messages searched for.
  76.          */
  77.         if (isoff(glob_flags, IS_PIPE))
  78.             clear_msg_list(list);
  79.         /*  "-r10-15"
  80.          *     ^argv[1][2]  if NULL, then
  81.          * list detached from "r" e.g. "-r" "5-20"
  82.          */
  83.         if (!argv[0][X])
  84.             argv++, X = 0;
  85.         (*argv) += X;
  86.         n = get_msg_list(argv, list);
  87.         (*argv) -= X;
  88.         if (n == -1)
  89.             goto bad;
  90.         argv += (n-1); /* we're going to increment another up top */
  91.         }
  92.         when 'a': {
  93.         if ((n = ago_date(++argv)) == -1)
  94.             goto bad;
  95.         argv += n;
  96.         }
  97.         when 'd':
  98.         if (!*++argv) {
  99.             print("specify a date for -%c\n", c);
  100.             goto bad;
  101.         }
  102.         if (!date1(*argv))
  103.             goto bad;
  104.         when 's' : case 'f': case 't': case 'h':
  105.         if (search_subj + search_from + search_to + *search_hdr > 1) {
  106.             print("specify one of `s', `f', `t' or `h' only\n");
  107.             goto bad;
  108.             }
  109.             if (c == 's')
  110.             search_subj = 1;
  111.         else if (c == 'f')
  112.             search_from = 1;
  113.         else if (c == 'h')
  114.             if (!*++argv)
  115.             print("Specify header to search for.\n");
  116.             else
  117.             (void) lcase_strcpy(search_hdr, *argv);
  118.         else
  119.             search_to = 1;
  120.         when 'x' : xflg = 1;
  121.         when 'i' : icase = 1;
  122.         otherwise:
  123.         print("pick: unknown flag: %c\n", argv[0][1]);
  124.         clear_msg_list(ret_list);
  125.         return -1;
  126.     }
  127.     pattern[0] = 0;
  128.     (void) argv_to_string(pattern, argv);
  129.     if (verbose) {
  130.     print_more("Searching for messages");
  131.     if (mdy[1] == 0) {
  132.         print(" that %scontain \"%s\"", (xflg)? "does not ": "",
  133.                 (*pattern)? pattern: "<previous expression>");
  134.         if (search_subj)
  135.         print_more(" in subject line");
  136.         else if (search_from)
  137.         print_more(" from author names");
  138.         else if (search_to)
  139.         print_more(" from the To: field");
  140.         else if (search_hdr[0])
  141.         print_more(" from the message header: \"%s:\"", search_hdr);
  142.     } else {
  143.         extern char *month_names[]; /* from dates.c */
  144.         print_more(" dated ");
  145.         if (before || after)
  146.         if (xflg)
  147.             print_more("%s ", (!before)? "before": "after");
  148.         else
  149.             print_more("on or %s ", (before)? "before": "after");
  150.         print_more("%s. %d, 19%d.",
  151.               month_names[mdy[0]], mdy[1], mdy[2]);
  152.     }
  153.     print_more("\n");
  154.     }
  155.     if (mdy[1] > 0 && icase)
  156.     print("using date: -i flag ignored.\n");
  157.     ret = find_pattern(pattern, list, ret_list);
  158. bad:
  159.     before = o_before, after = o_after, search_from = o_search_from;
  160.     search_subj = o_search_subj, search_to = o_search_to;
  161.     for (c = 0; c < 3; c++)
  162.     mdy[c] = o_mdy[c];
  163.  
  164.     return ret;
  165. }
  166.  
  167. find_pattern(p, check_list, ret_list)
  168. register char *p;
  169. char check_list[], ret_list[];
  170. {
  171.     register int n, val, i; /* val is return value from regex or re_exec */
  172.     long bytes = 0;
  173.     char buf[HDRSIZ];
  174.     static char *err = (char *)-1;
  175. #ifdef REGCMP
  176.     char *regcmp(), *regex();
  177. #else /* REGCMP */
  178.     char *re_comp();
  179. #endif /* REGCMP */
  180.  
  181.     if (p && *p == '\\')
  182.     p++;  /* take care of escaping special cases (`-', `\') */
  183.  
  184.     /* specify what we're looking for */
  185.     if (p && *p) {
  186.     if (icase)
  187.         p = lcase_strcpy(buf, p);
  188. #ifdef REGCMP
  189.     if (err && p)
  190.         xfree(err);
  191.     if (p && !(err = regcmp(p, NULL))) {
  192.         print("regcmp error: %s\n", p);
  193.         clear_msg_list(ret_list);
  194.         return -1;
  195.     }
  196. #else /* REGCMP */
  197.     if (err = re_comp(p)) {
  198.         print("re_comp error: %s\n", err);
  199.         clear_msg_list(ret_list);
  200.         return -1;
  201.     }
  202. #endif /* REGCMP */
  203.     } else if (err == (char *)-1 && mdy[1] <= 0) {
  204.     print("No previous regular expression\n");
  205.     clear_msg_list(ret_list);  /* doesn't matter really */
  206.     return -1;
  207.     }
  208.     /* start searching: set bytes, and message number: n */
  209.     for (n = 0; n < msg_cnt; n++)
  210.     if (msg_bit(check_list, n)) {
  211.         if (mdy[1] > 0) {
  212.         int msg_mdy[3];
  213.         if (ison(glob_flags, DATE_RECV))
  214.             p = msg[n].m_date_recv;
  215.         else
  216.             p = msg[n].m_date_sent;
  217.         (void) sscanf(p, "%2d%2d%2d", /* yr mo da */
  218.              &msg_mdy[2], &msg_mdy[0], &msg_mdy[1]);
  219.         msg_mdy[0]--;
  220.         Debug("checking %d's date: %d-%d-%d  ",
  221.                  n+1, msg_mdy[0]+1, msg_mdy[1], msg_mdy[2]);
  222.         /* start at year and wrap around.
  223.          * only when match the day (4), check for == (match)
  224.          */
  225.         for (i = 2; i < 5; i++)
  226.             if (before && msg_mdy[i%3] < mdy[i%3]
  227.             ||  after  && msg_mdy[i%3] > mdy[i%3]
  228.             ||  i == 4 && (msg_mdy[i%3] == mdy[i%3])) {
  229.                 Debug("matched (%s).\n",
  230.                 (i == 2)? "year" : (i == 3)? "month" : "day");
  231.                 set_msg_bit(ret_list, n);
  232.                 break;
  233.             } else if (msg_mdy[i%3] != mdy[i%3]) {
  234.             Debug("failed.\n");
  235.             break;
  236.             }
  237.         continue;
  238.         }
  239.         /* we must have the right date -- if we're searching for a
  240.          * string, find it.
  241.          */
  242.         (void) msg_get(n, NULL, 0);
  243.         bytes = 0;
  244.         while (bytes < msg[n].m_size) {
  245.         if (!search_subj && !search_from && !search_to &&
  246.             !*search_hdr && !(p = fgets(buf, sizeof buf, tmpf)))
  247.             break;
  248.         else if (search_subj) {
  249.             if (!(p = header_field(n, "subject")))
  250.             break;
  251.         } else if (search_from) {
  252.             if (!(p = header_field(n, "from"))) {
  253.             /*
  254.              * Check for MSG_SEPARATOR here?  Maybe not...
  255.              */
  256.             register char *p2;
  257.             (void) msg_get(n, NULL, 0);
  258.             if (!(p2 = fgets(buf, sizeof buf, tmpf)) ||
  259.                 !(p = index(p2, ' ')))
  260.                 continue;
  261.             p++;
  262.             if (p2 = any(p, " \t"))
  263.                 *p2 = 0;
  264.             }
  265.         } else if (search_to) {
  266.             if (!(p = header_field(n, "to")) &&
  267.                 !(p = header_field(n, "apparently-to")))
  268.             break;
  269.         } else if (*search_hdr) {
  270.             if (!(p = header_field(n, search_hdr)))
  271.             break;
  272.         }
  273.         if (icase)
  274.             p = lcase_strcpy(buf, p);
  275. #ifdef REGCMP
  276.         val = !!regex(err, p, NULL); /* convert value to a boolean */
  277. #else /* REGCMP */
  278.         val = re_exec(p);
  279. #endif /* REGCMP */
  280.         if (val == -1) {   /* doesn't apply in system V */
  281.             print("Internal error for pattern search.\n");
  282.             clear_msg_list(ret_list); /* it doesn't matter, really */
  283.             return -1;
  284.         }
  285.         if (!val)
  286.             bytes += strlen(p);
  287.         else {
  288.             set_msg_bit(ret_list, n);
  289.             break;
  290.         }
  291.         if (search_subj || search_from || search_to || *search_hdr)
  292.             break;
  293.         }
  294.     }
  295.     return 0;
  296. }
  297.  
  298. #ifdef CURSES
  299. /*
  300.  * search for a pattern in composed message headers -- also see next function
  301.  * flags ==  0   forward search (prompt).
  302.  * flags == -1   continue search (no prompt).
  303.  * flags ==  1   backward search (prompt).
  304.  */
  305. search(flags)
  306. register int flags;
  307. {
  308.     register char   *p;
  309.     char           pattern[128];
  310.     register int    this_msg = current_msg, val = 0;
  311.     static char     *err = (char *)-1, direction;
  312.     SIGRET        (*oldint)(), (*oldquit)();
  313. #ifdef REGCMP
  314.     char *regcmp();
  315. #else /* REGCMP */
  316.     char *re_comp();
  317. #endif /* REGCMP */
  318.  
  319.     if (msg_cnt <= 1) {
  320.     print("Not enough messages to invoke a search.\n");
  321.     return 0;
  322.     }
  323.     pattern[0] = '\0';
  324.     if (flags == -1)
  325.     print("continue %s search...", direction? "forward" : "backward");
  326.     else
  327.     print("%s search: ", flags? "backward" : "forward");
  328.     if (flags > -1)
  329.     if (Getstr(pattern, COLS-18, 0) < 0)
  330.         return 0;
  331.     else
  332.         direction = !flags;
  333. #ifdef REGCMP
  334.     if (err && *pattern)
  335.     xfree(err);
  336.     else if (err == (char *)-1 && !*pattern) {
  337.     print("No previous regular expression.");
  338.     return 0;
  339.     }
  340.     if (*pattern && !(err = regcmp(pattern, NULL))) {
  341.     print("Error in regcmp in %s", pattern);
  342.     return 0;
  343.     }
  344. #else /* REGCMP */
  345.     if (err = re_comp(pattern)) {
  346.     print(err);
  347.     return 0;
  348.     }
  349. #endif /* REGCMP */
  350.     move(LINES-1, 0), refresh();
  351.     on_intr();
  352.  
  353.     do  {
  354.     if (direction)
  355.         current_msg = (current_msg+1) % msg_cnt;
  356.     else
  357.         if (--current_msg < 0)
  358.         current_msg = msg_cnt-1;
  359.         p = compose_hdr(current_msg);
  360. #ifdef REGCMP
  361.     val = !!regex(err, p, NULL); /* convert value to a boolean */
  362. #else /* REGCMP */
  363.     val = re_exec(p);
  364. #endif /* REGCMP */
  365.     if (val == -1)     /* doesn't apply in system V */
  366.         print("Internal error for pattern search.\n");
  367.     } while (!val && current_msg != this_msg && isoff(glob_flags, WAS_INTR));
  368.  
  369.     if (ison(glob_flags, WAS_INTR)) {
  370.     print("Pattern search interrupted.");
  371.     current_msg = this_msg;
  372.     } else if (val == 0)
  373.     print("Pattern not found.");
  374.  
  375.     off_intr();
  376.     return val;
  377. }
  378. #endif /* CURSES */
  379.  
  380. /*
  381.  * parse a user given date string and set mdy[] array with correct
  382.  * values.  Return 0 on failure.
  383.  */
  384. date1(p)
  385. register char *p;
  386. {
  387.     register char *p2;
  388.     long      t;
  389.     int       i;
  390.     struct tm       *today;
  391.  
  392.     if (*p == '-' || *p == '+') {
  393.     before = !(after = *p == '+');
  394.     skipspaces(1);
  395.     }
  396.     if (!isdigit(*p) && *p != '/') {
  397.     print("syntax error on date: \"%s\"\n", p);
  398.     return 0;
  399.     }
  400.     (void) time (&t);
  401.     today = localtime(&t);
  402.     for (i = 0; i < 3; i++)
  403.     if (!p || !*p || *p == '/') {
  404.         switch(i) {   /* default to today's date */
  405.         case 0: mdy[0] = today->tm_mon;
  406.         when 1: mdy[1] = today->tm_mday;
  407.         when 2: mdy[2] = today->tm_year;
  408.         }
  409.         if (p && *p)
  410.         p++;
  411.     } else {
  412.         p2 = (*p)? index(p+1, '/') : NULL;
  413.         mdy[i] = atoi(p); /* atoi will stop at the '/' */
  414.         if (i == 0 && (--(mdy[0]) < 0 || mdy[0] > 11)) {
  415.         print("Invalid month: %s\n", p);
  416.         return 0;
  417.         } else if (i == 1 && (mdy[1] < 1 || mdy[1] > 31)) {
  418.         print("Invalid day: %s\n", p);
  419.         return 0;
  420.         }
  421.         if (p = p2) /* set p to p2 and check to see if it's valid */
  422.         p++;
  423.     }
  424.     return 1;
  425. }
  426.  
  427. /*
  428.  * Parse arguments specifying days/months/years "ago" (relative to today).
  429.  * Legal syntax: -ago [+-][args]
  430.  *    where "args" is defined to be:
  431.  *    [0-9]+[ ]*[dD][a-Z]*[ ,]*[0-9]+[mM][a-Z]*[ ,]*[0-9]+[ ]*[yY][a-Z]*
  432.  *    1 or more digits, 0 or more spaces, d or D followed by 0 or more chars,
  433.  *    0 or more whitespaces or commas, repeat for months and years...
  434.  * Examples:
  435.  *    1 day, 2 months, 0 years
  436.  *    2 weeks 1 year
  437.  *    10d, 5m
  438.  *    3w
  439.  *    1d 1Y
  440.  *
  441.  * Return number of args parsed; -1 on error.
  442.  */
  443. ago_date(argv)
  444. char **argv;
  445. {
  446. #define SECS_PER_DAY   (60 * 60 * 24)
  447. #define SECS_PER_WEEK  (SECS_PER_DAY * 7)
  448. #define SECS_PER_MONTH ((int)(SECS_PER_DAY * 30.5))
  449. #define SECS_PER_YEAR  (SECS_PER_DAY * 365)
  450.     register char *p;
  451.     char       buf[256];
  452.     int           n = 0, value;
  453.     long       t;
  454.     struct tm       *today;
  455.  
  456.     (void) argv_to_string(buf, argv);
  457.     p = buf;
  458.     (void) time (&t); /* get current time in seconds and subtract new values */
  459.     if (*p == '-')
  460.     before = TRUE;
  461.     else if (*p == '+')
  462.     after = TRUE;
  463.     skipspaces(before || after);
  464.     while (*p) {
  465.     if (!isdigit(*p))
  466.         break; /* really a syntax error, but it could be other pick args */
  467.     p = my_atoi(p, &value); /* get 1 or more digits */
  468.     skipspaces(0); /* 0 or more spaces */
  469.     switch (lower(*p)) {   /* d, m, or y */
  470.         case 'd' : t -= value * SECS_PER_DAY;
  471.         when 'w' : t -= value * SECS_PER_WEEK;
  472.         when 'm' : t -= value * SECS_PER_MONTH;
  473.         when 'y' : t -= value * SECS_PER_YEAR;
  474.         otherwise: return -1;
  475.     }
  476.     for (p++; Lower(*p) >= 'a' && *p <= 'z'; p++)
  477.         ; /* skip the rest of this token */
  478.     while (*p == ',' || isspace(*p))
  479.         ; /* 0 or more whitespaces or commas */
  480.     }
  481.     today = localtime(&t);
  482.     mdy[0] = today->tm_mon;
  483.     mdy[1] = today->tm_mday;
  484.     mdy[2] = today->tm_year;
  485.  
  486.     /* Count the number of args parsed */
  487.     for (n = 0; p > buf && *argv; n++)
  488.     p -= (strlen(*argv++)+1);
  489.     Debug("parsed %d args\n", n);
  490.     return n;
  491. }
  492.