home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 January
/
usenetsourcesnewsgroupsinfomagicjanuary1994.iso
/
sources
/
misc
/
volume18
/
mush
/
part11
< prev
next >
Wrap
Internet Message Format
|
1991-04-22
|
51KB
From: argv@zipcode.com (Dan Heller)
Newsgroups: comp.sources.misc
Subject: v18i068: mush - Mail User's Shell, Part11/22
Message-ID: <1991Apr22.000352.18801@sparky.IMD.Sterling.COM>
Date: 22 Apr 91 00:03:52 GMT
Approved: kent@sparky.imd.sterling.com
X-Checksum-Snefru: c9edc553 515fddfb a3b7e3e6 e79d52a3
Submitted-by: Dan Heller <argv@zipcode.com>
Posting-number: Volume 18, Issue 68
Archive-name: mush/part11
Supersedes: mush: Volume 12, Issue 28-47
#!/bin/sh
# do not concatenate these parts, unpack them in order with /bin/sh
# file loop.c continued
#
if test ! -r _shar_seq_.tmp; then
echo 'Please unpack part 1 first!'
exit 1
fi
(read Scheck
if test "$Scheck" != 11; then
echo Please unpack part "$Scheck" next!
exit 1
else
exit 0
fi
) < _shar_seq_.tmp || exit 1
if test ! -f _shar_wnt_.tmp; then
echo 'x - still skipping loop.c'
else
echo 'x - continuing file loop.c'
sed 's/^X//' << 'SHAR_EOF' >> 'loop.c' &&
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 /* This is the intent of the following loop:
X * tmp = (int)(p2 - str) + 1;
X * (void) strncpy(p + !final, str, tmp);
X * The strncpy() can't be used directly because
X * it may be overlapping, which fails sometimes.
X */
X /* copy up to and including quote */
X for (tmp = 0; tmp < (int)(p2 - str) + 1; tmp++)
X p[tmp+!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 * Report a history parsing error.
X * Suppress the message if nonobang is true.
X */
#define hist_error if (nonobang) {;} else print
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 */
char *
reference_hist(str, buf, hist_ref)
register char *str, **hist_ref;
char buf[];
{
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
/* 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 */
char *
hist_from_str(str, hist_number)
register char *str;
register int *hist_number;
{
X register char *p = NULL, c = 0;
X int full_search = 0, len, found;
X char buf[BUFSIZ];
X struct history *hist;
#ifndef REGCMP
X extern char *re_comp();
#else
X char *rex = NULL;
X extern char *regcmp();
#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) {
#ifndef REGCMP
X if (re_comp(str))
#else
X if (!(rex = regcmp(str, NULL))) /* Assign and test */
#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 =
#ifndef REGCMP
X re_exec(buf)
#else
X !!regex(rex, buf, NULL) /* convert to boolean value */
#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
disp_hist(n, argv) /* argc not used -- use space for the variable, "n" */
register int n;
char **argv;
{
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 Debug("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
init_history(newsize)
{
X if ((hist_size = newsize) < 1)
X hist_size = 1;
}
SHAR_EOF
echo 'File loop.c is complete' &&
chmod 0644 loop.c ||
echo 'restore of loop.c failed'
Wc_c="`wc -c < 'loop.c'`"
test 34934 -eq "$Wc_c" ||
echo 'loop.c: original size 34934, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= macros.c ==============
if test -f 'macros.c' -a X"$1" != X"-c"; then
echo 'x - skipping macros.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting macros.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'macros.c' &&
/* (@)# macros.c (c) copyright 9/19/88 (Bart Schaefer, Dan Heller) */
X
#include "bindings.h"
#include "mush.h"
X
extern struct cmd_map map_func_names[];
X
struct cmd_map *mac_stack, *mac_hide;
X
/*
X * print current binding to macro mappings if "str" is NULL.
X * else return the string "x_str" which the str is bound to.
X */
char *
c_macro(name, str, opts)
char *name;
register char *str;
register struct cmd_map *opts;
{
X register int incurses = iscurses;
X char buf[MAX_MACRO_LEN], buf2[sizeof buf * 3];
X
X if (!str) {
X for (; opts; opts = opts->m_next)
X if (opts->m_cmd == C_MACRO)
X break;
X if (!opts) {
X print("No %s settings.\n", name);
X return (char *)(-1);
X }
X if (incurses)
X clr_bot_line(), iscurses = FALSE;
X (void) do_pager(NULL, TRUE);
X (void) do_pager(sprintf(buf, "\nCurrent %s settings:\n\n",name), FALSE);
X }
X
X if (!opts)
X return NULL;
X
X for (; opts; opts = opts->m_next) {
X if (opts->m_cmd != C_MACRO)
X continue;
X if (!str) {
X (void) do_pager(sprintf(buf, "%-20.20s ",
X ctrl_strcpy(buf2, opts->m_str, FALSE)), FALSE);
X if (do_pager(sprintf(buf, "%s\n",
X ctrl_strcpy(buf2, opts->x_str, TRUE)), FALSE) == EOF)
X break;
X } else {
X if (strcmp(str, opts->m_str))
X continue;
X else
X return opts->x_str;
X }
X }
X iscurses = incurses;
X if (str)
X (void) do_pager(NULL, FALSE);
X return NULL;
}
X
mac_push(str)
register char *str;
{
X register struct cmd_map *tmp;
X
X /* now make a new macro struct and set fields */
X if (!(tmp = (struct cmd_map *)calloc((unsigned)1,sizeof(struct cmd_map)))) {
X error("calloc");
X return -1;
X }
X tmp->m_next = mac_stack;
X mac_stack = tmp;
X tmp->x_str = savestr(str); /* x_str is the text of the expansion */
X tmp->m_str = tmp->x_str; /* m_str is the current read position */
X
X /*
X * Save the current state of the glob_flags so
X * mac_push() can also serve as unget of stdin
X */
X tmp->m_cmd = glob_flags;
X return 0;
}
X
mac_queue(str)
register char *str;
{
X register struct cmd_map **tmp; /* NOTE pointer to pointer! */
X
X /* Find the bottom of the macro stack */
X for (tmp = &mac_stack; *tmp; tmp = &((*tmp)->m_next))
X ;
X /* now make a new macro struct and set fields */
X if (!(*tmp =
X (struct cmd_map *)calloc((unsigned)1,sizeof(struct cmd_map)))) {
X error("calloc");
X return -1;
X }
X (*tmp)->m_next = (struct cmd_map *)0; /* calloc should do this .... */
X (*tmp)->x_str = savestr(str); /* x_str is the text of the expansion */
X (*tmp)->m_str = (*tmp)->x_str; /* m_str is the current read position */
X
X /*
X * Save the current state of the glob_flags
X */
X (*tmp)->m_cmd = glob_flags;
X return 0;
}
X
void
mac_pop()
{
X register struct cmd_map *tmp;
X
X if (mac_stack) {
X tmp = mac_stack;
X mac_stack = tmp->m_next;
X xfree(tmp->x_str);
X xfree((char *) tmp);
X }
X /*
X * Restore saved MACRO glob_flags only (see mac_push())
X */
X if (mac_stack) {
X if (ison(mac_stack->m_cmd, IN_MACRO))
X turnon(glob_flags, IN_MACRO);
X else
X turnoff(glob_flags, IN_MACRO);
X if (ison(mac_stack->m_cmd, LINE_MACRO))
X turnon(glob_flags, LINE_MACRO);
X else
X turnoff(glob_flags, LINE_MACRO);
X if (ison(mac_stack->m_cmd, QUOTE_MACRO))
X turnon(glob_flags, QUOTE_MACRO);
X else
X turnoff(glob_flags, QUOTE_MACRO);
X }
}
X
/* Abandon macro processing */
void
mac_flush()
{
X while (mac_stack)
X mac_pop();
X if (mac_hide) {
X mac_stack = mac_hide;
X mac_hide = NULL_MAP;
X while (mac_stack)
X mac_pop();
X }
X turnoff(glob_flags, IN_MACRO);
X turnoff(glob_flags, LINE_MACRO);
X turnoff(glob_flags, QUOTE_MACRO);
}
X
/* Check for pending input from a macro. */
mac_pending()
{
X register struct cmd_map *msp;
X
X for (msp = mac_stack; msp && !*(msp->m_str); msp = msp->m_next)
X ;
X
X return !!msp;
}
X
/* Get input and treat it as a macro. */
get_mac_input(newline)
int newline; /* 1 if newline to be appended, 0 otherwise */
{
X register int len;
X char buf[MAX_MACRO_LEN];
X
X /* This call cannot be nested */
X if (mac_hide)
X return -1;
X
X /* Hide the mac_stack so input comes from stdin */
X mac_hide = mac_stack; mac_stack = NULL_MAP;
X
X if ((len = Getstr(buf, MAX_MACRO_LEN - 1, 0)) < 0)
X return len;
X if (newline) {
X buf[len++] = '\n';
X buf[len] = 0;
X }
X
X /* Restore the mac_stack */
X if (mac_stack) {
X /*
X * Somehow, a push happened even though mac_hide was
X * nonzero -- maybe by line wrap? Fix it as best we can.
X */
X struct cmd_map *msp;
X for (msp = mac_stack; msp->m_next; msp = msp->m_next)
X ;
X msp->m_next = mac_hide;
X } else
X mac_stack = mac_hide;
X mac_hide = NULL_MAP;
X
X /* Restore saved flags */
X if (mac_stack) {
X if (ison(mac_stack->m_cmd, IN_MACRO))
X turnon(glob_flags, IN_MACRO);
X else
X turnoff(glob_flags, IN_MACRO);
X if (ison(mac_stack->m_cmd, LINE_MACRO))
X turnon(glob_flags, LINE_MACRO);
X else
X turnoff(glob_flags, LINE_MACRO);
X }
X if (len > 0)
X Ungetstr(buf);
X
X return 1;
}
X
/* getchar() substitute -- reads from the current macro if one is active,
X * otherwise does a getchar().
X *
X * NOTE: In the mac_stack, x_str is the saved text of the current macro,
X * and m_str is the current read position within the macro.
X */
m_getchar()
{
X int c;
X
X while (mac_stack && (! *(mac_stack->m_str)))
X mac_pop();
X if (mac_stack) {
X c = *((mac_stack->m_str)++);
X return c;
X } else {
X turnoff(glob_flags, IN_MACRO);
X turnoff(glob_flags, LINE_MACRO);
X turnoff(glob_flags, QUOTE_MACRO);
X while ((c = getchar()) == 0) /* Ignore NUL chars from stdin */
X ; /* until better solution found */
X return c;
X }
}
X
m_ungetc(c)
char c;
{
X if (mac_stack && (mac_stack->m_str > mac_stack->x_str))
X *(--(mac_stack->m_str)) = c;
X else
X (void) ungetc(c, stdin);
}
X
/*
X * Try to read a long command; assumes MAC_LONG_CMD already seen.
X * On immediate failure, return 0.
X * On failure after reading some input, return less than zero.
X * On success, return greater than 0.
X * The absolute value of the return is the number of chars placed in buf.
X */
read_long_cmd (buf)
char *buf;
{
X register char c, *p = buf;
X register int count = 0;
X
X /*
X * Test in_macro() in this loop because the _entire_
X * long command _must_ be in the macro -- if we run
X * out of macro in mid-long-command, it is an error.
X */
X while (in_macro() && (count < MAX_LONG_CMD - 1)
X && ((c = m_getchar()) != MAC_LONG_END)) {
X *p++ = c; ++count;
X }
X *p = '\0';
X if (c != MAC_LONG_END)
X return (-count);
X return count;
}
X
/*
X * Identify and possibly execute a reserved long macro command
X * Executes if do_exec is true. Otherwise, just parse.
X */
reserved_cmd (buf, do_exec)
char *buf;
{
X int ret = 1;
X
X if (!strcmp(buf, MAC_GET_STR)) {
X if (do_exec)
X ret = get_mac_input(0);
X } else if (!strcmp(buf, MAC_GET_LINE)) {
X if (do_exec)
X ret = get_mac_input(1);
X } else
X ret = 0;
X return ret;
}
X
#ifdef CURSES
X
/*
X * Identify (and possibly execute, if reserved) curses mode commands
X * that appear in macro strings enclosed by MAC_LONG_CMD and
X * MAC_LONG_END. Return the binding of the command.
X */
long_mac_cmd (c, do_exec)
int c;
{
X char buf[MAX_LONG_CMD];
X register int count, binding;
X int y, x;
X
X if (c != MAC_LONG_CMD)
X return C_ERROR;
X
X if ((count = read_long_cmd(buf)) <= 0) {
X print("Invalid long macro command");
X if (ison(glob_flags, CNTD_CMD))
X putchar('\n');
X if (do_exec)
X mac_flush();
X return C_ERROR;
X }
X
X if (do_exec) {
X if (ison(glob_flags, CNTD_CMD))
X clr_bot_line();
X getyx(stdscr, y, x);
X move(LINES - 1, 0);
X }
X if (reserved_cmd(buf, do_exec)) {
X if (do_exec) {
X if (isoff(glob_flags, CNTD_CMD))
X move(y, x);
X return getcmd();
X } else
X return C_NULL;
X } else if (do_exec)
X move(y, x);
X
X /* Can start at C_NULL because of "no-op" command */
X for (count = 0; count <= C_HELP; count++) {
X if (!strcmp(buf, map_func_names[count].m_str)) {
X binding = (int)map_func_names[count].m_cmd;
X break;
X }
X }
X /* Don't allow C_MACRO to be called directly */
X if (count > C_HELP || binding == C_MACRO) {
X print("Invalid long macro command");
X if (ison(glob_flags, CNTD_CMD))
X putchar('\n');
X return C_ERROR;
X } else
X return binding;
}
X
#endif /* CURSES */
X
/*
X * Check the validity of a macro binding as far as possible
X */
check_mac_bindings(buf)
char *buf;
{
X int ok = TRUE;
X
X while (ok && buf && *buf) {
X if (*buf == MAC_LONG_CMD) {
X char *i;
#ifdef CURSES
X int count;
#endif /* CURSES */
X
X if (ok)
X ok = ((i = index(++buf, MAC_LONG_END)) != NULL);
X if (i)
X *i = '\0'; /* Don't worry, we'll fix it */
X else
X return ok;
#ifdef CURSES
X /* OK to start at C_NULL because of "no-op" command */
X for (count = 0; count <= C_HELP; count++)
X if (! strcmp(buf, map_func_names[count].m_str))
X break;
X /* Don't allow C_MACRO to be called directly */
X if (count == C_MACRO)
X ok = FALSE;
X else if (count > C_HELP)
#endif /* CURSES */
X if (ok && !(ok = reserved_cmd(buf, FALSE)))
X wprint("Warning: unrecognized curses command: \"%s\"\n", buf);
X buf = i;
X *buf++ = MAC_LONG_END;
X } else if (*buf++ == '\\' && *buf)
X ++buf;
X }
X return ok;
}
SHAR_EOF
chmod 0644 macros.c ||
echo 'restore of macros.c failed'
Wc_c="`wc -c < 'macros.c'`"
test 9279 -eq "$Wc_c" ||
echo 'macros.c: original size 9279, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= mail.c ==============
if test -f 'mail.c' -a X"$1" != X"-c"; then
echo 'x - skipping mail.c (File already exists)'
rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting mail.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'mail.c' &&
/* @(#)mail.c (c) copyright 1986 (Dan Heller) */
X
#include "mush.h"
X
/*
X * mail.c --
X * do_mail() external interface to these functions; parses options.
X * mail_someone() called from do_mail() to begin composing the message.
X * start_file() creates the editing file and reset signal catching.
X * add_to_letter() adds the next line to letter --determine ~ escapes.
X * finish_up_letter() prompts for Cc:, verifies send, adds signatures.
X * send_it() expands aliases, invokes mailer, sends to record file.
X * add_headers() adds all headers to a FILE *, reads draft files
X * rm_edfile() signals are directed here. remove letter, longjmp
X * dead_letter() make a dead.letter if mail failed.
X */
X
static char Subject[BUFSIZ],To[HDRSIZ],Cc[HDRSIZ],Bcc[HDRSIZ],in_reply_to[256];
static int killme;
static u_long flags;
static SIGRET (*oldterm)(), (*oldint)(), (*oldquit)();
static int finish_up_letter(), send_it(), start_file();
static long add_headers();
static jmp_buf cntrl_c_buf;
static char *Hfile, *edfile;
FILE *ed_fp;
char *hfile, *mktemp();
X
/* argc and argv could be null if coming from tool mode compose */
do_mail(n, argv, list)
register int n; /* no need for "argc", so use the space for a variable */
register char **argv, *list;
{
X char firstchar = (argv)? **argv: 'm';
X char *to = NULL, *cc = NULL, *addcc = NULL, *bcc = NULL, *subj = NULL;
X char *route = NULL;
X char inc_list[MAXMSGS_BITS], buf[HDRSIZ];
X u_long flgs = 0;
X
X if (ison(glob_flags, IS_GETTING)) {
X wprint("You must finish the letter you are editing first.\n");
X return -1;
X }
X if (ison(glob_flags, DO_PIPE)) {
X wprint("You can't pipe out of the %s command.\n", argv? *argv : "mail");
X return -1;
X }
X clear_msg_list(inc_list);
X hfile = Hfile = NULL;
X
X /* If piped to mail, include the messages piped */
X if (ison(glob_flags, IS_PIPE) ||
X (lower(firstchar) == 'r' && do_set(set_options, "autoinclude"))) {
X turnon(flgs, INCLUDE);
X bitput(list, inc_list, msg_cnt, =);
X }
X /* Set SIGN and DO_FORTUNE now so we can turn them off later */
X if (do_set(set_options, "autosign"))
X turnon(flgs, SIGN);
X if (do_set(set_options, "fortune"))
X turnon(flgs, DO_FORTUNE);
X while (argv && *argv && *++argv && **argv == '-') {
X n = 1;
X while (n && argv[0][n])
X switch (argv[0][n]) {
#ifdef VERBOSE_ARG
X case 'v': turnon(flgs, VERBOSE); n++; break;
#endif /* VERBOSE_ARG */
X case 'H':
X if (argv[1]) {
X n = 0;
X Hfile = *++argv;
X } else {
X wprint("Must specify a file\n");
X return -1;
X }
X when 'h':
X if (argv[1]) {
X n = -1; /* it gets incremented below */
X hfile = savestr(*++argv);
X if (ison(glob_flags, REDIRECT)) {
X turnoff(glob_flags, REDIRECT);
X turnon(flgs, SEND_NOW);
X }
X } else {
X wprint("Must specify a file containing headers\n");
X return -1;
X }
X /* Fall through */
X case 'E': turnon(flgs, EDIT_HDRS); n++;
X when 'e': turnon(flgs, EDIT); n++;
X when 'F': turnon(flgs, DO_FORTUNE); n++;
X when 'b':
X if (argv[1]) {
X n = 0, bcc = *++argv;
X fix_up_addr(bcc);
X } else {
X wprint("Must specify blind-carbon list\n");
X return -1;
X }
X when 'c':
X if (argv[1]) {
X n = 0, addcc = *++argv;
X fix_up_addr(addcc);
X } else {
X wprint("Must specify carbon-copy list\n");
X return -1;
X }
X when 's':
X if (argv[1])
X n = 0, subj = *++argv;
X else
X n++, turnon(flgs, NEW_SUBJECT);
X when 'i': case 'I': case 'f': {
X int m;
X if (!msg_cnt) {
X wprint("No message to include!\n");
X return -1;
X }
X if (argv[0][n] == 'i') {
X turnon(flgs, INCLUDE);
X turnoff(flgs, INCLUDE_H);
X turnoff(flgs, FORWARD);
X } else if (argv[0][n] == 'I') {
X turnon(flgs, INCLUDE_H);
X turnoff(flgs, INCLUDE);
X turnoff(flgs, FORWARD);
X } else if (argv[0][n] == 'f') {
X turnon(flgs, FORWARD);
X turnon(flgs, SEND_NOW);
X turnoff(flgs, INCLUDE_H);
X turnoff(flgs, INCLUDE);
X }
X /* "-i 3-5" or "-i3-5" Consider the latter case first */
X if (!argv[0][++n])
X argv++, n = 0;
X (*argv) += n;
X m = get_msg_list(argv, inc_list);
X (*argv) -= n;
X if (m == -1)
X return -1;
X /* if there were args, then go back to the first char
X * in the next argv
X */
X if (m)
X n = 0;
X if (!n) /* n may be 0 from above! */
X argv += (m-1);
X }
X when 'U':
X turnon(flgs, SEND_NOW);
X n++;
X when 'u':
X turnoff(flgs, SIGN);
X turnoff(flgs, DO_FORTUNE);
X n++;
X when 'r':
X if (lower(firstchar) == 'r') {
X route = *++argv;
X n = 0;
X break;
X }
X /* fall thru */
X default:
X if (argv[0][n] != '?') {
X wprint("%c: unknown option\n\n", argv[0][n]);
X return -1;
X } else
X return help(0, "mail", cmd_help);
X }
X }
X if (isoff(flgs, FORWARD)) {
X if (ison(flgs, SEND_NOW)) {
X if (!hfile && !Hfile) {
X wprint("Can't send immediately without draft file.\n");
X return -1;
X }
X turnoff(flgs, EDIT); /* -U overrides -e */
X } else if (do_set(set_options, "autoedit"))
X turnon(flgs, EDIT);
X } else if (ison(flgs, EDIT)) /* -e modifies -f */
X turnoff(flgs, SEND_NOW);
#ifdef VERBOSE_ARG
X if (do_set(set_options, "verbose"))
X turnon(flgs, VERBOSE);
#endif /* VERBOSE_ARG */
X *in_reply_to = *To = *Subject = *Cc = *Bcc = 0;
X if (lower(firstchar) == 'r') {
X char *in_reply_fmt, *pcc = NULL;
X to = To, cc = Cc;
X /*
X * Generate a reply to all the messages passed to respond(). This
X * list is different than the include-msg list above. Get info about
X * whom the messages were sent to for reply-all.
X * BUG: currently, redundant addresses aren't pruned from Bcc list!
X */
X for (n = 0; n < msg_cnt; n++)
X if (msg_bit(list, n)) {
X if (to != To)
X *to++ = ',', *to++ = ' ';
X (void) reply_to(n, (firstchar == 'R'), buf);
X if (strlen(buf) + (to - To) > sizeof(To) - 1) {
X wprint("# recipients exceeded at msg %d\n", n);
X break;
X }
X to += Strcpy(to, buf);
X if (firstchar == 'R') {
X if (pcc = cc_to(n, buf)) {
X /* if there was a previous cc, append ", " */
X if (cc != Cc)
X *cc++ = ',', *cc++ = ' ';
X if (strlen(pcc) + (cc - Cc) > sizeof(Cc) - 1)
X wprint("# Cc's exceeded at msg %d\n", n);
X else
X cc += Strcpy(cc, pcc);
X }
X }
X /* remove redundant addresses now, or headers could get too
X * long before the list runs out (it still might)
X */
X rm_redundant_addrs(To, Cc);
X to = To + strlen(To);
X cc = Cc + strlen(Cc);
X }
X /* clean up end of Cc line for replyall's */
X while (*cc == ' ' || *cc == ',')
X *cc-- = '\0';
X if (firstchar == 'R' && !do_set(set_options, "metoo")) {
X /* Each reply_to() call above will leave at least
X * one person in To. If that one person was us,
X * we need to get removed from the complete list.
X */
X (void) take_me_off(to);
X }
X to = To, cc = Cc;
X if (route || (route = do_set(set_options, "auto_route")))
X /* careful! This routine could add lots-o-bytes and lose addresses
X * to avoid writing out of segment.
X */
X route_addresses(To, Cc, route);
X if (in_reply_fmt = do_set(set_options, "in_reply_to"))
X /* "9" here is a magic # --see compose_hdr() */
X (void) strcpy(in_reply_to,
X format_hdr(current_msg, in_reply_fmt, FALSE)+9);
X }
X if (ison(flgs, FORWARD) && ison(flgs, EDIT) ||
X lower(firstchar) == 'r' && isoff(flgs, NEW_SUBJECT)) {
X turnoff(flgs, NEW_SUBJECT);
X if (subj && *subj && (isoff(flgs, FORWARD) || ison(flgs, EDIT)))
X subj = strcpy(Subject, subj);
X else if (subj = subject_to(current_msg, buf))
X subj = strcpy(Subject, buf + 4*(lower(firstchar) != 'r'));
X } else if (isoff(flgs, NEW_SUBJECT) && isoff(flgs, FORWARD) &&
X (do_set(set_options, "ask") || do_set(set_options, "asksub")))
X turnon(flgs, NEW_SUBJECT);
X if (argv && *argv) {
X char buf[HDRSIZ];
X (void) argv_to_string(buf, argv);
X fix_up_addr(buf);
X to = &To[strlen(To)];
X if (*To)
X *to++ = ',', *to++ = ' ';
X (void) strcpy(to, buf);
X to = To;
X }
X if (addcc && *addcc) {
X cc = &Cc[strlen(Cc)];
X if (*Cc)
X *cc++ = ',', *cc++ = ' ';
X (void) strcpy(cc, addcc); /* addcc has already been fixed up */
X cc = Cc;
X }
X /* remove any redundant addresses that just got added */
X rm_redundant_addrs(To, Cc);
X if (bcc && *bcc)
X (void) strncpy(Bcc, bcc, sizeof(Bcc)); /* bcc already fixed up */
X bcc = Bcc;
X
X return mail_someone(to, subj, cc, bcc, flgs, inc_list);
}
X
static
mail_someone(to, subject, cc, bcc, flgs, list)
register char *to, *subject, *cc, *bcc, *list;
u_long flgs;
{
X register char *p;
X
X flags = flgs;
X if (to && *to) {
X if (!*To)
X (void) strncpy(To, to, sizeof(To));
X } else
X to = NO_STRING;
X if (subject && *subject) {
X if (!*Subject)
X (void) strncpy(Subject, subject, sizeof(Subject));
X } else
X subject = NO_STRING;
X if (cc && *cc) {
X if (!*Cc)
X (void) strncpy(Cc, cc, sizeof(Cc));
X } else
X Cc[0] = '\0';
X if (bcc && *bcc) {
X if (!*Bcc)
X (void) strncpy(Bcc, bcc, sizeof(Bcc));
X } else
X Bcc[0] = '\0';
X
X if (ison(glob_flags, REDIRECT)) {
X /*
X * NOTE: Could change this to finish_up_letter() to allow
X * signatures to be appended. The -U! option to mush would
X * be extended to suppress signing when redirection is on.
X */
X int sent = send_it();
X if (sent == -1) {
X wprint("Message not sent!\n");
X rm_edfile(-1);
X }
X return sent;
X }
X /* if (!*to) then prompting will be done */
X if (!istool && !hfile) {
X if (p = set_header("To: ", to, !*to)) {
X if (!*to) /* if user typed To-line here, fix up the addresses */
X fix_up_addr(p);
X (void) strcpy(To, p);
X }
X if (!*To && ison(flags, FORWARD) && ison(flags, SEND_NOW)) {
X turnoff(flags, SEND_NOW); /* user must edit To: line or do again */
X print("(You must add a To: address.)\n");
X }
X /* don't prompt for subject if forwarding mail */
X if (isoff(flags, FORWARD) && (*subject || ison(flags, NEW_SUBJECT)) &&
X (p = set_header("Subject: ", subject,
X !*subject && ison(flags, NEW_SUBJECT))))
X (void) strcpy(Subject, p);
X if (*Cc || ison(flags, EDIT_HDRS) && do_set(set_options, "askcc")) {
X if ((p = set_header("Cc: ", cc, !*Cc)) && *p) {
X fix_up_addr(p);
X (void) strcpy(Cc, p);
X }
X }
X if (*Bcc)
X print("Bcc: %s\n", Bcc);
X putchar('\n');
X }
X
X /* If forwarding w/o editing, start a new file for each. */
X if (ison(flags, FORWARD) && ison(flags, SEND_NOW)) {
X char fwd[MAXMSGS_BITS];
X register int i;
X clear_msg_list(fwd);
X for (i = 0; i < msg_cnt; i++)
X if (msg_bit(list, i)) {
X set_msg_bit(fwd, i);
X if (start_file(fwd) < 0)
X return -1;
X turnon(msg[i].m_flags, FORWARD);
X if (isoff(glob_flags, READ_ONLY))
X turnon(glob_flags, DO_UPDATE);
X clear_msg_list(fwd);
X }
X } else
X return start_file(list);
X return 0;
}
X
static
start_file(list)
char *list;
{
X register char *dir;
X register int i;
X char line[MAXPATHLEN];
X int had_hfile = FALSE;
X
X /* getdir() uses the home directory if no tmpdir */
X if (!(dir = getdir(do_set(set_options, "tmpdir"))))
alted:
X dir = ALTERNATE_HOME;
X (void) mktemp(sprintf(line, "%s/%s", dir, EDFILE));
X strdup(edfile, line);
X if (!(ed_fp = mask_fopen(edfile, "w+"))) {
X if (strcmp(dir, ALTERNATE_HOME))
X goto alted;
X error("can't create %s", edfile);
X return -1;
X }
X if (!istool) {
X oldint = signal(SIGINT, rm_edfile);
X oldquit = signal(SIGQUIT, rm_edfile);
X oldterm = signal(SIGTERM, rm_edfile);
X }
X
X if (istool && isoff(flags, SEND_NOW) ||
X (isoff(flags, FORWARD) || isoff(flags, SEND_NOW)) &&
X (ison(flags, EDIT_HDRS) || do_set(set_options, "edit_hdrs"))) {
X turnon(flags, EDIT_HDRS);
X if (hfile)
X had_hfile = TRUE;
X if (add_headers(NULL_FILE, &ed_fp, 1, flags) == (long) -1)
X return -1;
X }
X if (Hfile) {
X (void) file_to_fp(Hfile, ed_fp, "r");
X Hfile = NULL;
X had_hfile = TRUE;
X }
X if (istool && isoff(flags, SEND_NOW))
X strdup(hfile, edfile);
X
X /* if flags call for it, include current message (with header?) */
X if (ison(flags, INCLUDE|FORWARD|INCLUDE_H)) {
X long copy_flgs = 0, is_forw = ison(flags, FORWARD);
X char buf[sizeof(To)];
X if (!is_forw) {
X turnon(copy_flgs, INDENT);
X if (ison(flags, INCLUDE_H) &&
X !chk_option("alwaysignore", "include"))
X turnon(copy_flgs, NO_IGNORE);
X else if (ison(flags, INCLUDE))
X turnon(copy_flgs, NO_HEADER);
X } else if (ison(flags, SEND_NOW) ||
X !chk_option("alwaysignore", "forward"))
X turnon(copy_flgs, FORWARD); /* FORWARD implies NO_IGNORE */
#ifdef MSG_SEPARATOR
X turnon(copy_flgs, NO_SEPARATOR);
#endif /* MSG_SEPARATOR */
X for (i = 0; i < msg_cnt; i++)
X if (msg_bit(list, i)) {
X if (is_forw && isoff(flags, SEND_NOW)) {
X (void) reply_to(i, FALSE, buf);
X (void) fprintf(ed_fp,"--- Forwarded mail from %s\n\n",buf);
X }
X wprint("%sing message %d ...",
X is_forw? "forward" : "includ", i+1);
X wprint("(%d lines)\n",
X copy_msg(i, ed_fp, (u_long) copy_flgs, NULL));
X set_isread(i); /* if we included it, we read it, right? */
X if (is_forw && isoff(flags, SEND_NOW))
X (void) fprintf(ed_fp,
X "\n--- End of forwarded message from %s\n", buf);
X if (!is_forw || isoff(flags, SEND_NOW))
X (void) fputc('\n', ed_fp);
X }
X (void) fflush(ed_fp);
X }
X if (!istool && ison(glob_flags, WARNING)) {
X if (escape && strncmp(escape, DEF_ESCAPE, 1))
X print("(escape character is set to `%c')\n", *escape);
X if (wrapcolumn && wrapcolumn < 20)
X print("(warning: wrapping only %d columns from the left!)\n",
X wrapcolumn);
X }
X
X /* do an "if" again in case editor not found and EDIT turned off */
X if (!istool && ison(flags, EDIT)) {
X char **argv, *edit;
X int argc;
X if ((!(edit = do_set(set_options, "visual")) || !*edit) &&
X (!(edit = do_set(set_options, "editor")) || !*edit))
X edit = DEF_EDITOR;
X (void) sprintf(line, "%s %s", edit, edfile);
X if ((argv = mk_argv(line, &argc, FALSE)) && argc > 0) {
X print("Starting \"%s\"...\n", argv[0]);
X (void) fclose(ed_fp);
X ed_fp = NULL_FILE;
X execute(argv);
X free_vec(argv);
X turnoff(flags, EDIT);
X turnoff(flags, FORWARD); /* forwarded messages must be unedited */
X /* upon exit of editor, user must now type eofc or "." to send */
X if (!(ed_fp = fopen(edfile, "r+"))) {
X error("can't reopen %s", edfile);
X return -1;
X }
X (void) fseek(ed_fp, 0L, 2);
X } else
X print("Unable to start \"%s\"\n", edit);
X wprint("(continue editing letter or ^%c to send)\n", eofc + '@');
X } else if (ison(flags, SEND_NOW)) {
X /* if finish_up_letter() was successful, file was successfully sent. */
X if (!setjmp(cntrl_c_buf) && finish_up_letter() == 0) {
X rm_edfile(0);
X return 0;
X }
X } else if (had_hfile) {
X /* it's not obvious what's going on -- enlighten user */
X wprint("(continue editing or ^%c to send)\n", eofc + '@');
X }
X
#ifdef SUNTOOL
X if (istool) {
X /* toolmode doesn't care if SEND_NOW -- user continues to edit file.
X * if SEND_NOW is not set, then the editor file has just been started,
X * so again, just return so user can edit file.
X */
X if (ed_fp)
X fclose(ed_fp), ed_fp = NULL_FILE;
X turnon(glob_flags, IS_GETTING);
X return 0;
X }
#endif /* SUNTOOL */
X if (ison(flags, SEND_NOW)) {
X /* editing couldn't have been on -- finish_up_letter() failed */
X rm_edfile(0 - ison(flags, FORWARD));
X return -1;
X }
X
X i = 0;
X turnon(glob_flags, IS_GETTING);
X do {
X /* If the user hits ^C in cbreak mode, mush will return to
X * Getstr and not clear the buffer. whatever is typed next will
X * be appended to the line. jumping here will force the line to
X * be cleared cuz it's a new call.
X */
X (void) setjmp(cntrl_c_buf);
X while (Getstr(line, sizeof(line), 0) > -1) {
X if (!istool) /* toolmode checks on a timer -- don't do it here */
X (void) check_new_mail(); /* if new mail comes in, get it */
X if ((i = add_to_letter(line)) <= 0)
X break;
X }
X } while (i >= 0 && finish_up_letter() == -1);
X turnoff(glob_flags, IS_GETTING);
X return i; /* return -1 if ~x or ~q to terminate letter */
}
X
char *tilde_commands[] = {
X "commands: [OPTIONAL argument]",
X "t [list]\tChange list of recipients",
X "s [subject]\tModify [set] subject header",
X "c [cc list]\tModify [set] carbon copy recipients",
X "b [bcc list]\tModify [set] blind carbon recipients",
X "h\t\tModify all message headers",
X "e [editor]\tEnter editor. Editor used: \"set editor\", env EDITOR, vi",
X "v [editor]\tEnter visual editor. \"set visual\", env VISUAL, vi",
X "u\t\tEdit previous (last) line in file.",
X "p [pager]\tPage message; pager used: \"set pager\", env. PAGER, more",
X "i [msg#'s]\tInclude current msg body [msg#'s] indented by \"indent_str\"",
X "I [msg#'s]\tSame, but include the message headers from included messages",
X "f [msg#'s]\tForward mail. Not indented, but marked as \"forwarded mail\"",
X "S[!]\t\tInclude Signature file [suppress file]",
X "F[!]\t\tAdd a fortune at end of letter [don't add]",
X "w file\t\tWrite msg buffer to file name",
X "a file\t\tAppend msg buffer to file name",
X "r file\t\tRead filename into message buffer",
X "q \t\tQuit message; save in dead.letter (unless \"nosave\" is set).",
X "x \t\tQuit message; don't save in dead.letter.",
X "$variable\tInsert the string value for \"variable\" into message.",
X ":cmd\t\tRun the mail command \"cmd\".",
X "|cmd\t\tPipe the message through the unix command \"cmd\".",
X "E[!]\t\tClear contents of letter after saving to dead.letter [unless !].",
X 0
};
X
/*
X * TC_EDIT(tc) returns TRUE if tilde_command[tc] involves message
X * editing operations. Used when EDIT_HDRS is active.
X */
#define TC_EDIT(tc) ((tc) && ((tc) < 6 || !tilde_commands[(tc)+1]))
X
/*
X * Add the line (char *) parameter to the letter. Determine tilde
X * escapes and determine what to do. This function returns 0 to
X * indicate user wants to end the letter, -1 if the letter cannot
X * be sent (~q, ~x no buffer after editor, etc...) or 1 to indicate
X * successful addition of the line to the letter.
X * This function may be called by toolmode just to change certain mush
X * internal variables via tilde escapes. Thus, ed_fp might be null.
X */
add_to_letter(line)
char line[];
{
X register char *p;
X char buf[HDRSIZ > MAXPATHLEN ? HDRSIZ : MAXPATHLEN];
X
X killme = 0;
X if (ed_fp) /* may be null if istool */
X (void) fseek(ed_fp, 0L, 2);
X
X if (!strcmp(line, ".") && do_set(set_options, "dot"))
X return 0;
X if (line[0] != *escape || ison(glob_flags, QUOTE_MACRO)) {
X (void) fputs(line, ed_fp);
X (void) fputc('\n', ed_fp);
X (void) fflush(ed_fp);
X return 1;
X }
X /* all commands are "~c" (where 'c' is the command). set p = first
X * character after 'c' and skip whitespace
X */
X p = &line[2];
X skipspaces(0);
X switch (line[1]) {
X case 'v' : case 'p': case 'e' : case '|' : {
X if (!*p || *p == 'i')
X switch (line[1]) {
X case 'p' :
X if (!*p && !(p = do_set(set_options, "pager")))
X p = DEF_PAGER;
X if (!*p || !strcmp(p, "internal"))
X p = NULL;
X when 'v' :
X if (*p && p[1] || (p = do_set(set_options, "visual")))
X break;
X /* else fall through */
X default :
X if (!(p = do_set(set_options, "editor")) || !*p)
X p = DEF_EDITOR;
X when '|' :
X print("No command for pipe\n");
X return 1;
X }
X if (line[1] == 'p' || line[1] == '|')
X rewind(ed_fp);
X if (line[1] == 'p') {
X (void) do_pager(p, TRUE); /* start the pager "p" */
X if (isoff(flags, EDIT_HDRS)) {
X (void) do_pager(sprintf(buf, "To: %s\n", To), FALSE);
X if (Subject[0])
X (void) do_pager(sprintf(buf, "Subject: %s\n", Subject),
X FALSE);
X if (Cc[0])
X (void) do_pager(sprintf(buf, "Cc: %s\n", Cc), FALSE);
X if (Bcc[0])
X (void) do_pager(sprintf(buf, "Bcc: %s\n", Bcc), FALSE);
X (void) do_pager(strcpy(buf,
X "--------\nMessage contains:\n"),
X FALSE);
X }
X while (fgets(buf, sizeof(buf), ed_fp))
X if (do_pager(buf, FALSE) == EOF)
X break;
X (void) do_pager(NULL, FALSE); /* end pager */
X } else if (line[1] == '|') {
X FILE *pipe_fp;
X (void) sprintf(buf, "( %s ) > %s", p, edfile);
X if (unlink(edfile) < 0) {
X error("Can't unlink %s:", edfile);
X break; /* Drop out of switch */
X }
X if ((pipe_fp = popen(buf, "w")) == NULL_FILE) {
X error("Can't run \"%s\":", p);
X (void) file_to_fp(edfile, ed_fp, "w");
X } else {
X while (fgets(buf, sizeof(buf), ed_fp))
X if (fputs(buf, pipe_fp) == EOF) {
X print("Broken pipe\n");
X break;
X }
X (void) pclose(pipe_fp);
X }
X pipe_fp = ed_fp; /* save ed_fp until we can reopen it */
X if (!(ed_fp = fopen(edfile, "r+"))) {
X error("can't reopen %s", edfile);
X (void) rewind(pipe_fp);
X if (file_to_fp(edfile, pipe_fp, "w") < 0 ||
X !(ed_fp = fopen(edfile, "r+"))) {
X error("can't restore old contents of %s", edfile);
X ed_fp = pipe_fp;
X dead_letter(0);
X return -1;
X }
X }
X (void) fclose(pipe_fp);
X } else {
X char **argv;
X int argc;
X (void) sprintf(buf, "%s %s", p, edfile);
X if ((argv = mk_argv(buf, &argc, FALSE)) && argc > 0) {
X (void) fclose(ed_fp);
X ed_fp = NULL_FILE;
X execute(argv);
X free_vec(argv);
X /* tool will return even tho editor isn't done */
X if (!(ed_fp = fopen(edfile, "r+"))) {
X error("can't reopen %s", edfile);
X return -1;
X }
X } else
X print("Unable to start \"%s\"\n", p);
X }
X }
X when '$': {
X register char *p2;
X if (!(p2 = do_set(set_options, p)))
X print("(%s isn't set)\n", p);
X else
X putstring(p2, ed_fp);
X }
X when ':': {
X char new[MAXMSGS_BITS];
X u_long save_flags = glob_flags;
X
X turnon(glob_flags, IGN_SIGS);
X turnoff(glob_flags, DO_PIPE);
X turnoff(glob_flags, IS_PIPE);
X (void) cmd_line(p, new);
X glob_flags = save_flags;
X }
X when 'i': case 'f': case 'I': case 'm': {
X int n;
X u_long copy_flgs = 0;
X char list[MAXMSGS_BITS];
X
X if (!msg_cnt) {
X wprint("No messages.\n");
X break;
X }
X clear_msg_list(list);
X if (line[1] != 'f') {
X turnon(copy_flgs, INDENT);
X if (line[1] == 'i')
X turnon(copy_flgs, NO_HEADER);
X else if (!chk_option("alwaysignore", "include"))
X turnon(copy_flgs, NO_IGNORE);
X } else if (!chk_option("alwaysignore", "forward"))
X turnon(copy_flgs, NO_IGNORE);
#ifdef MSG_SEPARATOR
X turnon(copy_flgs, NO_SEPARATOR);
#endif /* MSG_SEPARATOR */
X if (!*p)
X set_msg_bit(list, current_msg);
X else if (!do_range(p, list))
X return 1;
X for (n = 0; n < msg_cnt; n++)
X if (msg_bit(list, n)) {
X if (line[1] == 'f') {
X (void) reply_to(n, FALSE, buf);
X (void) fprintf(ed_fp,
X "--- Forwarded mail from %s\n\n", buf);
X }
X wprint("Including message %d ... ", n+1);
X wprint("(%d lines)\n", copy_msg(n, ed_fp, copy_flgs, NULL));
X set_isread(n);
X if (line[1] == 'f')
X (void) fprintf(ed_fp,
X "\n--- End of forwarded message from %s\n\n", buf);
X else
X (void) fputc('\n', ed_fp);
X }
X }
X /* To: Cc: and Bcc: headers */
X when 'b':
X case 't':
X case 'c': {
X char *h = (line[1] == 't')? To : (line[1] == 'c')? Cc : Bcc;
X char *Prompt = line[1] == 't'? "To: " :
X line[1] == 'c'? "Cc: " : "Bcc: ";
X if (ison(flags, EDIT_HDRS)) {
X print("You must use an editor to change your headers.\n");
X break;
X }
X
X if (*p) {
X fix_up_addr(p);
X if (*h)
X (void) sprintf(h+strlen(h), ", %s", p);
X else
X (void) strcpy(h, p);
X } else if (!(p = set_header(Prompt, h, TRUE)) || !*p)
X *h = 0;
X else {
X fix_up_addr(p);
X (void) strcpy(h, p);
X }
X }
X when 's':
X if (ison(flags, EDIT_HDRS)) {
X print("You must use an editor to change your headers.\n");
X break;
X }
X if (*p || (p = set_header("Subject: ", Subject, 1)))
X if (!*p)
X Subject[0] = 0;
X else
X (void) strcpy(Subject, p);
X when 'h':
X if (ison(flags, EDIT_HDRS)) {
X print("You must use an editor to change your headers.\n");
X break;
X }
X while ((p = set_header("To: ", To, 1)) && !*p)
X print("(There must be a recipient.)\n");
X (void) strcpy(To, p);
X if (p = set_header("Subject: ", Subject, 1))
X if (!*p)
X Subject[0] = 0;
X else
X (void) strcpy(Subject, p);
X if (p = set_header("Cc: ", Cc, 1))
X if (!*p)
X Cc[0] = 0;
X else {
X fix_up_addr(p);
X (void) strcpy(Cc, p);
X }
X if (p = set_header("Bcc: ", Bcc, 1))
X if (!*p)
X Bcc[0] = 0;
X else {
X fix_up_addr(p);
X (void) strcpy(Bcc, p);
X }
X when 'F': case 'S' : {
X if (*p == '!')
X turnoff(flags, line[1] == 'F'? DO_FORTUNE : SIGN);
X else
X turnon(flags, line[1] == 'F'? DO_FORTUNE : SIGN);
X wprint("%sadding %s at end of message.\n", *p == '!'? "not " : "",
X line[1] == 'F'? "fortune" : "signature");
X }
X when 'w': case 'a': case 'r':
X if (!*p) {
X print("(you must specify a filename)\n");
X return 1;
X }
X (void) fseek(ed_fp, 0L, 2); /* append */
X (void) file_to_fp(p, ed_fp, (line[1] == 'r')? "r":
X (line[1] == 'w')? "w": "a");
X /* go up one line in the message file and allow the user to edit it */
X when 'u': {
X long newpos, pos = ftell(ed_fp);
X char oldline[256];
X if (pos <= 0L) { /* pos could be -1 if ftell() failed */
X print("(No previous line in file.)\n");
X return 1;
X }
X /* get the last 256 bytes written and read backwards from the
X * current place until '\n' is found. Start by moving past the
X * first \n which is at the end of the line we want to edit
X */
X newpos = max(0, pos - 256L);
X (void) fseek(ed_fp, newpos, L_SET);
X /* don't fgets -- it'll stop at a \n */
X (void) fread(line, sizeof(char), (int)(pos-newpos), ed_fp);
X pos--;
X /* the last char in line should be a \n cuz it was last input */
X if (line[(int)(pos-newpos)] != '\n')
X print("I don't know how, but your last line ended with %c.\n",
X line[(int)(pos-newpos)]);
X else
X line[(int)(pos-newpos)] = 0; /* null terminate \n for ^H-ing */
X for (pos--; pos > newpos && line[(int)(pos-newpos)] != '\n'; pos--)
X ;
X /* we've gone back to the end of the second previous line. Check
X * to see if the char we're pointing to is a \n. It should be, but
X * if it's not, we moved back to the first line of the file.
X */
X if (line[(int)(pos-newpos)] == '\n')
X ++pos;
X /* save the old line that's there in case the user boo-boos */
X (void) strcpy(oldline, &line[(int)(pos-newpos)]);
X /* let set header print out the line and get the input */
X if (!(p = set_header("", &line[(int)(pos-newpos)], TRUE))) {
X print("Something bad happened and I don't know what it is.\n");
X p = oldline;
X } else if (*p == *escape)
X print("(Warning: %c escapes ignored on %cu lines.)\n",
X *escape, *escape);
X /* seek to to the position where the new line will go */
X (void) fseek(ed_fp, pos, L_SET);
X /* put the newly typed line */
X (void) fputs(p, ed_fp); /* don't add \n. padding may be necessary */
X /* if the new line is less than the old line, we're going to do
X * one of two things. The best thing to do is to truncate the
X * file to the end of the new line. Sys-v can't do that, so we
X * pad the line with blanks. May be messy in some cases, but...
X */
X if ((pos = strlen(p) - strlen(oldline)) < 0) {
#ifndef SYSV
X /* add the \n, flush the file, truncate to the current pos */
X (void) fputc('\n', ed_fp);
X (void) fflush(ed_fp);
X (void) ftruncate(fileno(ed_fp), (off_t) ftell(ed_fp));
#else /* SYSV */
X /* pad with blanks to the length of the old line. add \n */
X while (pos++ < 0)
X (void) fputc(' ', ed_fp);
X (void) fputc('\n', ed_fp), (void) fflush(ed_fp);
#endif /* SYSV */
X } else {
X /* the new line is >= the old line, add \n -- no trunc req. */
X (void) fputc('\n', ed_fp);
X (void) fflush(ed_fp);
X }
X return 1;
X }
X /* break; not here cuz of "return" (lint). */
X case 'E':
X if (ison(flags, EDIT_HDRS)) {
X print("You must use an editor to empty the message buffer.\n");
X break;
X }
X if (*p != '!' && !do_set(set_options, "nosave"))
X dead_letter(0);
X if (emptyfile(&ed_fp, edfile) == -1) {
X error(edfile);
X return -1;
X } else
X print("Message buffer empty\n");
X when 'q':
X /* save in dead.letter if nosave not set -- rm_edfile(-2). */
X rm_edfile(-2); /* doesn't return out of tool mode */
X return -1;
X /* break; not stated cuz of "return" (lint) */
SHAR_EOF
true || echo 'restore of mail.c failed'
fi
echo 'End of part 11'
echo 'File mail.c is continued in part 12'
echo 12 > _shar_seq_.tmp
exit 0
exit 0 # Just in case...
--
Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD UUCP: uunet!sparky!kent
Phone: (402) 291-8300 FAX: (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.