home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-05-06 | 54.3 KB | 1,979 lines |
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # The tool that generated this appeared in the comp.sources.unix newsgroup;
- # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
- # If this archive is complete, you will see the following message at the end:
- # "End of archive 5 (of 19)."
- # Contents: mush/loop.c mush/pick.c
- # Wrapped by argv@turnpike on Wed May 2 13:59:22 1990
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'mush/loop.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'mush/loop.c'\"
- else
- echo shar: Extracting \"'mush/loop.c'\" \(34930 characters\)
- sed "s/^X//" >'mush/loop.c' <<'END_OF_FILE'
- X/* loop.c (c) copyright 1986 (Dan Heller) */
- X
- X/*
- X * Here is where the main loop for text mode exists. Also, all the
- X * history is kept here and all the command parsing and execution
- X * and alias expansion in or out of text/graphics mode is done here.
- X */
- X
- X#include "mush.h"
- X#include "version.h"
- X
- X#ifdef BSD
- X#include <sys/wait.h>
- X#else
- X#ifndef SYSV
- X#include <wait.h>
- X#endif /* SYSV */
- X#endif /* BSD */
- X
- X#define ever (;;)
- X#define MAXARGS 100
- X#define isdelimeter(c) (index(" \t;|", c))
- X
- Xchar *alias_expand(), *hist_expand(), *reference_hist(), *hist_from_str();
- Xchar *calloc();
- X
- Xstruct history {
- X int histno;
- X char **argv;
- X struct history *prev;
- X struct history *next;
- X};
- Xstatic struct history *hist_head, *hist_tail;
- X#define malloc(n) (struct history *)calloc((unsigned)1,(unsigned)(n))
- X#define NULL_HIST (struct history *)0
- X
- Xstatic char *last_aliased;
- Xstatic int hist_size, print_only;
- X
- Xdo_loop()
- X{
- X register char *p, **argv;
- X char **last_argv = DUBL_NULL, line[256];
- X int argc, c = (iscurses - 1);
- X#ifdef CURSES
- X int save_echo_flg = FALSE;
- X#endif /* CURSES */
- X
- X /* catch the right signals -- see main.c for other signal catching */
- X (void) signal(SIGINT, catch);
- X (void) signal(SIGQUIT, catch);
- X (void) signal(SIGHUP, catch);
- X#ifndef SUNTOOL
- X (void) signal(SIGTERM, catch);
- X (void) signal(SIGCHLD,
- X#ifndef SYSV
- X sigchldcatcher
- X#else /* SYSV */
- X SIG_DFL
- X#endif /* SYSV */
- X );
- X#endif /* SUNTOOL*/
- X
- X turnoff(glob_flags, IGN_SIGS);
- X if (hist_size == 0) /* if user didn't set history in .rc file */
- X hist_size = 1;
- X
- X for ever {
- X if (setjmp(jmpbuf)) {
- X Debug("jumped back to main loop (%s: %d)\n", __FILE__,__LINE__);
- X#ifdef CURSES
- X if (c > 0) { /* don't pass last command back to curses_command() */
- X iscurses = TRUE;
- X c = hit_return();
- X }
- X#endif /* CURSES */
- X }
- X#ifdef CURSES
- X if (iscurses || c > -1) {
- X /* if !iscurses, we know that we returned from a curses-based
- X * call and we really ARE still in curses. Reset tty modes!
- X */
- X if (ison(glob_flags, ECHO_FLAG)) {
- X turnoff(glob_flags, ECHO_FLAG);
- X echo_off();
- X save_echo_flg = TRUE;
- X }
- X if (!iscurses) {
- X iscurses = TRUE;
- X c = hit_return();
- X }
- X if (c < 0)
- X c = 0;
- X if ((c = curses_command(c)) == -1 && save_echo_flg) {
- X echo_on();
- X turnon(glob_flags, ECHO_FLAG);
- X save_echo_flg = FALSE;
- X }
- X continue;
- X }
- X#endif /* CURSES */
- X clear_msg_list(msg_list);
- X (void) check_new_mail();
- X
- X /* print a prompt according to printf like format:
- X * (current message, deleted, unread, etc) are found in mail_status.
- X */
- X mail_status(1);
- X if (Getstr(line, sizeof(line), 0) > -1)
- X p = line;
- X else {
- X if (isatty(0) && (p = do_set(set_options, "ignoreeof"))) {
- X if (!*p)
- X continue;
- X else
- X p = strcpy(line, p); /* so processing won't destroy var */
- X } else {
- X putchar('\n');
- X (void) mush_quit(0, DUBL_NULL);
- X continue; /* quit may return if new mail arrives */
- X }
- X }
- X
- X skipspaces(0);
- X if (!*p && !(p = do_set(set_options, "newline"))) {
- X (void) readmsg(0, DUBL_NULL, msg_list);
- X continue;
- X }
- X if (!*p) /* if newline is set, but no value, then continue */
- X continue;
- X
- X /* upon error, argc = -1 -- still save in history so user can
- X * modify syntax error. if !argv, error is too severe. We pass
- X * the last command typed in last_argv for history reference, and
- X * get back the current command _as typed_ (unexpanded by aliases
- X * or history) in last_argv.
- X */
- X if (!(argv = make_command(p, &last_argv, &argc)))
- X continue;
- X /* now save the old argv in a newly created history structure */
- X (void) add_history(0, last_argv); /* argc is currently ignored */
- X
- X if (print_only) {
- X print_only = 0;
- X free_vec(argv);
- X } else if (argc > -1)
- X (void) do_command(argc, argv, msg_list);
- X }
- X}
- X
- X/* Add a command to the history list
- X */
- X/*ARGSUSED*/
- Xadd_history(un_used, argv)
- Xchar **argv;
- X{
- X struct history *new;
- X
- X if (!(new = malloc(sizeof (struct history))))
- X error("can't increment history");
- X else {
- X new->histno = ++hist_no;
- X new->argv = argv; /* this is the command _as typed_ */
- X new->next = NULL_HIST;
- X new->prev = hist_head;
- X /* if first command, the tail of the list is "new" because
- X * nothing is in the list. If not the first command, the
- X * head of the list's "next" pointer points to the new command.
- X */
- X if (hist_head)
- X hist_head->next = new;
- X else
- X hist_tail = new;
- X hist_head = new;
- X }
- X /*
- X * truncate the history list to the size of the history.
- X * Free the outdated command (argv) and move the tail closer to front.
- X * use a while loop in case the last command reset histsize to "small"
- X */
- X while (hist_head->histno - hist_tail->histno >= hist_size) {
- X hist_tail = hist_tail->next;
- X free_vec(hist_tail->prev->argv);
- X xfree((char *) (hist_tail->prev));
- X hist_tail->prev = NULL_HIST;
- X }
- X}
- X
- X/* make a command from "buf".
- X * first, expand history references. make an argv from that and save
- X * in last_argv (to be passed back and stored in history). After that,
- X * THEN expand aliases. return that argv to be executed as a command.
- X */
- Xchar **
- Xmake_command(start, last_argv, argc)
- Xregister char *start, ***last_argv;
- Xint *argc;
- X{
- X register char *p, **tmp;
- X char buf[BUFSIZ];
- X
- X if (!last_argv)
- X tmp = DUBL_NULL;
- X else
- X tmp = *last_argv;
- X /* first expand history -- (here's where argc gets set)
- X * pass the buffer, the history list to reference if \!* (or whatever)
- X * result in static buffer (pointed to by p) -- even if history parsing is
- X * ignored, do this to remove \'s behind !'s and verifying matching quotes
- X */
- X if (!(p = hist_expand(start, tmp, argc)) || Strcpy(buf, p) > sizeof buf)
- X return DUBL_NULL;
- X /* if history was referenced in the command, echo new command */
- X if (*argc)
- X puts(buf);
- X
- X /* argc may == -1; ignore this error for now but catch it later */
- X if (!(tmp = mk_argv(buf, argc, 0)))
- X return DUBL_NULL;
- X
- X /* save this as the command typed */
- X if (last_argv)
- X *last_argv = tmp;
- X
- X /* expand all aliases (recursively)
- X * pass _this_ command (as typed and without aliases) to let aliases
- X * with "!*" be able to reference the command line just typed.
- X */
- X if (alias_stuff(buf, *argc, tmp) == -1)
- X return DUBL_NULL;
- X
- X if (!last_argv)
- X free_vec(tmp);
- X
- X /* with everything expanded, build final argv from new buffer
- X * Note that backslashes and quotes still exist. Those are removed
- X * because argument final is 1.
- X */
- X tmp = mk_argv(buf, argc, 1);
- X return tmp;
- X}
- X
- X/* Return values from commands, see check_internal() */
- Xstatic int last_status; /* Changes after every command */
- Xstatic char last_output[MAXMSGS]; /* Changes after SUCCESSFUL command */
- X
- X/*
- X * do the command specified by the argument vector, argv.
- X * First check to see if argc < 0. If so, someone called this
- X * command and they should not have! make_command() will return
- X * an argv but it will set argc to -1 if there's a syntax error.
- X */
- Xdo_command(argc, argv, list)
- Xchar **argv, list[];
- X{
- X register char *p;
- X char **tmp = argv, *next_cmd = NULL;
- X int i, status = 0;
- X long do_pipe = ison(glob_flags, DO_PIPE);
- X
- X if (argc <= 0) {
- X turnoff(glob_flags, DO_PIPE);
- X return -1;
- X }
- X
- X clear_msg_list(list);
- X
- X for (i = 0; do_pipe >= 0 && argc; argc--) {
- X p = argv[i];
- X /* mk_argv inserts a boolean in argv[i][2] for separators */
- X if ((!strcmp(p, "|") || !strcmp(p, ";")) && p[2]) {
- X if (do_pipe = (*p == '|'))
- X turnon(glob_flags, DO_PIPE);
- X else if (next_cmd = argv[i+1])
- X argv[i+1] = NULL, argc--;
- X argv[i] = NULL;
- X if ((status = exec_argv(i, argv, list)) <= -1)
- X mac_flush();
- X else
- X list_to_str(list, last_output);
- X turnon(glob_flags, IGN_SIGS); /* prevent longjmp */
- X /* if piping, then don't call next command if this one failed. */
- X if (status <= -1 && do_pipe) {
- X print("Broken pipe.\n");
- X do_pipe = -1, turnoff(glob_flags, DO_PIPE);
- X }
- X last_status = status;
- X /* if command failed and piping, or command worked and not piping */
- X if (do_pipe <= 0)
- X status = 0, clear_msg_list(list);
- X /* else command worked and piping: set is_pipe */
- X else if (!status)
- X turnon(glob_flags, IS_PIPE), turnoff(glob_flags, DO_PIPE);
- X argv[i] = p;
- X argv += (i+1);
- X i = 0;
- X turnoff(glob_flags, IGN_SIGS);
- X } else
- X i++;
- X }
- X if (*argv && do_pipe >= 0) {
- X status = exec_argv(i, argv, list);
- X turnon(glob_flags, IGN_SIGS);
- X if (status < 0) {
- X mac_flush();
- X } else
- X list_to_str(list, last_output);
- X last_status = status;
- X }
- X Debug("freeing: "), print_argv(tmp);
- X free_vec(tmp);
- X turnoff(glob_flags, DO_PIPE), turnoff(glob_flags, IS_PIPE);
- X if (next_cmd) {
- X if (tmp = mk_argv(next_cmd, &argc, 1)) {
- X turnoff(glob_flags, IGN_SIGS);
- X status = do_command(argc, tmp, list);
- X turnon(glob_flags, IGN_SIGS);
- X } else
- X status = argc;
- X xfree(next_cmd);
- X }
- X turnoff(glob_flags, IGN_SIGS);
- X return status;
- X}
- X
- Xexec_argv(argc, argv, list)
- Xregister char **argv, list[];
- X{
- X register int n;
- X
- X if (!argv || !*argv || argv[0][0] == '\\' && !argv[0][1]) {
- X if (ison(glob_flags, IS_PIPE))
- X print("Invalid null command.\n");
- X else if (ison(glob_flags, DO_PIPE)) {
- X set_msg_bit(list, current_msg);
- X return 0;
- X }
- X return -1;
- X } else if (argv[0][0] == '\\') {
- X /* Can't change *argv (breaks free_vec),
- X * so shift to remove the backslash
- X */
- X for (n = 0; argv[0][n]; n++)
- X argv[0][n] = argv[0][n+1];
- X }
- X Debug("executing: "), print_argv(argv);
- X
- X /* if interrupted during execution of a command, return -1 */
- X if (isoff(glob_flags, IGN_SIGS) && setjmp(jmpbuf)) {
- X Debug("jumped back to exec_argv (%s: %d)\n", __FILE__, __LINE__);
- X return -1;
- X }
- X
- X /* standard commands */
- X for (n = 0; cmds[n].command; n++)
- X if (!strcmp(argv[0], cmds[n].command))
- X return (*cmds[n].func)(argc, argv, list);
- X
- X /* ucb-Mail compatible commands */
- X for (n = 0; ucb_cmds[n].command; n++)
- X if (!strcmp(argv[0], ucb_cmds[n].command))
- X return (*ucb_cmds[n].func)(argc, argv, list);
- X
- X /* for hidden, undocumented commands */
- X for (n = 0; hidden_cmds[n].command; n++)
- X if (!strcmp(argv[0], hidden_cmds[n].command))
- X return (*hidden_cmds[n].func)(argc, argv, list);
- X
- X n = -1; /* default to failure */
- X if ((isdigit(**argv) || index("^.*$-`{}", **argv))
- X && (n = get_msg_list(argv, list)) != 0) {
- X if (n < 0)
- X return -1;
- X else if (isoff(glob_flags, DO_PIPE))
- X for (n = 0; n < msg_cnt; n++)
- X if (msg_bit(list, n)) {
- X display_msg((current_msg = n), (long)0);
- X unset_msg_bit(list, n);
- X }
- X return 0;
- X } else {
- X /* get_msg_list will set the current message bit if nothing parsed */
- X if (n == 0)
- X unset_msg_bit(list, current_msg);
- X if (strlen(*argv) == 1 && index("$^.", **argv)) {
- X if (!msg_cnt) {
- X print("No messages.");
- X return -1;
- X } else {
- X if (**argv != '.')
- X current_msg = (**argv == '$') ? msg_cnt-1 : 0;
- X set_msg_bit(list, current_msg);
- X display_msg(current_msg, (long)0);
- X }
- X return 0;
- X }
- X }
- X
- X if (!istool && do_set(set_options, "unix")) {
- X if (ison(glob_flags, IS_PIPE)) {
- X return pipe_msg(argc, argv, list);
- X } else
- X execute(argv); /* try to execute a unix command */
- X return -1; /* doesn't affect messages! */
- X }
- X
- X print("%s: command not found.\n", *argv);
- X if (!istool)
- X print("type '?' for valid commands, or type `help'\n");
- X return -1;
- X}
- X
- X/* recursively look for aliases on a command line. aliases may
- X * reference other aliases.
- X */
- Xalias_stuff(b, argc, Argv)
- Xregister char *b, **Argv;
- X{
- X register char *p, **argv = DUBL_NULL;
- X register int n = 0, i = 0, Argc;
- X static int loops;
- X int dummy;
- X
- X if (++loops == 20) {
- X print("Alias loop.\n");
- X return -1;
- X }
- X for (Argc = 0; Argc < argc; Argc++) {
- X register char *h = Argv[n + ++i];
- X register char *p2 = "";
- X int sep;
- X
- X /* we've hit a command separator or the end of the line */
- X if (h && strcmp(h, ";") && strcmp(h, "|"))
- X continue;
- X
- X /* create a new argv containing this (possible subset) of argv */
- X if (!(argv = (char **)calloc((unsigned)(i+1), sizeof (char *))))
- X continue;
- X sep = n + i;
- X while (i--)
- X strdup(argv[i], Argv[n+i]);
- X
- X if ((!last_aliased || strcmp(last_aliased, argv[0]))
- X && (p = alias_expand(argv[0]))) {
- X /* if history was referenced, ignore the rest of argv
- X * else copy all of argv onto the end of the buffer.
- X */
- X if (!(p2 = hist_expand(p, argv, &dummy)))
- X break;
- X if (!dummy)
- X (void) argv_to_string(p2+strlen(p2), argv+1);
- X if (Strcpy(b, p2) > BUFSIZ) {
- X print("Not enough buffer space.\n");
- X break;
- X }
- X /* release old argv and build a new one based on new string */
- X free_vec(argv);
- X if (!(argv = mk_argv(b, &dummy, 0)))
- X break;
- X if (alias_stuff(b, dummy, argv) == -1)
- X break;
- X } else
- X b = argv_to_string(b, argv);
- X xfree(last_aliased), last_aliased = NULL;
- X free_vec(argv);
- X b += strlen(b);
- X if (h) {
- X b += strlen(sprintf(b, " %s ", h));
- X while (++Argc < argc && (h = Argv[Argc]))
- X if (Argc > sep && strcmp(h, ";"))
- X break;
- X n = Argc--;
- X }
- X i = 0;
- X }
- X xfree(last_aliased), last_aliased = NULL;
- X --loops;
- X if (Argc < argc) {
- X free_vec(argv);
- X return -1;
- X }
- X return 0;
- X}
- X
- Xchar *
- Xalias_expand(cmd)
- Xregister char *cmd;
- X{
- X register char *p;
- X register int x;
- X
- X if (!(p = do_set(functions, cmd)))
- X return NULL;
- X last_aliased = savestr(cmd); /* to be freed elsewhere; don't strdup! */
- X if (isoff(glob_flags, WARNING))
- X return p;
- X for (x = 0; cmds[x].command; x++)
- X if (!strcmp(cmd, cmds[x].command)) {
- X wprint("(real command: \"%s\" aliased to \"%s\")\n", cmd, p);
- X return p;
- X }
- X for (x = 0; ucb_cmds[x].command; x++)
- X if (!strcmp(cmd, ucb_cmds[x].command)) {
- X wprint("(ucb-command: \"%s\" aliased to \"%s\")\n", cmd, p);
- X return p;
- X }
- X return p;
- X}
- X
- Xstatic int nonobang;
- X
- X/* expand history references and separate message lists from other tokens */
- Xchar *
- Xhist_expand(str, argv, hist_was_referenced)
- Xregister char *str, **argv;
- Xregister int *hist_was_referenced;
- X{
- X static char buf[BUFSIZ];
- X register int b = 0, inquotes = 0;
- X int first_space = 0, ignore_bang;
- X
- X ignore_bang = (ison(glob_flags, IGN_BANG) ||
- X do_set(set_options, "ignore_bang"));
- X nonobang = !!do_set(set_options, "nonobang");
- X
- X if (hist_was_referenced)
- X *hist_was_referenced = 0;
- X while (*str) {
- X while (!inquotes && isspace(*str))
- X str++;
- X do {
- X if (!*str)
- X break;
- X if (b >= sizeof(buf)-1) {
- X print("argument list too long.\n");
- X return NULL;
- X }
- X if ((buf[b] = *str++) == '\'') {
- X /* make sure there's a match! */
- X inquotes = !inquotes;
- X }
- X if (!first_space && !inquotes && index("0123456789{}*$^.", buf[b])
- X && b && !index("0123456789{}-^. \t", buf[b-1])) {
- X buf[b+1] = buf[b];
- X buf[b++] = ' ';
- X while ((buf[++b] = *str++) && index("0123456789-,${}.", buf[b]))
- X ;
- X if (!buf[b])
- X str--;
- X first_space++;
- X }
- X /* check for (;) (|) or any other delimiter and separate it from
- X * other tokens.
- X */
- X if (!inquotes && buf[b] != '\0' && isdelimeter(buf[b]) &&
- X (b < 0 || buf[b-1] != '\\')) {
- X if (!isspace(buf[b]))
- X first_space = -1; /* resume msg-list separation */
- X if (b && !isspace(buf[b-1]))
- X buf[b+1] = buf[b], buf[b++] = ' ';
- X b++;
- X break;
- X }
- X /*
- X * If double-quotes, just copy byte by byte, char by char,
- X * but do remove backslashes from in front of !s
- X */
- X if (!inquotes && buf[b] == '"') {
- X int B = b;
- X while ((buf[++B] = *str++) && buf[B] != '"')
- X if (*str == '!' && buf[B] == '\\')
- X buf[B] = '!', str++;
- X if (buf[B])
- X b = B;
- X else
- X str--;
- X b++;
- X continue;
- X }
- X if (buf[b] == '\\') {
- X first_space = 1; /* don't split escaped words */
- X if ((buf[++b] = *str) == '!')
- X buf[--b] = '!';
- X ++str;
- X } else if (buf[b] == '!' && *str && *str != '\\' && !isspace(*str)
- X && !ignore_bang) {
- X char word[BUFSIZ], *s;
- X if (!(s = reference_hist(str, word, argv))) {
- X if (!nonobang)
- X return NULL;
- X } else {
- X str = s;
- X if (hist_was_referenced)
- X *hist_was_referenced = 1;
- X if (strlen(word) + b >= sizeof buf) {
- X print("argument list too long.\n");
- X return NULL;
- X }
- X b += Strcpy(&buf[b], word) - 1;
- X }
- X }
- X b++;
- X } while (*str && (!isdelimeter(*str) || str[-1] == '\\'));
- X if (!inquotes)
- X first_space++, buf[b++] = ' ';
- X }
- X buf[b] = 0;
- X return buf;
- X}
- X
- X/*
- X * expand references to internal variables. This allows such things
- X * as $iscurses, $hdrs_only, etc. to work correctly.
- X */
- Xchar *
- Xcheck_internal(str)
- Xregister char *str;
- X{
- X int ret_val = -1;
- X static char version[80], get_status[4];
- X
- X if (!strcmp(str, "iscurses"))
- X ret_val = (iscurses || ison(glob_flags, PRE_CURSES));
- X else if (!strcmp(str, "istool"))
- X ret_val = istool;
- X else if (!strcmp(str, "hdrs_only"))
- X ret_val = (hdrs_only && *hdrs_only);
- X else if (!strcmp(str, "is_shell"))
- X ret_val = is_shell;
- X else if (!strcmp(str, "is_sending"))
- X ret_val = (ison(glob_flags, IS_SENDING) != 0);
- X else if (!strcmp(str, "redirect"))
- X ret_val = (isatty(0) != 0);
- X else if (!strcmp(str, "thisfolder"))
- X return (mailfile && *mailfile) ? mailfile : NULL;
- X else if (!strcmp(str, "status"))
- X return sprintf(get_status, "%d", last_status);
- X else if (!strcmp(str, "output"))
- X return last_output;
- X else if (!strcmp(str, "version")) {
- X /* Create the version string ONCE, then re-use it. */
- X if (!*version)
- X (void) sprintf(version, "%s (%d.%s.%d %s)",
- X MUSHNAME, RELEASE, REVISION, PATCHLEVEL, RELEASE_DATE);
- X return version;
- X }
- X
- X return ret_val > 0 ? "1" : ret_val == 0? "0" : NULL;
- X}
- X
- X/*
- X * Structure for expansions
- X * (move to a header file?)
- X */
- Xstruct expand {
- X char *orig; /* string beginning with substring to be expanded */
- X char *exp; /* result of expansion of substring */
- X char *rest; /* rest of the original string beyond substring */
- X};
- X
- X/*
- X * Parse and expand a single variable reference. Variable references
- X * begin with a '$' and thereafter look like any of:
- X * $ $$ is the pid of the current process
- X * [%x] $[%x] expands %x as a hdr_format character ($%x is same)
- X * (%x) $(%x) expands %x as a prompt format character
- X * name Value of variable "name" (error if not set)
- X * v:x Modified expansion; v is any of above, x is any of
- X * h head of a file pathname
- X * t tail of a file pathname
- X * l value converted to lowercase
- X * u value converted to uppercase
- X * q quote against further expansion (not yet)
- X * <num> select the <num>th space-separated field
- X * ?name Set/unset truth value of "name"
- X * {v} Separate v (any of above) from surrounding text
- X * A variable name may include alphabetics, numbers, or underscores but
- X * must begin with an alphabetic or underscore.
- X */
- Xvarexp(ref)
- Xstruct expand *ref;
- X{
- X char *str = ref->orig, c, *p, *var, *end = NULL, *op = NULL;
- X int do_bool, do_fmt = 0, expanded = 0;
- X
- X if (*str == '$') {
- X /* Allow a $ all by itself to stand */
- X if (!*++str || isspace(*str)) {
- X ref->exp = savestr("$");
- X ref->rest = str;
- X return 1;
- X }
- X /* Handle $?{name} for backwards compatibility */
- X if (do_bool = (*str == '?'))
- X str++;
- X if (*str == '{')
- X if (p = index(str + 1, '}')) {
- X var = str + 1;
- X end = p;
- X } else
- X goto bad_var;
- X else
- X var = str;
- X /* Handle $?name and ${?name} (normal cases) */
- X if (*var == '?') {
- X if (do_bool) /* backwards compatibility clash */
- X goto bad_var;
- X ++var, do_bool = 1;
- X }
- X switch (*var) {
- X case '$':
- X if (str[0] == '{' && str[2] != '}')
- X goto bad_var;
- X else {
- X char buf[16];
- X (void) sprintf(buf, "%d", getpid());
- X ref->exp = savestr(buf);
- X ref->rest = (end ? end : var) + 1;
- X return 1;
- X }
- X when '%':
- X for (p = var + 1; *p && !index(" \t\n;|\"'$", *p); p++)
- X if (*p == ':') {
- X if (!do_bool && !op) {
- X op = p;
- X do_fmt = p - var;
- X } else
- X break;
- X }
- X if (!do_fmt)
- X do_fmt = p - var;
- X end = p;
- X when '[': case '(': /*)*/
- X p = any(var, *var == '(' ? ") \t\n" : "] \t\n");
- X if (!p || isspace(*p))
- X goto bad_var;
- X if (end && p > end)
- X goto bad_var;
- X else {
- X var++;
- X do_fmt = p - var;
- X if (*++p == ':')
- X op = p;
- X else
- X end = p;
- X }
- X /* fall through */
- X default:
- X if (!do_fmt && !isalpha(*var) && *var != '_')
- X goto bad_var;
- X if (!end)
- X end = var + strlen(var);
- X for (p = (op ? op : var + do_fmt) + 1; p < end; p++)
- X if (!do_bool && !op && *p == ':') {
- X op = p;
- X } else if (!isalnum(*p) && *p != '_') {
- X if (*str == '{') /*}*/
- X goto bad_var;
- X end = p;
- X break;
- X }
- X if (op && op > end)
- X op = NULL;
- X }
- X /* replace the end of "var" (end) with a nul,
- X * and save char in `c'. Similarly chop at op.
- X */
- X c = *end, *end = 0;
- X if (op)
- X *op++ = 0;
- X
- X if (!do_fmt && debug > 3)
- X printf("expanding (%s) ", var);
- X
- X /* get the value of the variable. */
- X if (do_fmt) {
- X char c1 = var[do_fmt];
- X var[do_fmt] = 0;
- X if (debug > 3)
- X printf("expanding (%s) ", var);
- X if (/*(*/ ')' == c1)
- X p = format_prompt(current_msg, var);
- X else
- X p = format_hdr(current_msg, var, FALSE) + 9;
- X var[do_fmt] = c1;
- X } else if (!(p = check_internal(var)))
- X p = do_set(set_options, var);
- X if (do_bool) {
- X ref->exp = savestr((p && (*p || !do_fmt)) ? "1" : "0");
- X expanded = 1;
- X if (debug > 3)
- X printf("--> (%s)\n", p);
- X } else if (p) {
- X if (debug > 3)
- X printf("--> (%s)", p);
- X if (op && isdigit(*op)) {
- X int varc, ix = atoi(op) - 1;
- X char **varv = mk_argv(p, &varc, FALSE);
- X /* Ignore non-fatal errors like unmatched quotes */
- X if (varv && varc < 0)
- X for (varc = 0; varv[varc]; varc++)
- X ;
- X if (ix < 0 || varc <= ix || !varv)
- X ref->exp = savestr("");
- X else
- X ref->exp = savestr(varv[ix]);
- X expanded = 1;
- X free_vec(varv);
- X } else if (op) {
- X char *p2 = rindex(p, '/');
- X expanded = (*op == 'h' || *op == 't');
- X if (*op == 't' && p2)
- X p = p2 + 1;
- X else if (*op == 'h' && p2)
- X *p2 = 0;
- X ref->exp = savestr(p);
- X if (*op == 'h' && p2)
- X *p2 = '/';
- X else if (*op == 'l' || *op == 'u') {
- X expanded = 1;
- X for (p = ref->exp; *p; p++)
- X if (*op == 'u')
- X Upper(*p);
- X else
- X Lower(*p);
- X }
- X if (!expanded) {
- X print("Unknown colon modifier :%c.\n", *op);
- X xfree(ref->exp);
- X } else
- X if (debug > 3)
- X printf("--> (%s)\n", p);
- X } else {
- X ref->exp = savestr(p);
- X expanded = 1;
- X if (debug > 3)
- X printf("\n");
- X }
- X } else {
- X print("%s: undefined variable\n", var);
- X expanded = 0;
- X }
- X *end = c; /* replace the null with the old character */
- X if (op)
- X *--op = ':'; /* Put back the colon */
- X ref->rest = end + (*str == '{'); /* } */
- X }
- X return expanded;
- Xbad_var:
- X print("Illegal variable name.\n");
- X return 0;
- X}
- X
- X/*
- X * find mush variable references and expand them to their values.
- X * variables are preceded by a '$' and cannot be within single
- X * quotes. Only if expansion has been made do we copy buf back into str.
- X * We expand only as far as the first unprotected `;' separator in str,
- X * to get the right behavior when multiple commands are on one line.
- X * RETURN 0 on failure, 1 on success.
- X */
- Xvariable_expand(str)
- Xregister char *str;
- X{
- X register int b = 0, inquotes = 0;
- X char buf[BUFSIZ], *start = str;
- X int expanded = 0;
- X
- X while (*str && b < sizeof buf - 1) {
- X if (*str == '~' && (str == start || isspace(*(str-1)))) {
- X register char *p = any(str, " \t"), *tmp;
- X int x = 1;
- X if (p)
- X *p = 0;
- X tmp = getpath(str, &x);
- X /* if error, print message and return 0 */
- X if (x == -1) {
- X wprint("%s: %s\n", str, tmp);
- X return 0;
- X }
- X b += Strcpy(buf+b, tmp);
- X if (p)
- X *p = ' ', str = p;
- X else
- X str += strlen(str);
- X expanded = 1;
- X }
- X /* if single-quotes, just copy byte by byte, char by char ... */
- X if ((buf[b] = *str++) == '\'' && !inquotes) {
- X while ((buf[++b] = *str++) && buf[b] != '\'')
- X ;
- X if (!buf[b])
- X str--;
- X } else if (!inquotes && buf[b] == '\\' && *str) {
- X buf[++b] = *str++;
- X b++;
- X continue;
- X } else if (buf[b] == '"')
- X inquotes = !inquotes;
- X /* If $ is eol, continue. Variables must start with a `$'
- X * and continue with {, _, a-z, A-Z or it is not a variable. }
- X */
- X if (buf[b] == '$' && *str) {
- X struct expand expansion;
- X expansion.orig = str - 1;
- X if (varexp(&expansion)) {
- X b += Strcpy(&buf[b], expansion.exp);
- X xfree(expansion.exp);
- X str = expansion.rest;
- X expanded = 1;
- X } else
- X return 0;
- X } else if (!inquotes && buf[b] == ';') {
- X while (buf[++b] = *str++)
- X ;
- X b++;
- X break;
- X } else
- X b++;
- X }
- X buf[b] = 0;
- X if (expanded) /* if any expansions were done, copy back into orig buf */
- X (void) strcpy(start, buf);
- X if (debug > 3)
- X printf("expanded to: %s\n", start);
- X return 1;
- X}
- X
- X/* make an argv of space delimited character strings out of string "str".
- X * place in "argc" the number of args made. If final is true, then expand
- X * variables and file names and remove quotes and backslants according to
- X * standard.
- X */
- Xchar **
- Xmk_argv(str, argc, final)
- Xregister char *str;
- Xint *argc;
- X{
- X register char *s = NULL, *p;
- X register int tmp, err = 0, unq_sep = 0;
- X char *newargv[MAXARGS], **argv, *p2, c, buf[BUFSIZ];
- X
- X if (debug > 3)
- X (void) printf("Working on: %s\n",str);
- X /* If final is true, do variable expansions first */
- X if (final) {
- X (void) strcpy(buf, str);
- X str = buf;
- X if (!variable_expand(str))
- X return DUBL_NULL;
- X }
- X *argc = 0;
- X while (*str && *argc < MAXARGS) {
- X while (isspace(*str))
- X ++str;
- X /* When we have hit an unquoted `;', final must be true,
- X * so we're finished. Stuff the rest of the string at the
- X * end of the argv -- do_command will pass it back later,
- X * for further processing -- and break out of the loop.
- X * NOTE: *s is not yet valid the first time through this
- X * loop, so unq_sep should always be initialized to 0.
- X */
- X if (unq_sep && s && *s == ';') {
- X if (*str) { /* Don't bother saving a null string */
- X newargv[*argc] = savestr(str);
- X (*argc)++;
- X }
- X break;
- X }
- X if (*str) { /* found beginning of a word */
- X unq_sep = 0; /* innocent until proven guilty */
- X s = p = str;
- X do {
- X if (p - s >= sizeof buf-1) {
- X print("argument list too long.\n");
- X return DUBL_NULL;
- X }
- X if (*str == ';' || *str == '|')
- X unq_sep = final; /* Mark an unquoted separator */
- X if ((*p = *str++) == '\\') {
- X if (final && (*str == ';' || *str == '|'))
- X --p; /* Back up to overwrite the backslash */
- X if (*++p = *str) /* assign and compare to NUL */
- X str++;
- X continue;
- X }
- X if (p2 = index("\"'", *p)) {
- X register char c2 = *p2;
- X /* you can't escape quotes inside quotes of the same type */
- X if (!(p2 = index(str, c2))) {
- X if (final)
- X print("Unmatched %c.\n", c2);
- X err++;
- X p2 = str;
- X }
- X tmp = (int)(p2 - str) + 1; /* take up to & include quote */
- X (void) strncpy(p + !final, str, tmp);
- X p += tmp - 2 * !!final; /* change final to a boolean */
- X if (*(str = p2))
- X str++;
- X }
- X } while (++p, *str && (!isdelimeter(*str) || str[-1] == '\\'));
- X if (c = *str) /* set c = *str, check for null */
- X str++;
- X *p = 0;
- X if (*s) {
- X /* To differentiate real separators from quoted or
- X * escaped ones, always store 3 chars:
- X * 1) The separator character
- X * 2) A nul (string terminator)
- X * 3) An additional boolean (0 or 1)
- X * The boolean is checked by do_command. Note that this
- X * applies only to "solitary" separators, i.e. those not
- X * part of a larger word.
- X */
- X if (final && (!strcmp(s, ";") || !strcmp(s, "|"))) {
- X char *sep = savestr("xx"); /* get 3 char slots */
- X sep[0] = *s, sep[1] = '\0', sep[2] = unq_sep;
- X newargv[*argc] = sep;
- X } else
- X newargv[*argc] = savestr(s);
- X (*argc)++;
- X }
- X *p = c;
- X }
- X }
- X if (!*argc)
- X return DUBL_NULL;
- X /* newargv[*argc] = NULL; */
- X if (!(argv = (char **)calloc((unsigned)((*argc)+1), sizeof(char *)))) {
- X perror("mk_argv: calloc");
- X return DUBL_NULL;
- X }
- X for (tmp = 0; tmp < *argc; tmp++)
- X argv[tmp] = newargv[tmp];
- X if (err)
- X *argc = -1;
- X else if (debug > 3)
- X (void) printf("Made argv: "), print_argv(argv);
- X return argv;
- X}
- X
- X/*
- X * Report a history parsing error.
- X * Suppress the message if nonobang is true.
- X */
- X#define hist_error if (nonobang) {;} else print
- X
- X/*
- X * reference previous history from syntax of str and place result into buf
- X * We know we've got a history reference -- we're passed the string starting
- X * the first char AFTER the '!' (which indicates history reference)
- X */
- Xchar *
- Xreference_hist(str, buf, hist_ref)
- Xregister char *str, **hist_ref;
- Xchar buf[];
- X{
- X int relative; /* relative from current hist_no */
- X int old_hist, argstart = 0, lastarg, argend = 0, n = 0;
- X register char *p, *rb = NULL, **argv = hist_ref;
- X struct history *hist;
- X
- X buf[0] = 0;
- X if (*str == '{')
- X if (!(rb = index(str, '}'))) { /* { */
- X hist_error("Unmatched '}'");
- X return NULL;
- X } else
- X *rb = 0, ++str;
- X relative = *str == '-';
- X if (index("!:$*", *str)) {
- X old_hist = hist_no;
- X if (*str == '!')
- X str++;
- X } else if (isdigit(*(str + relative)))
- X str = my_atoi(str + relative, &old_hist);
- X else if (!(p = hist_from_str(str, &old_hist))) {
- X if (rb) /* { */
- X *rb = '}';
- X return NULL;
- X } else
- X str = p;
- X if (relative)
- X old_hist = (hist_no - old_hist) + 1;
- X if (old_hist == hist_no) {
- X if (!(argv = hist_ref))
- X hist_error("You haven't done anything yet!\n");
- X } else {
- X if (old_hist <= hist_no-hist_size || old_hist > hist_no ||
- X old_hist <= 0) {
- X if (old_hist <= 0)
- X hist_error("You haven't done that many commands, yet.\n");
- X else
- X hist_error("Event %d %s.\n", old_hist,
- X (old_hist > hist_no)? "hasn't happened yet": "expired");
- X if (rb) /* { */
- X *rb = '}';
- X return NULL;
- X }
- X hist = hist_head;
- X while (hist && hist->histno != old_hist)
- X hist = hist->prev;
- X if (hist)
- X argv = hist->argv;
- X }
- X if (!argv) {
- X if (rb) /* { */
- X *rb = '}';
- X return NULL;
- X }
- X while (argv[argend+1])
- X argend++;
- X lastarg = argend;
- X if (*str && index(":$*-", *str)) {
- X int isrange;
- X if (*str == ':' && isdigit(*++str))
- X str = my_atoi(str, &argstart);
- X if (isrange = (*str == '-'))
- X str++;
- X if (!isdigit(*str)) {
- X if (*str == 'p')
- X str++, print_only = 1;
- X else if (*str == '*') {
- X str++;
- X if (!isrange) {
- X if (argv[0]) {
- X if (argv[1])
- X argstart = 1;
- X else {
- X if (rb) /* { */
- X *rb = '}';
- X return (rb ? rb + 1 : str);
- X }
- X } else
- X argstart = 0;
- X }
- X } else if (*str == '$') {
- X if (!isrange)
- X argstart = argend;
- X str++;
- X } else if (isrange && argend > argstart)
- X argend--; /* unspecified end of range implies last-1 arg */
- X else
- X argend = argstart; /* no range specified; use arg given */
- X } else
- X str = my_atoi(str, &argend);
- X }
- X if (argstart > lastarg || argend > lastarg || argstart > argend) {
- X hist_error("Bad argument selector.\n");
- X if (rb) /* { */
- X *rb = '}';
- X return (nonobang ? rb ? rb + 1 : str : NULL);
- X }
- X if (debug > 3)
- X print("history expanding from "), print_argv(argv);
- X while (argstart <= argend) {
- X n += Strcpy(&buf[n], argv[argstart++]);
- X buf[n++] = ' ';
- X }
- X buf[--n] = 0;
- X if (rb) /* { */
- X *rb = '}';
- X return (rb ? rb + 1 : str);
- X}
- X
- X/* find a history command that contains the string "str"
- X * place that history number in "hist" and return the end of the string
- X * parsed: !?foo (find command with "foo" in it) !?foo?bar (same, but add "bar")
- X * in the second example, return the pointer to "bar"
- X */
- Xchar *
- Xhist_from_str(str, hist_number)
- Xregister char *str;
- Xregister int *hist_number;
- X{
- X register char *p = NULL, c = 0;
- X int full_search = 0, len, found;
- X char buf[BUFSIZ];
- X struct history *hist;
- X#ifndef REGCMP
- X extern char *re_comp();
- X#else
- X extern char *regcmp();
- X#endif /* REGCMP */
- X
- X /* For !{something}, the {} are stripped in reference_hist() */
- X if (*str == '?') {
- X if (p = index(++str, '?'))
- X c = *p, *p = 0;
- X else
- X p = str + strlen(str);
- X full_search = 1;
- X } else {
- X p = str;
- X while (*p && *p != ':' && !isspace(*p))
- X p++;
- X c = *p, *p = 0;
- X }
- X if (*str) {
- X#ifndef REGCMP
- X if (re_comp(str))
- X#else
- X if (!regcmp(str, NULL))
- X#endif /* REGCMP */
- X {
- X if (c)
- X *p = c;
- X return NULL;
- X }
- X } else {
- X *hist_number = hist_no;
- X if (c)
- X *p = c;
- X return (*p == '?' ? p + 1 : p);
- X }
- X len = strlen(str);
- X /* move thru the history in reverse searching for a string match. */
- X for (hist = hist_head; hist; hist = hist->prev) {
- X if (full_search) {
- X (void) argv_to_string(buf, hist->argv);
- X Debug("Checking for (%s) in (#%d: %s)\n", str, hist->histno, buf);
- X }
- X if (!full_search) {
- X (void) strcpy(buf, hist->argv[0]);
- X Debug("Checking for (%s) in (#%d: %*s)\n",
- X str, hist->histno, len, buf);
- X found = !strncmp(buf, str, len);
- X } else
- X found =
- X#ifndef REGCMP
- X re_exec(buf)
- X#else
- X !!regex(str, buf, NULL) /* convert to boolean value */
- X#endif /* REGCMP */
- X == 1;
- X if (found) {
- X *hist_number = hist->histno;
- X Debug("Found it in history #%d\n", *hist_number);
- X *p = c;
- X return (*p == '?' ? p + 1 : p);
- X }
- X }
- X hist_error("%s: event not found\n", str);
- X *p = c;
- X return NULL;
- X}
- X
- Xdisp_hist(n, argv) /* argc not used -- use space for the variable, "n" */
- Xregister int n;
- Xchar **argv;
- X{
- X register int list_num = TRUE, num_of_hists = hist_size;
- X register int reverse = FALSE;
- X struct history *hist = hist_tail;
- X
- X while (*++argv && *argv[0] == '-') {
- X n = 1;
- X do switch(argv[0][n]) {
- X case 'h': list_num = FALSE;
- X when 'r': reverse = TRUE; hist = hist_head;
- X otherwise: return help(0, "history", cmd_help);
- X }
- X while (argv[0][++n]);
- X }
- X
- X if (!hist) {
- X print("No history yet.\n");
- X return -1;
- X }
- X if (*argv)
- X if (!isdigit(**argv)) {
- X print("history: badly formed number\n");
- X return -1;
- X } else
- X num_of_hists = atoi(*argv);
- X
- X if (num_of_hists > hist_size || num_of_hists > hist_no)
- X num_of_hists = min(hist_size, hist_no);
- X
- X if (!reverse)
- X while (hist_no - hist->histno > num_of_hists) {
- X (void) printf("skipping %d\n", hist->histno);
- X hist = hist->next;
- X }
- X
- X (void) do_pager(NULL, TRUE);
- X for (n = 0; n < num_of_hists && hist; n++) {
- X char buf[256];
- X if (list_num)
- X (void) do_pager(sprintf(buf, "%4.d ", hist->histno), FALSE);
- X (void) argv_to_string(buf, hist->argv);
- X (void) do_pager(buf, FALSE);
- X if (do_pager("\n", FALSE) == -1)
- X break;
- X hist = (reverse)? hist->prev : hist->next;
- X }
- X (void) do_pager(NULL, FALSE);
- X return 0;
- X}
- X
- Xinit_history(newsize)
- X{
- X if ((hist_size = newsize) < 1)
- X hist_size = 1;
- X}
- END_OF_FILE
- if test 34930 -ne `wc -c <'mush/loop.c'`; then
- echo shar: \"'mush/loop.c'\" unpacked with wrong size!
- fi
- # end of 'mush/loop.c'
- fi
- if test -f 'mush/pick.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'mush/pick.c'\"
- else
- echo shar: Extracting \"'mush/pick.c'\" \(16964 characters\)
- sed "s/^X//" >'mush/pick.c' <<'END_OF_FILE'
- X/* @(#)pick.c 2.4 (c) copyright 10/18/86 (Dan Heller) */
- X
- X#include "mush.h"
- X
- Xstatic int before, after, search_from, search_subj, search_to, xflg, icase;
- Xstatic char search_hdr[64];
- Xstatic int mdy[3];
- Xstatic int pick();
- Xstatic void month_day_year();
- X
- Xdo_pick(n, argv, list)
- Xregister int n;
- Xregister char **argv, list[];
- X{
- X char ret_list[MAXMSGS_BITS];
- X
- X if (n > 1 && !strcmp(argv[1], "-?"))
- X return help(0, "pick", cmd_help);
- X
- X clear_msg_list(ret_list);
- X /* if is_pipe, then the messages to search for are already set.
- X * if not piped, then reverse the bits for all message numbers.
- X * That is, search EACH message. only those matching will be returned.
- X */
- X if (isoff(glob_flags, IS_PIPE))
- X bitput(ret_list, list, msg_cnt, =~); /* macro, turn on all bits */
- X /* Use n temporarily as verbosity flag */
- X n = (!chk_option("quiet", "pick") &&
- X isoff(glob_flags, DO_PIPE));
- X if ((n = pick(argv, list, ret_list, n)) == -1)
- X return -1;
- X if (istool && isoff(glob_flags, DO_PIPE))
- X print("%d matches:\n", n);
- X for (n = 0; n < msg_cnt; n++)
- X if (msg_bit(ret_list, n)) {
- X if (isoff(glob_flags, DO_PIPE))
- X if (istool)
- X print_more("%d ", n+1);
- X else
- X print("%s\n", compose_hdr(n));
- X set_msg_bit(list, n);
- X } else
- X unset_msg_bit(list, n);
- X return 0;
- X}
- X
- X/*
- X * search for messages. Return the number of matches. Errors such
- X * as internal errors or syntax errors, return -1.
- X * "head" and "tail" are specified using +<num> or -<num> as args.
- X * Both can be specified and the order is significant.
- X * pick +5 -3
- X * returns the last three of the first five matches.
- X * pick -3 +2
- X * returns the first two of the last three matches.
- X */
- Xstatic int
- Xpick(argv, list, ret_list, verbose)
- Xregister char **argv, list[], ret_list[];
- X{
- X register char c;
- X int matches = 0;
- X char pattern[256];
- X short head_first, head_cnt, tail_cnt, search = TRUE;
- X int n;
- X
- X if (!msg_cnt) {
- X print("No Messages.\n");
- X return -1;
- X }
- X
- X head_first = TRUE;
- X head_cnt = tail_cnt = -1;
- X icase = before = after = search_from = search_subj = search_to = xflg = 0;
- X mdy[0] = mdy[1] = search_hdr[0] = 0;
- X while (*argv && *++argv && (**argv == '-' || **argv == '+'))
- X if (**argv == '+' || isdigit(argv[0][1])) {
- X if (**argv == '+')
- X head_cnt = atoi(&argv[0][1]);
- X else {
- X tail_cnt = atoi(&argv[0][1]);
- X if (head_cnt == -1)
- X head_first = FALSE;
- X }
- X if (head_cnt == 0 || tail_cnt == 0) {
- X print("pick: invalid head/tail number: %s\n", &argv[0][1]);
- X clear_msg_list(ret_list);
- X return -1;
- X }
- X } else if ((c = argv[0][1]) == 'e') {
- X if (!*++argv) {
- X print("use: -e expression...\n");
- X return -1;
- X }
- X break;
- X } else switch (c) {
- X /* users specifies a range */
- X case 'r': {
- X int X = 2;
- X /* if not a pipe, then clear all bits cuz we only want
- X * to search the message specified here...
- X * If it is a pipe, then add to the messages searched for.
- X */
- X if (isoff(glob_flags, IS_PIPE))
- X clear_msg_list(list);
- X /* "-r10-15"
- X * ^argv[1][2] if NULL, then
- X * list detached from "r" e.g. "-r" "5-20"
- X */
- X if (!argv[0][X])
- X argv++, X = 0;
- X (*argv) += X;
- X n = get_msg_list(argv, list);
- X (*argv) -= X;
- X if (n == -1)
- X return -1;
- X argv += (n-1); /* we're going to increment another up top */
- X }
- X when 'a': {
- X if ((n = ago_date(++argv)) == -1)
- X return -1;
- X argv += n;
- X }
- X when 'd':
- X if (!*++argv) {
- X print("Specify a date for -%c\n", c);
- X return -1;
- X }
- X if (!date1(*argv))
- X return -1;
- X when 's' : case 'f': case 't': case 'h':
- X if (search_subj + search_from + search_to + *search_hdr > 1) {
- X print("Specify one of `s', `f', `t' or `h' only\n");
- X return -1;
- X }
- X if (c == 's')
- X search_subj = 1;
- X else if (c == 'f')
- X search_from = 1;
- X else if (c == 'h')
- X if (!*++argv)
- X print("Specify header to search for.\n");
- X else
- X (void) lcase_strcpy(search_hdr, *argv);
- X else
- X search_to = 1;
- X when 'x' : xflg = 1;
- X when 'i' : icase = 1;
- X otherwise:
- X print("pick: unknown flag: %c\n", argv[0][1]);
- X clear_msg_list(ret_list);
- X return -1;
- X }
- X if (xflg && head_cnt + tail_cnt >= 0) {
- X print("Can't specify -x and head/tail options together.\n");
- X return -1;
- X }
- X pattern[0] = 0;
- X (void) argv_to_string(pattern, argv);
- X search = (pattern[0] || head_cnt + tail_cnt < 0);
- X if (verbose) {
- X if (head_cnt + tail_cnt >= 0) {
- X print("Finding the ");
- X if (head_cnt > 0) {
- X if (head_first)
- X if (tail_cnt == -1)
- X print_more("first %d message%s",
- X head_cnt, head_cnt > 1? "s" : "");
- X else
- X print_more("last %d message%s",
- X tail_cnt, tail_cnt > 1? "s" : "");
- X else /* there must be a tail_cnt and it comes first */
- X print_more("first %d message%s",
- X head_cnt, head_cnt > 1? "s" : "");
- X } else
- X print_more("last %d message%s",
- X tail_cnt, tail_cnt > 1? "s" : "");
- X if (tail_cnt > 0 && head_cnt > 0)
- X if (head_first)
- X print_more(" of the first %d", head_cnt);
- X else
- X print_more(" of the last %d", tail_cnt);
- X } else
- X print_more("Searching for messages");
- X if (!search) {
- X if (tail_cnt > 0 && head_cnt > 0)
- X print_more(" messages");
- X if (ison(glob_flags, IS_PIPE))
- X print_more(" from the input list");
- X } else if (mdy[1] == 0) {
- X print(" that %scontain \"%s\"", (xflg)? "do not ": "",
- X (*pattern)? pattern: "<previous expression>");
- X if (search_subj)
- X print_more(" in subject line");
- X else if (search_from)
- X print_more(" from author names");
- X else if (search_to)
- X print_more(" from the To: field");
- X else if (search_hdr[0])
- X print_more(" from the message header \"%s:\"", search_hdr);
- X } else {
- X extern char *month_names[]; /* from dates.c */
- X print_more(" dated ");
- X if (before || after)
- X if (xflg)
- X print_more("%s ", (!before)? "before": "after");
- X else
- X print_more("on or %s ", (before)? "before": "after");
- X print_more("%s. %d, %d",
- X month_names[mdy[0]], mdy[1], mdy[2] + 1900);
- X }
- X print_more(".\n");
- X }
- X if (mdy[1] > 0 && icase)
- X print("using date: -i flag ignored.\n");
- X if (!search) {
- X for (n = 0; n < msg_cnt && (!head_first || matches < head_cnt); n++)
- X if (msg_bit(list, n))
- X ++matches, set_msg_bit(ret_list, n);
- X } else
- X matches = find_pattern(head_first? head_cnt : msg_cnt,
- X pattern, list, ret_list);
- X if (xflg && matches >= 0) {
- X /* invert items in ret_list that also appear in list */
- X bitput(list, ret_list, msg_cnt, ^=);
- X /* there should be a faster way to do this count ... */
- X for (matches = n = 0; n < msg_cnt; n++)
- X if (msg_bit(ret_list, n))
- X ++matches;
- X }
- X Debug("matches = %d\n", matches);
- X if (!matches)
- X return 0;
- X
- X /* ok, the list we've got is a list of matched messages. If "tailing"
- X * is set, reduce the number of matches to at least tail_cnt.
- X */
- X if (tail_cnt >= 0)
- X for (n = 0; n < msg_cnt && matches > tail_cnt; n++)
- X if (msg_bit(ret_list, n)) {
- X Debug("tail: dropping %d\n", n+1);
- X unset_msg_bit(ret_list, n);
- X matches--;
- X }
- X
- X /* if tailing came before heading, we need to do the heading now. */
- X if (!head_first && head_cnt >= 0)
- X for (n = 0; n < msg_cnt; n++)
- X if (msg_bit(ret_list, n))
- X if (head_cnt > 0)
- X head_cnt--;
- X else {
- X unset_msg_bit(ret_list, n);
- X matches--;
- X }
- X return matches;
- X}
- X
- X/*
- X * find_pattern will search thru all the messages set in the check_list
- X * until the list runs out or "cnt" has been exhasted. ret_list contains
- X * the list of messages which have matched the pattern.
- X * return -1 for internal error or # of pattern matches.
- X */
- Xfind_pattern(cnt, p, check_list, ret_list)
- Xint cnt;
- Xregister char *p;
- Xchar check_list[], ret_list[];
- X{
- X register int n, val, i; /* val is return value from regex or re_exec */
- X int matches = 0;
- X long bytes = 0;
- X char buf[HDRSIZ];
- X static char *err = (char *)-1;
- X#ifdef REGCMP
- X char *regcmp(), *regex();
- X#else /* REGCMP */
- X char *re_comp();
- X#endif /* REGCMP */
- X
- X if (p && *p == '\\')
- X p++; /* take care of escaping special cases (`-', `\') */
- X
- X /* specify what we're looking for */
- X if (p && *p) {
- X if (icase)
- X p = lcase_strcpy(buf, p);
- X#ifdef REGCMP
- X if (err && p)
- X xfree(err);
- X if (p && !(err = regcmp(p, NULL))) {
- X print("regcmp error: %s\n", p);
- X clear_msg_list(ret_list);
- X return -1;
- X }
- X#else /* REGCMP */
- X if (err = re_comp(p)) {
- X print("re_comp error: %s\n", err);
- X clear_msg_list(ret_list);
- X return -1;
- X }
- X#endif /* REGCMP */
- X } else if (err == (char *)-1 && mdy[1] <= 0) {
- X print("No previous regular expression\n");
- X clear_msg_list(ret_list); /* doesn't matter really */
- X return -1;
- X }
- X /* start searching: set bytes, and message number: n */
- X for (n = 0; cnt && n < msg_cnt; n++)
- X if (msg_bit(check_list, n)) {
- X if (mdy[1] > 0) {
- X int msg_mdy[3];
- X if (ison(glob_flags, DATE_RECV))
- X p = msg[n].m_date_recv;
- X else
- X p = msg[n].m_date_sent;
- X /* Ick -- fix this mdy thing asap */
- X month_day_year(p, &msg_mdy[0], &msg_mdy[1], &msg_mdy[2]);
- X Debug("checking %d's date: %d-%d-%d ",
- X n+1, msg_mdy[0]+1, msg_mdy[1], msg_mdy[2]);
- X /* start at year and wrap around.
- X * only when match the day (4), check for == (match)
- X */
- X for (i = 2; i < 5; i++)
- X if (before && msg_mdy[i%3] < mdy[i%3]
- X || after && msg_mdy[i%3] > mdy[i%3]
- X || i == 4 && (msg_mdy[i%3] == mdy[i%3])) {
- X Debug("matched (%s).\n",
- X (i == 2)? "year" : (i == 3)? "month" : "day");
- X set_msg_bit(ret_list, n);
- X cnt--, matches++;
- X break;
- X } else if (msg_mdy[i%3] != mdy[i%3]) {
- X Debug("failed.\n");
- X break;
- X }
- X continue;
- X }
- X /* we must have the right date -- if we're searching for a
- X * string, find it.
- X */
- X (void) msg_get(n, NULL, 0);
- X bytes = 0;
- X while (bytes < msg[n].m_size) {
- X if (!search_subj && !search_from && !search_to &&
- X !*search_hdr && !(p = fgets(buf, sizeof buf, tmpf)))
- X break;
- X else if (search_subj) {
- X if (!(p = header_field(n, "subject")))
- X break;
- X } else if (search_from) {
- X if (!(p = header_field(n, "from"))) {
- X /*
- X * Check for MSG_SEPARATOR here? Maybe not...
- X */
- X register char *p2;
- X (void) msg_get(n, NULL, 0);
- X if (!(p2 = fgets(buf, sizeof buf, tmpf)) ||
- X !(p = index(p2, ' ')))
- X continue;
- X p++;
- X if (p2 = any(p, " \t"))
- X *p2 = 0;
- X }
- X } else if (search_to) {
- X if (!(p = header_field(n, "to")) &&
- X !(p = header_field(n, "apparently-to")))
- X break;
- X } else if (*search_hdr) {
- X if (!(p = header_field(n, search_hdr)))
- X break;
- X }
- X if (icase)
- X p = lcase_strcpy(buf, p);
- X#ifdef REGCMP
- X val = !!regex(err, p, NULL); /* convert value to a boolean */
- X#else /* REGCMP */
- X val = re_exec(p);
- X#endif /* REGCMP */
- X if (val == -1) { /* doesn't apply in system V */
- X print("Internal error for pattern search.\n");
- X clear_msg_list(ret_list); /* it doesn't matter, really */
- X return -1;
- X }
- X if (val) {
- X set_msg_bit(ret_list, n);
- X cnt--, matches++;
- X break;
- X }
- X if (search_subj || search_from || search_to || *search_hdr)
- X break;
- X else
- X bytes += strlen(p);
- X }
- X }
- X return matches;
- X}
- X
- X#ifdef CURSES
- X/*
- X * search for a pattern in composed message headers -- also see next function
- X * flags == 0 forward search (prompt).
- X * flags == -1 continue search (no prompt).
- X * flags == 1 backward search (prompt).
- X */
- Xsearch(flags)
- Xregister int flags;
- X{
- X register char *p;
- X char pattern[128];
- X register int this_msg = current_msg, val = 0;
- X static char *err = (char *)-1, direction;
- X SIGRET (*oldint)(), (*oldquit)();
- X#ifdef REGCMP
- X char *regcmp();
- X#else /* REGCMP */
- X char *re_comp();
- X#endif /* REGCMP */
- X
- X if (msg_cnt <= 1) {
- X print("Not enough messages to invoke a search.\n");
- X return 0;
- X }
- X pattern[0] = '\0';
- X if (flags == -1)
- X print("continue %s search...", direction? "forward" : "backward");
- X else
- X print("%s search: ", flags? "backward" : "forward");
- X if (flags > -1)
- X if (Getstr(pattern, COLS-18, 0) < 0)
- X return 0;
- X else
- X direction = !flags;
- X#ifdef REGCMP
- X if (err && *pattern)
- X xfree(err);
- X else if (err == (char *)-1 && !*pattern) {
- X print("No previous regular expression.");
- X return 0;
- X }
- X if (*pattern && !(err = regcmp(pattern, NULL))) {
- X print("Error in regcmp in %s", pattern);
- X return 0;
- X }
- X#else /* REGCMP */
- X if (err = re_comp(pattern)) {
- X print(err);
- X return 0;
- X }
- X#endif /* REGCMP */
- X move(LINES-1, 0), refresh();
- X on_intr();
- X
- X do {
- X if (direction)
- X current_msg = (current_msg+1) % msg_cnt;
- X else
- X if (--current_msg < 0)
- X current_msg = msg_cnt-1;
- X p = compose_hdr(current_msg);
- X#ifdef REGCMP
- X val = !!regex(err, p, NULL); /* convert value to a boolean */
- X#else /* REGCMP */
- X val = re_exec(p);
- X#endif /* REGCMP */
- X if (val == -1) /* doesn't apply in system V */
- X print("Internal error for pattern search.\n");
- X } while (!val && current_msg != this_msg && isoff(glob_flags, WAS_INTR));
- X
- X if (ison(glob_flags, WAS_INTR)) {
- X print("Pattern search interrupted.");
- X current_msg = this_msg;
- X } else if (val == 0)
- X print("Pattern not found.");
- X
- X off_intr();
- X return val;
- X}
- X#endif /* CURSES */
- X
- X/*
- X * Get just the month, day, and year from a date.
- X * This is a temporary measure until the date compares in pick()
- X * can be overhauled. It really should be in dates.c, but ...
- X */
- Xstatic
- Xvoid
- Xmonth_day_year(date, month, day, year)
- Xchar *date;
- Xint *month, *day, *year;
- X{
- X long gmt;
- X char unused[4], zone[8];
- X struct tm *t;
- X extern long getzoff();
- X
- X (void) sscanf(date, "%ld%3c%s", &gmt, unused, zone);
- X gmt += getzoff(zone);
- X t = gmtime(&gmt);
- X *month = t->tm_mon;
- X *day = t->tm_mday;
- X *year = t->tm_year;
- X}
- X
- X/*
- X * parse a user given date string and set mdy[] array with correct
- X * values. Return 0 on failure.
- X */
- Xdate1(p)
- Xregister char *p;
- X{
- X register char *p2;
- X long t;
- X int i;
- X struct tm *today;
- X
- X if (*p == '-' || *p == '+') {
- X before = !(after = *p == '+');
- X skipspaces(1);
- X }
- X if (!isdigit(*p) && *p != '/') {
- X print("syntax error on date: \"%s\"\n", p);
- X return 0;
- X }
- X (void) time (&t);
- X today = localtime(&t);
- X for (i = 0; i < 3; i++)
- X if (!p || !*p || *p == '/') {
- X switch(i) { /* default to today's date */
- X case 0: mdy[0] = today->tm_mon;
- X when 1: mdy[1] = today->tm_mday;
- X when 2: mdy[2] = today->tm_year;
- X }
- X if (p && *p)
- X p++;
- X } else {
- X p2 = (*p)? index(p+1, '/') : NULL;
- X mdy[i] = atoi(p); /* atoi will stop at the '/' */
- X if (i == 0 && (--(mdy[0]) < 0 || mdy[0] > 11)) {
- X print("Invalid month: %s\n", p);
- X return 0;
- X } else if (i == 1 && (mdy[1] < 1 || mdy[1] > 31)) {
- X print("Invalid day: %s\n", p);
- X return 0;
- X }
- X if (p = p2) /* set p to p2 and check to see if it's valid */
- X p++;
- X }
- X return 1;
- X}
- X
- X/*
- X * Parse arguments specifying days/months/years "ago" (relative to today).
- X * Legal syntax: -ago [+-][args]
- X * where "args" is defined to be:
- X * [0-9]+[ ]*[dD][a-Z]*[ ,]*[0-9]+[mM][a-Z]*[ ,]*[0-9]+[ ]*[yY][a-Z]*
- X * 1 or more digits, 0 or more spaces, d or D followed by 0 or more chars,
- X * 0 or more whitespaces or commas, repeat for months and years...
- X * Examples:
- X * 1 day, 2 months, 0 years
- X * 2 weeks 1 year
- X * 10d, 5m
- X * 3w
- X * 1d 1Y
- X *
- X * Return number of args parsed; -1 on error.
- X */
- Xago_date(argv)
- Xchar **argv;
- X{
- X#define SECS_PER_DAY (60 * 60 * 24)
- X#define SECS_PER_WEEK (SECS_PER_DAY * 7)
- X#define SECS_PER_MONTH ((int)(SECS_PER_DAY * 30.5))
- X#define SECS_PER_YEAR (SECS_PER_DAY * 365)
- X register char *p;
- X char buf[256];
- X int n = 0, value;
- X long t;
- X struct tm *today;
- X
- X (void) argv_to_string(buf, argv);
- X p = buf;
- X (void) time (&t); /* get current time in seconds and subtract new values */
- X if (*p == '-')
- X before = TRUE;
- X else if (*p == '+')
- X after = TRUE;
- X skipspaces(before || after);
- X while (*p) {
- X if (!isdigit(*p)) {
- X p -= 2;
- X break; /* really a syntax error, but it could be other pick args */
- X }
- X p = my_atoi(p, &value); /* get 1 or more digits */
- X skipspaces(0); /* 0 or more spaces */
- X switch (lower(*p)) { /* d, m, or y */
- X case 'd' : t -= value * SECS_PER_DAY;
- X when 'w' : t -= value * SECS_PER_WEEK;
- X when 'm' : t -= value * SECS_PER_MONTH;
- X when 'y' : t -= value * SECS_PER_YEAR;
- X otherwise: return -1;
- X }
- X for (p++; Lower(*p) >= 'a' && *p <= 'z'; p++)
- X ; /* skip the rest of this token */
- X while (*p == ',' || isspace(*p))
- X ++p; /* 0 or more whitespaces or commas */
- X }
- X today = localtime(&t);
- X mdy[0] = today->tm_mon;
- X mdy[1] = today->tm_mday;
- X mdy[2] = today->tm_year;
- X
- X /* Count the number of args parsed */
- X for (n = 0; p > buf && *argv; n++)
- X p -= (strlen(*argv++)+1);
- X Debug("parsed %d args\n", n);
- X return n;
- X}
- END_OF_FILE
- if test 16964 -ne `wc -c <'mush/pick.c'`; then
- echo shar: \"'mush/pick.c'\" unpacked with wrong size!
- fi
- # end of 'mush/pick.c'
- fi
- echo shar: End of archive 5 \(of 19\).
- cp /dev/null ark5isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 19 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-
-
-