home *** CD-ROM | disk | FTP | other *** search
- 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.
-