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 >
Wrap
C/C++ Source or Header
|
1992-10-30
|
18KB
|
644 lines
/* @(#)pick.c 7.2 (c) copyright 5/19/91 (Dan Heller) */
#include "mush.h"
static int before, after, search_from, search_subj, search_to, xflg, icase;
static u_long match_priority;
static char search_hdr[64];
static int mdy[3];
static int pick();
static void month_day_year();
do_pick(n, argv, list)
register int n;
register char **argv, list[];
{
char ret_list[MAXMSGS_BITS];
if (n > 1 && !strcmp(argv[1], "-?"))
return help(0, "pick", cmd_help);
clear_msg_list(ret_list);
/* if is_pipe, then the messages to search for are already set.
* if not piped, then reverse the bits for all message numbers.
* That is, search EACH message. only those matching will be returned.
*/
if (isoff(glob_flags, IS_PIPE))
bitput(ret_list, list, msg_cnt, =~); /* macro, turn on all bits */
/* Use n temporarily as verbosity flag */
n = (!chk_option("quiet", "pick") &&
isoff(glob_flags, DO_PIPE));
if ((n = pick(argv, list, ret_list, n)) == -1)
return -1;
if (istool && isoff(glob_flags, DO_PIPE))
print("%d matches:\n", n);
for (n = 0; n < msg_cnt; n++)
if (msg_bit(ret_list, n)) {
if (isoff(glob_flags, DO_PIPE))
if (istool)
print_more("%d ", n+1);
else
print("%s\n", compose_hdr(n));
set_msg_bit(list, n);
} else
unset_msg_bit(list, n);
return 0;
}
/*
* search for messages. Return the number of matches. Errors such
* as internal errors or syntax errors, return -1.
* "head" and "tail" are specified using +<num> or -<num> as args.
* Both can be specified and the order is significant.
* pick +5 -3
* returns the last three of the first five matches.
* pick -3 +2
* returns the first two of the last three matches.
*/
static int
pick(argv, list, ret_list, verbose)
register char **argv, list[], ret_list[];
{
register char c;
int matches = 0;
char pattern[256];
short head_first, head_cnt, tail_cnt, search = TRUE;
int n;
if (!msg_cnt) {
print("No Messages.\n");
return -1;
}
head_first = TRUE;
head_cnt = tail_cnt = -1;
match_priority = 0;
icase = before = after = search_from = search_subj = search_to = xflg = 0;
pattern[0] = mdy[0] = mdy[1] = search_hdr[0] = 0;
while (*argv && *++argv && (**argv == '-' || **argv == '+'))
if (**argv == '+' || isdigit(argv[0][1])) {
if (**argv == '+')
head_cnt = atoi(&argv[0][1]);
else {
tail_cnt = atoi(&argv[0][1]);
if (head_cnt == -1)
head_first = FALSE;
}
if (head_cnt == 0 || tail_cnt == 0) {
print("pick: invalid head/tail number: %s\n", &argv[0][1]);
clear_msg_list(ret_list);
return -1;
}
} else if ((c = argv[0][1]) == 'e') {
if (!*++argv) {
print("usage: -e expression...\n");
return -1;
}
break;
} else switch (c) {
/* user specifies a range */
case 'r': {
int X = 2;
/* if not a pipe, then clear all bits cuz we only want
* to search the message specified here...
* If it is a pipe, then add to the messages searched for.
*/
if (isoff(glob_flags, IS_PIPE))
clear_msg_list(list);
/* "-r10-15"
* ^argv[1][2] if NULL, then
* list detached from "r" e.g. "-r" "5-20"
*/
if (!argv[0][X])
argv++, X = 0;
(*argv) += X;
n = get_msg_list(argv, list);
(*argv) -= X;
if (n == -1)
return -1;
argv += (n-1); /* we're going to increment another up top */
}
when 'a': {
if ((n = ago_date(++argv)) == -1)
return -1;
argv += n;
}
when 'd':
if (!*++argv) {
print("Specify a date for -%c\n", c);
return -1;
}
if (!date1(*argv))
return -1;
when 's' : case 'f': case 't': case 'h':
if (search_subj + search_from + search_to + *search_hdr > 1) {
print("Specify one of `s', `f', `t' or `h' only\n");
return -1;
}
if (c == 's')
search_subj = 1;
else if (c == 'f')
search_from = 1;
else if (c == 'h')
if (!*++argv)
print("Specify header to search for.\n");
else
(void) lcase_strcpy(search_hdr, *argv);
else
search_to = 1;
when 'p' : /* Select on priority field */
if (!*++argv || (c = upper(**argv)) < 'A' ||
c > MAX_PRIORITY + 'A') {
print("pick: invalid priority: %s\n", argv[0]);
clear_msg_list(ret_list);
return -1;
}
turnon(match_priority, M_PRIORITY(c - 'A' + 1));
when 'x' : xflg = 1;
when 'i' : icase = 1;
otherwise:
print("pick: unknown flag: %c\n", argv[0][1]);
clear_msg_list(ret_list);
return -1;
}
if (xflg && head_cnt + tail_cnt >= 0) {
print("Can't specify -x and head/tail options together.\n");
return -1;
}
if (!mdy[1]) {
(void) argv_to_string(pattern, argv);
if (pattern[0] == '\0' && match_priority == 0 &&
head_cnt + tail_cnt < 0) {
print("No pattern specified\n");
clear_msg_list(ret_list); /* doesn't matter really */
return -1;
}
}
search = (pattern[0] || mdy[1] || match_priority > 0);
if (verbose) {
if (head_cnt + tail_cnt >= 0) {
print("Finding the ");
if (head_cnt > 0) {
if (head_first)
if (tail_cnt == -1)
print_more("first %d message%s",
head_cnt, head_cnt > 1? "s" : "");
else
print_more("last %d message%s",
tail_cnt, tail_cnt > 1? "s" : "");
else /* there must be a tail_cnt and it comes first */
print_more("first %d message%s",
head_cnt, head_cnt > 1? "s" : "");
} else
print_more("last %d message%s",
tail_cnt, tail_cnt > 1? "s" : "");
if (tail_cnt > 0 && head_cnt > 0)
if (head_first)
print_more(" of the first %d", head_cnt);
else
print_more(" of the last %d", tail_cnt);
} else
print_more("Searching for %smessages",
match_priority > 0 ? "priority " : "");
if (!search) {
if (tail_cnt > 0 && head_cnt > 0)
print_more(" messages");
if (ison(glob_flags, IS_PIPE))
print_more(" from the input list");
} else if (pattern[0]) {
print_more(" that %scontain \"%s\"", (xflg)? "do not ": "",
pattern);
if (search_subj)
print_more(" in subject line");
else if (search_from)
print_more(" from author names");
else if (search_to)
print_more(" from the To: field");
else if (search_hdr[0])
print_more(" from the message header \"%s:\"", search_hdr);
} else if (mdy[1]) {
extern char *month_names[]; /* from dates.c */
print_more("%s dated ", xflg && !(before || after)? " not" : "");
if (before || after)
if (xflg)
print_more("%s ", (!before)? "before": "after");
else
print_more("on or %s ", (before)? "before": "after");
print_more("%s. %d, %d",
month_names[mdy[0]], mdy[1], mdy[2] + 1900);
}
print_more(".\n");
}
if (mdy[1] > 0 && icase)
print("using date: -i flag ignored.\n");
if (!search) {
for (n = 0; n < msg_cnt && (!head_first || matches < head_cnt); n++)
if (msg_bit(list, n))
++matches, set_msg_bit(ret_list, n);
} else
matches = find_pattern(head_first? head_cnt : msg_cnt,
pattern, list, ret_list);
if (xflg && matches >= 0) {
/* invert items in ret_list that also appear in list */
bitput(list, ret_list, msg_cnt, ^=);
/* there should be a faster way to do this count ... */
for (matches = n = 0; n < msg_cnt; n++)
if (msg_bit(ret_list, n))
++matches;
}
Debug("matches = %d\n", matches);
if (!matches)
return 0;
/* ok, the list we've got is a list of matched messages. If "tailing"
* is set, reduce the number of matches to at least tail_cnt.
*/
if (tail_cnt >= 0)
for (n = 0; n < msg_cnt && matches > tail_cnt; n++)
if (msg_bit(ret_list, n)) {
Debug("tail: dropping %d\n", n+1);
unset_msg_bit(ret_list, n);
matches--;
}
/* if tailing came before heading, we need to do the heading now. */
if (!head_first && head_cnt >= 0)
for (n = 0; n < msg_cnt; n++)
if (msg_bit(ret_list, n))
if (head_cnt > 0)
head_cnt--;
else {
unset_msg_bit(ret_list, n);
matches--;
}
return matches;
}
/*
* find_pattern will search thru all the messages set in the check_list
* until the list runs out or "cnt" has been exhasted. ret_list contains
* the list of messages which have matched the pattern.
* return -1 for internal error or # of pattern matches.
*/
find_pattern(cnt, p, check_list, ret_list)
int cnt;
register char *p;
char check_list[], ret_list[];
{
register int n, val, i; /* val is return value from regex or re_exec */
int matches = 0;
long bytes = 0;
char buf[HDRSIZ];
char *err = NULL;
#ifdef REGCMP
char *regcmp(), *regex();
#else /* REGCMP */
char *re_comp();
#endif /* REGCMP */
if (p && *p == '\\')
p++; /* take care of escaping special cases (`-', `\') */
/* specify what we're looking for */
if (p && *p) {
if (icase)
p = lcase_strcpy(buf, p);
#ifdef REGCMP
if (p && !(err = regcmp(p, NULL))) {
print("regcmp error: %s\n", p);
clear_msg_list(ret_list);
return -1;
}
#else /* REGCMP */
if (err = re_comp(p)) {
print("re_comp error: %s\n", err);
clear_msg_list(ret_list);
return -1;
}
#endif /* REGCMP */
} else if (err == NULL && mdy[1] <= 0 && match_priority == 0) {
print("No previous regular expression\n");
clear_msg_list(ret_list); /* doesn't matter really */
return -1;
}
/* start searching: set bytes, and message number: n */
for (n = 0; cnt && n < msg_cnt; n++)
if (msg_bit(check_list, n)) {
if (match_priority > 0) {
if (msg[n].m_flags & match_priority)
++matches, set_msg_bit(ret_list, n);
continue;
}
if (mdy[1] > 0) {
int msg_mdy[3];
if (ison(glob_flags, DATE_RECV))
p = msg[n].m_date_recv;
else
p = msg[n].m_date_sent;
/* Ick -- fix this mdy thing asap */
month_day_year(p, &msg_mdy[0], &msg_mdy[1], &msg_mdy[2]);
Debug("checking %d's date: %d-%d-%d ",
n+1, msg_mdy[0]+1, msg_mdy[1], msg_mdy[2]);
/* start at year and wrap around.
* only when match the day (4), check for == (match)
*/
for (i = 2; i < 5; i++)
if (before && msg_mdy[i%3] < mdy[i%3]
|| after && msg_mdy[i%3] > mdy[i%3]
|| i == 4 && (msg_mdy[i%3] == mdy[i%3])) {
Debug("matched (%s).\n",
(i == 2)? "year" : (i == 3)? "month" : "day");
set_msg_bit(ret_list, n);
cnt--, matches++;
break;
} else if (msg_mdy[i%3] != mdy[i%3]) {
Debug("failed.\n");
break;
}
continue;
}
/* we must have the right date -- if we're searching for a
* string, find it.
*/
(void) msg_get(n, NULL, 0);
bytes = 0;
while (bytes < msg[n].m_size) {
if (!search_subj && !search_from && !search_to &&
!*search_hdr && !(p = fgets(buf, sizeof buf, tmpf)))
break;
else if (search_subj) {
if (!(p = header_field(n, "subject")))
break;
} else if (search_from) {
if (!(p = header_field(n, "from"))) {
/*
* Check for MSG_SEPARATOR here? Maybe not...
*/
register char *p2;
(void) msg_get(n, NULL, 0);
if (!(p2 = fgets(buf, sizeof buf, tmpf)) ||
!(p = index(p2, ' ')))
continue;
p++;
if (p2 = any(p, " \t"))
*p2 = 0;
}
} else if (search_to) {
if (!(p = header_field(n, "to")) &&
!(p = header_field(n, "apparently-to")))
break;
} else if (*search_hdr) {
if (!(p = header_field(n, search_hdr)))
break;
}
if (icase)
p = lcase_strcpy(buf, p);
#ifdef REGCMP
val = !!regex(err, p, NULL); /* convert value to a boolean */
#else /* REGCMP */
val = re_exec(p);
#endif /* REGCMP */
if (val == -1) { /* doesn't apply in system V */
print("Internal error for pattern search.\n");
clear_msg_list(ret_list); /* it doesn't matter, really */
return -1;
}
if (val) {
set_msg_bit(ret_list, n);
cnt--, matches++;
break;
}
if (search_subj || search_from || search_to || *search_hdr)
break;
else
bytes += strlen(p);
}
}
#ifdef REGCMP
if (err)
free(err);
#endif /* REGCMP */
return matches;
}
#ifdef CURSES
/*
* search for a pattern in composed message headers -- also see next function
* flags == 0 forward search (prompt).
* flags == -1 continue search (no prompt).
* flags == 1 backward search (prompt).
*/
search(flags)
register int flags;
{
register char *p;
char pattern[128];
register int this_msg = current_msg, val = 0;
static char *err = (char *)-1, direction;
SIGRET (*oldint)(), (*oldquit)();
#ifdef REGCMP
char *regex(), *regcmp();
#else /* REGCMP */
char *re_comp();
#endif /* REGCMP */
if (msg_cnt <= 1) {
print("Not enough messages to invoke a search.\n");
return 0;
}
pattern[0] = '\0';
if (flags == -1)
print("continue %s search...", direction? "forward" : "backward");
else
print("%s search: ", flags? "backward" : "forward");
if (flags > -1)
if (Getstr(pattern, COLS-18, 0) < 0)
return 0;
else
direction = !flags;
#ifdef REGCMP
if (err != (char *)-1 && *pattern)
xfree(err);
else if (err == (char *)-1 && !*pattern) {
print("No previous regular expression.");
return 0;
}
if (*pattern && !(err = regcmp(pattern, NULL))) {
print("Error in regcmp in %s", pattern);
return 0;
}
#else /* REGCMP */
if (err = re_comp(pattern)) {
print(err);
return 0;
}
#endif /* REGCMP */
move(LINES-1, 0), refresh();
on_intr();
do {
if (direction)
current_msg = (current_msg+1) % msg_cnt;
else
if (--current_msg < 0)
current_msg = msg_cnt-1;
p = compose_hdr(current_msg);
#ifdef REGCMP
val = !!regex(err, p, NULL); /* convert value to a boolean */
#else /* REGCMP */
val = re_exec(p);
#endif /* REGCMP */
if (val == -1) /* doesn't apply in system V */
print("Internal error for pattern search.\n");
} while (!val && current_msg != this_msg && isoff(glob_flags, WAS_INTR));
if (ison(glob_flags, WAS_INTR)) {
print("Pattern search interrupted.");
current_msg = this_msg;
} else if (val == 0)
print("Pattern not found.");
off_intr();
return val;
}
#endif /* CURSES */
/*
* Get just the month, day, and year from a date.
* This is a temporary measure until the date compares in pick()
* can be overhauled. It really should be in dates.c, but ...
*/
static
void
month_day_year(date, month, day, year)
char *date;
int *month, *day, *year;
{
long gmt;
char unused[4], zone[8];
struct tm *t;
extern long getzoff();
(void) sscanf(date, "%ld%3c%s", &gmt, unused, zone);
gmt += getzoff(zone);
t = gmtime(&gmt);
*month = t->tm_mon;
*day = t->tm_mday;
*year = t->tm_year;
}
/*
* parse a user given date string and set mdy[] array with correct
* values. Return 0 on failure.
*/
date1(p)
register char *p;
{
register char *p2;
long t;
int i;
struct tm *today;
if (*p == '-' || *p == '+') {
before = !(after = *p == '+');
skipspaces(1);
}
if (!isdigit(*p) && *p != '/') {
print("syntax error on date: \"%s\"\n", p);
return 0;
}
(void) time (&t);
today = localtime(&t);
for (i = 0; i < 3; i++)
if (!p || !*p || *p == '/') {
switch(i) { /* default to today's date */
case 0: mdy[0] = today->tm_mon;
when 1: mdy[1] = today->tm_mday;
when 2: mdy[2] = today->tm_year;
}
if (p && *p)
p++;
} else {
p2 = (*p)? index(p+1, '/') : NULL;
mdy[i] = atoi(p); /* atoi will stop at the '/' */
if (i == 0 && (--(mdy[0]) < 0 || mdy[0] > 11)) {
print("Invalid month: %s\n", p);
return 0;
} else if (i == 1 && (mdy[1] < 1 || mdy[1] > 31)) {
print("Invalid day: %s\n", p);
return 0;
}
if (p = p2) /* set p to p2 and check to see if it's valid */
p++;
}
return 1;
}
/*
* Parse arguments specifying days/months/years "ago" (relative to today).
* Legal syntax: -ago [+-][args]
* where "args" is defined to be:
* [0-9]+[ ]*[dD][a-Z]*[ ,]*[0-9]+[mM][a-Z]*[ ,]*[0-9]+[ ]*[yY][a-Z]*
* 1 or more digits, 0 or more spaces, d or D followed by 0 or more chars,
* 0 or more whitespaces or commas, repeat for months and years...
* Examples:
* 1 day, 2 months, 0 years
* 2 weeks 1 year
* 10d, 5m
* 3w
* 1d 1Y
*
* Return number of args parsed; -1 on error.
*/
ago_date(argv)
char **argv;
{
#define SECS_PER_DAY (60 * 60 * 24)
#define SECS_PER_WEEK (SECS_PER_DAY * 7)
#define SECS_PER_MONTH ((int)(SECS_PER_DAY * 30.5))
#define SECS_PER_YEAR (SECS_PER_DAY * 365)
register char *p;
char buf[256];
int n = 0, value;
long t;
struct tm *today;
(void) argv_to_string(buf, argv);
p = buf;
(void) time (&t); /* get current time in seconds and subtract new values */
if (*p == '-')
before = TRUE;
else if (*p == '+')
after = TRUE;
skipspaces(before || after);
while (*p) {
if (!isdigit(*p)) {
p -= 2;
break; /* really a syntax error, but it could be other pick args */
}
p = my_atoi(p, &value); /* get 1 or more digits */
skipspaces(0); /* 0 or more spaces */
switch (lower(*p)) { /* d, m, or y */
case 'd' : t -= value * SECS_PER_DAY;
when 'w' : t -= value * SECS_PER_WEEK;
when 'm' : t -= value * SECS_PER_MONTH;
when 'y' : t -= value * SECS_PER_YEAR;
otherwise: return -1;
}
for (p++; Lower(*p) >= 'a' && *p <= 'z'; p++)
; /* skip the rest of this token */
while (*p == ',' || isspace(*p))
++p; /* 0 or more whitespaces or commas */
}
today = localtime(&t);
mdy[0] = today->tm_mon;
mdy[1] = today->tm_mday;
mdy[2] = today->tm_year;
/* Count the number of args parsed */
for (n = 0; p > buf && *argv; n++)
p -= (strlen(*argv++)+1);
Debug("parsed %d args\n", n);
return n;
}