[comp.sources.unix] v18i031: Mail user's shell version 6.4, Part09/19

rsalz@bbn.com (Rich Salz) (03/17/89)

Submitted-by: Dan Heller <island!argv@sun.com>
Posting-number: Volume 18, Issue 31
Archive-name: mush6.4/part09



#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 9 (of 19)."
# Contents:  bind.c hdrs.c
# Wrapped by rsalz@papaya.bbn.com on Mon Mar 13 19:25:15 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'bind.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bind.c'\"
else
echo shar: Extracting \"'bind.c'\" \(20444 characters\)
sed "s/^X//" >'bind.c' <<'END_OF_FILE'
X/* bind.c */
X
X#include "bindings.h"
X#include "mush.h"
X
Xextern char *c_macro();
Xstatic un_bind();
X
Xstruct cmd_map *cmd_map, *line_map, *bang_map;
X
X/*
X * Bindings are added here in REVERSE of the order that
X * they will be displayed!  Display order is based on a
X * guess about the frequency of use and (to a lesser
X * extent) how hard they are to remember.
X *
X * The user's own new bindings, if any, will be displayed
X * before any of these default bindings.
X */
Xinit_bindings()
X{
X#ifdef CURSES
X    /* Help gets displayed last */
X    add_bind("?", C_HELP, NULL, &cmd_map);
X    add_bind("V", C_VERSION, NULL, &cmd_map);
X
X    /* Miscellaneous shell commands */
X    add_bind("%", C_CHDIR, NULL, &cmd_map);
X    add_bind("|", C_PRINT_MSG, NULL, &cmd_map);
X    add_bind("!", C_SHELL_ESC, NULL, &cmd_map);
X    add_bind(":", C_CURSES_ESC, NULL, &cmd_map);
X
X    /* Mush customization commands */
X    /* NOTE: No default C_MACRO bindings */
X    add_bind(")", C_SAVEOPTS, NULL, &cmd_map);
X    add_bind("(", C_SOURCE, NULL, &cmd_map);
X    add_bind("&!", C_MAP_BANG, NULL, &cmd_map);
X    add_bind("&:", C_MAP, NULL, &cmd_map);
X    add_bind("&&", C_BIND_MACRO, NULL, &cmd_map);
X    add_bind("v", C_VAR_SET, NULL, &cmd_map);
X    add_bind("i", C_IGNORE, NULL, &cmd_map);
X    add_bind("h", C_OWN_HDR, NULL, &cmd_map);
X    add_bind("B", C_UNBIND, NULL, &cmd_map);
X    add_bind("b", C_BIND, NULL, &cmd_map);
X    add_bind("a", C_ALIAS, NULL, &cmd_map);
X
X    /* Display modification commands */
X    add_bind("\022", C_REVERSE, NULL, &cmd_map);	/* ^R */
X    add_bind("\014", C_REDRAW, NULL, &cmd_map);		/* ^L */
X    add_bind("Z", C_PREV_SCREEN, NULL, &cmd_map);
X    add_bind("z", C_NEXT_SCREEN, NULL, &cmd_map);
X
X    /* Searching and sorting commands */
X    add_bind("\016", C_CONT_SEARCH, NULL, &cmd_map);	/* ^N */
X    add_bind("\037", C_PREV_SEARCH, NULL, &cmd_map);	/* ^/ */
X    add_bind("/", C_NEXT_SEARCH, NULL, &cmd_map);
X    add_bind("O", C_REV_SORT, NULL, &cmd_map);
X    add_bind("o", C_SORT, NULL, &cmd_map);
X
X    /* Ways to get out */
X    add_bind("X", C_EXIT_HARD, NULL, &cmd_map);
X    add_bind("x", C_EXIT, NULL, &cmd_map);
X    add_bind("Q", C_QUIT_HARD, NULL, &cmd_map);
X    add_bind("q", C_QUIT, NULL, &cmd_map);
X
X    /* Folder modification commands */
X    add_bind("\025", C_UPDATE, NULL, &cmd_map);		/* ^U */
X    add_bind("\020", C_PRESERVE, NULL, &cmd_map);	/* ^P */
X    add_bind("W", C_WRITE_LIST, NULL, &cmd_map);
X    add_bind("w", C_WRITE_MSG, NULL, &cmd_map);
X    add_bind("U", C_UNDEL_LIST, NULL, &cmd_map);
X    add_bind("u", C_UNDEL_MSG, NULL, &cmd_map);
X    add_bind("S", C_SAVE_LIST, NULL, &cmd_map);
X    add_bind("s", C_SAVE_MSG, NULL, &cmd_map);
X    add_bind("f", C_FOLDER, NULL, &cmd_map);
X    add_bind("D", C_DELETE_LIST, NULL, &cmd_map);
X    add_bind("d", C_DELETE_MSG, NULL, &cmd_map);
X    add_bind("C", C_COPY_LIST, NULL, &cmd_map);
X    add_bind("c", C_COPY_MSG, NULL, &cmd_map);
X
X    /* Cursor movement and message selection */
X    add_bind("g", C_GOTO_MSG, NULL, &cmd_map);
X    add_bind("}", C_BOTTOM_PAGE, NULL, &cmd_map);
X    add_bind("{", C_TOP_PAGE, NULL, &cmd_map);
X    add_bind("$", C_LAST_MSG, NULL, &cmd_map);
X    add_bind("^", C_FIRST_MSG, NULL, &cmd_map);
X    add_bind("\013",C_PREV_MSG, NULL, &cmd_map);	/* ^K */
X    add_bind("\012", C_NEXT_MSG, NULL, &cmd_map);	/* ^J */
X    add_bind("-",C_PREV_MSG, NULL, &cmd_map);
X    add_bind("+",C_NEXT_MSG, NULL, &cmd_map);
X    add_bind("K", C_PREV_MSG, NULL, &cmd_map);
X    add_bind("k", C_PREV_MSG, NULL, &cmd_map);
X    add_bind("J", C_NEXT_MSG, NULL, &cmd_map);
X    add_bind("j", C_NEXT_MSG, NULL, &cmd_map);
X
X    /* Mail-sending commands */
X    add_bind("R", C_REPLY_ALL, NULL, &cmd_map);
X    add_bind("r", C_REPLY_SENDER, NULL, &cmd_map);
X    add_bind("M", C_MAIL_FLAGS, NULL, &cmd_map);
X    add_bind("m", C_MAIL, NULL, &cmd_map);
X
X    /* Mail-reading commands */
X    add_bind(".", C_DISPLAY_MSG, NULL, &cmd_map);
X    add_bind("T", C_TOP_MSG, NULL, &cmd_map);
X    add_bind("t", C_DISPLAY_MSG, NULL, &cmd_map);
X    add_bind("p", C_DISPLAY_MSG, NULL, &cmd_map);
X    add_bind("n", C_DISPLAY_NEXT, NULL, &cmd_map);
X
X#endif /* CURSES */
X}
X
X/* Bindable function names.
X *  Most of these can't be used if CURSES is not defined,
X *  but help and lookups get confused if they aren't all here.
X */
Xstruct cmd_map map_func_names[] = {
X    /* These MUST be in numerical order; see bindings.h */
X    { C_NULL,		"no-op",		NULL, NULL_MAP },
X    { C_GOTO_MSG,	"goto-msg",		NULL, NULL_MAP },
X    { C_WRITE_LIST,	"write-list",		NULL, NULL_MAP },
X    { C_WRITE_MSG,	"write",		NULL, NULL_MAP },
X    { C_SAVE_LIST,	"save-list",		NULL, NULL_MAP },
X    { C_SAVE_MSG,	"save",			NULL, NULL_MAP },
X    { C_COPY_LIST,	"copy-list",		NULL, NULL_MAP },
X    { C_COPY_MSG,	"copy",			NULL, NULL_MAP },
X    { C_DELETE_LIST,	"delete-list",		NULL, NULL_MAP },
X    { C_DELETE_MSG,	"delete",		NULL, NULL_MAP },
X    { C_UNDEL_LIST,	"undelete-list",	NULL, NULL_MAP },
X    { C_UNDEL_MSG,	"undelete",		NULL, NULL_MAP },
X    { C_REDRAW,		"redraw",		NULL, NULL_MAP },
X    { C_REVERSE,	"reverse-video",	NULL, NULL_MAP },
X    { C_NEXT_MSG,	"next-msg",		NULL, NULL_MAP },
X    { C_PREV_MSG,	"back-msg",		NULL, NULL_MAP },
X    { C_FIRST_MSG,	"first-msg",		NULL, NULL_MAP },
X    { C_LAST_MSG,	"last-msg",		NULL, NULL_MAP },
X    { C_TOP_PAGE,	"top-page",		NULL, NULL_MAP },
X    { C_BOTTOM_PAGE,	"bottom-page",		NULL, NULL_MAP },
X    { C_NEXT_SCREEN,	"screen-next",		NULL, NULL_MAP },
X    { C_PREV_SCREEN,	"screen-back",		NULL, NULL_MAP },
X    { C_SOURCE,		"source",		NULL, NULL_MAP },
X    { C_SAVEOPTS,	"saveopts",		NULL, NULL_MAP },
X    { C_NEXT_SEARCH,	"search-next",		NULL, NULL_MAP },
X    { C_PREV_SEARCH,	"search-back",		NULL, NULL_MAP },
X    { C_CONT_SEARCH,	"search-again",		NULL, NULL_MAP },
X    { C_PRESERVE,	"preserve",		NULL, NULL_MAP },
X    { C_REV_SORT,	"sort-reverse",		NULL, NULL_MAP },
X    { C_SORT,		"sort",			NULL, NULL_MAP },
X    { C_QUIT_HARD,	"quit!",		NULL, NULL_MAP },
X    { C_QUIT,		"quit",			NULL, NULL_MAP },
X    { C_EXIT_HARD,	"exit!",		NULL, NULL_MAP },
X    { C_EXIT,		"exit",			NULL, NULL_MAP },
X    { C_UPDATE,		"update",		NULL, NULL_MAP },
X    { C_FOLDER,		"folder",		NULL, NULL_MAP },
X    { C_SHELL_ESC,	"shell-escape",		NULL, NULL_MAP },
X    { C_CURSES_ESC,	"line-mode",		NULL, NULL_MAP },
X    { C_PRINT_MSG,	"lpr",			NULL, NULL_MAP },
X    { C_CHDIR,		"chdir",		NULL, NULL_MAP },
X    { C_VAR_SET,	"variable",		NULL, NULL_MAP },
X    { C_IGNORE,		"ignore",		NULL, NULL_MAP },
X    { C_ALIAS,		"alias",		NULL, NULL_MAP },
X    { C_OWN_HDR,	"my-hdrs",		NULL, NULL_MAP },
X    { C_VERSION,	"version",		NULL, NULL_MAP },
X    { C_MAIL_FLAGS,	"mail-flags",		NULL, NULL_MAP },
X    { C_MAIL,		"mail",			NULL, NULL_MAP },
X    { C_REPLY_ALL,	"reply-all",		NULL, NULL_MAP },
X    { C_REPLY_SENDER,	"reply",		NULL, NULL_MAP },
X    { C_DISPLAY_NEXT,	"display-next",		NULL, NULL_MAP },
X    { C_DISPLAY_MSG,	"display",		NULL, NULL_MAP },
X    { C_TOP_MSG,	"top",			NULL, NULL_MAP },
X    { C_BIND_MACRO,	"bind-macro",		NULL, NULL_MAP },
X    { C_BIND,		"bind",			NULL, NULL_MAP },
X    { C_UNBIND,		"unbind",		NULL, NULL_MAP },
X    { C_MAP_BANG,	"map!",			NULL, NULL_MAP },
X    { C_MAP,		"map",			NULL, NULL_MAP },
X    { C_MACRO,		"macro",		NULL, NULL_MAP },
X    /* C_HELP Must be the last one! */
X    { C_HELP,		"help",			NULL, NULL_MAP }
X};
X
X#ifdef CURSES
X
X/*
X * getcmd() is called from curses mode only.  It waits for char input from
X * the user via m_getchar() (which means that a macro could provide input)
X * and then compares the chars input against the "bind"ings set up by the
X * user (or the defaults).  For example, 'j' could bind to "next msg" which
X * is interpreted by the big switch statement in curses_command() (curses.c).
X * getcmd() returns the int-value of the curses command the input is "bound"
X * to.  If the input is unrecognized, C_NULL is returned (curses_command()
X * might require some cleanup, so this is valid, too).
X *
X * Since the input could originate from a macro rather than the terminal,
X * check to see if this is the case and search for a '[' char which indicates
X * that there is a curses command or other "long" command to be executed.
X */
Xgetcmd()
X{
X    char 		buf[MAX_BIND_LEN * 3];
X    register int 	c, m, match;
X    register char	*p = buf;
X    register struct cmd_map *list;
X
X    bzero(buf, MAX_BIND_LEN);
X    active_cmd = NULL_MAP;
X    c = m_getchar();
X    /* If user did job control (^Z), then the interrupt flag will be
X     * set.  Be sure it's unset before continuing.
X     */
X    turnoff(glob_flags, WAS_INTR);
X    if (isdigit(c)) {
X	buf[0] = c;
X	buf[1] = '\0';
X	Ungetstr(buf); /* So mac_flush can clear on error */
X	return C_GOTO_MSG;
X    }
X    for (;;) {
X	if (ison(glob_flags, IN_MACRO) && c == MAC_LONG_CMD)
X	    return long_mac_cmd(c, TRUE);
X	else
X	    *p++ = c;
X	m = 0;
X	for (list = cmd_map; list; list = list->m_next) {
X	    if ((match = prefix(buf, list->m_str)) == MATCH) {
X		if (debug)
X		    print("\"%s\" ",
X			ctrl_strcpy(buf,
X				    map_func_names[list->m_cmd].m_str,
X				    TRUE));
X		if (list->m_cmd == C_MACRO) {
X		    curs_macro(list->x_str);
X		    return getcmd();
X		}
X		active_cmd = list;
X		return (int)list->m_cmd;
X	    } else if (match != NO_MATCH)
X		m++;
X	}
X	if (m == 0) {
X	    if (debug) {
X		char tmp[sizeof buf];
X		print("No binding for \"%s\" found.",
X		    ctrl_strcpy(tmp, buf, TRUE));
X	    }
X	    return C_NULL;
X	}
X	c = m_getchar();
X    }
X}
X
X#endif /* CURSES */
X
X/*
X * bind_it() is used to set or unset bind, map and map! settings.
X * bind is used to accelerate curses commands by mapping key sequences
X * to curses commands.  map is used to accelerate command mode keysequences
X * by simulating stdin.  map! is the same, but used when in compose mode.
X *
X * bind_it() doesn't touch messages; return -1 for curses mode.
X * return -2 to have curses command set CNTD_CMD to prevent screen refresh
X * to allow user to read output in case of multiple lines.
X *
X * Since this routine deals with a lot of binding and unbinding of things
X * like line-mode "map"s and is interactive (calls Getstr()), be very careful
X * not to allow expansions during interaction.
X */
Xbind_it(len, argv)
Xchar **argv;
X{
X    char string[MAX_BIND_LEN], buf[256], *name = NULL;
X    char *rawstr; /* raw format of string (ptr to string if no argv avail) */
X    char ascii[MAX_BIND_LEN*2]; /* printable ascii version of string */
X    register int x;
X    SIGRET (*oldint)(), (*oldquit)();
X    struct cmd_map **map_list;
X    int unbind = (argv && **argv == 'u');
X    int map = 0, is_bind_macro = 0;
X    int ret = 0 - iscurses; /* return value */
X
X    if (argv && !strcmp(name = *argv, "bind-macro"))
X	is_bind_macro++;
X
X    if (map = (argv && (!strcmp(name, "map!") || !strcmp(name, "unmap!"))))
X	map_list = &bang_map;
X    else if (map = (argv && (!strcmp(name, "map") || !strcmp(name, "unmap"))))
X	map_list = &line_map;
X    else
X	map_list = &cmd_map;
X
X    if (argv && *++argv && !strcmp(*argv, "-?"))
X	/* Subtract ret and iscurses to signal output */
X	return help(0, name, cmd_help) - ret - iscurses;
X
X    if (iscurses)
X	on_intr();
X
X    if (unbind) {
X	if (!*argv) {
X	    print("%s what? ", name);
X	    len = Getstr(buf, sizeof buf, 0);
X	    if (len <= 0) {
X		if (iscurses)
X		    off_intr();
X		return -1;
X	    }
X	    rawstr = m_xlate(buf);
X	} else
X	    rawstr = m_xlate(*argv);
X	if (!un_bind(rawstr, map_list)) {
X	    ctrl_strcpy(ascii, rawstr, TRUE);
X	    print("\"%s\" isn't bound to a command.\n", ascii);
X	}
X	if (iscurses)
X	    off_intr();
X	return ret;
X    }
X    if (argv && *argv) {
X	rawstr = m_xlate(*argv);
X	(void) ctrl_strcpy(ascii, rawstr, TRUE);
X	if (!*++argv) {
X	    /*
X	     * determine whether "argv" references a "map" or a "bind"
X	     */
X	    int binding = c_bind(rawstr, *map_list);
X	    if (binding == C_MACRO) {
X		char *mapping = c_macro(NULL, rawstr, *map_list);
X		if (mapping) {
X		    print("\"%s\" is mapped to ", ascii);
X		    print_more("\"%s\".\n",
X			ctrl_strcpy(buf, mapping, FALSE));
X		} else
X		    print("\"%s\" isn't mapped.\n", ascii);
X	    } else if (binding)
X		print("\"%s\" is %s to \"%s\".\n", ascii,
X		    map? "mapped" : "bound", map_func_names[binding].m_str);
X	    else if (map)
X		print("\"%s\" isn't mapped.\n", ascii);
X	    else
X		print("\"%s\" isn't bound to a command.\n", ascii);
X	    if (iscurses)
X		off_intr();
X	    return ret;
X	}
X    } else {
X	print("%s [<CR>=all, -?=help]: ", name);
X	len = Getstr(string, MAX_BIND_LEN-1, 0);
X	if (len == 0) {
X	    int add_to_ret = iscurses;
X#ifdef CURSES
X	    if (iscurses)
X		move(LINES-1, 0), refresh();
X#endif
X	    if (map || is_bind_macro)
X		add_to_ret = !c_macro(name, NULL, *map_list);
X	    else
X		add_to_ret = !c_bind(NULL, *map_list);
X	    if (iscurses)
X		off_intr();
X	    /* signal CTND_CMD if there was output */
X	    return ret - add_to_ret;
X	}
X	if (len < 0) {
X	    if (iscurses)
X		off_intr();
X	    return ret;
X	}
X	rawstr = m_xlate(string);
X	(void) ctrl_strcpy(ascii, rawstr, TRUE);
X    }
X    /* if a binding was given on the command line */
X    if (argv && *argv && !map)
X	if (is_bind_macro)
X	    (void) strcpy(buf, "macro");
X	else
X	    (void) strcpy(buf, *argv++);
X    else {
X	/* at this point, "rawstr" and "ascii" should both be set */
X	int binding;
X
X	if (!strcmp(ascii, "-?")) {
X	    if (iscurses)
X		clr_bot_line();
X	    ret -= help(0, name, cmd_help);
X	    if (iscurses)
X		off_intr();
X	    /* Subtract iscurses to signal CNTD_CMD */
X	    return ret - iscurses;
X	}
X
X	if (!map && !is_bind_macro) {
X	    binding = c_bind(rawstr, *map_list);
X
X	    for (len = 0; len == 0; ) {
X		print("\"%s\" = <%s>: New binding [<CR> for list]: ",
X		    ascii, (binding? map_func_names[binding].m_str : "unset"));
X		len = Getstr(buf, sizeof buf, 0);
X		if (iscurses)
X		    clr_bot_line();
X		/* strip any trailing whitespace */
X		if (len > 0)
X		    len = no_newln(buf) - buf;
X		if (len == 0) {
X		    (void) do_pager(NULL, TRUE);
X		    if (iscurses)
X			putchar('\n');
X		    for (x = 1; x <= C_HELP; x++) {
X			if (!(x % 4))
X			    if (do_pager("\n", FALSE) == EOF)
X				break;
X			(void) do_pager(sprintf(buf, "%-15.15s  ",
X					    map_func_names[x].m_str), FALSE);
X		    }
X		    (void) do_pager("\n", FALSE);
X		    (void) do_pager(NULL, FALSE);
X		    ret -= iscurses;
X		}
X	    }
X	} else /* map */
X	    (void) strcpy(buf, "macro"), len = 5;
X	/* if list was printed, ret < -1 -- tells CNTD_CMD to be set and
X	 * prevents screen from being refreshed (lets user read output
X	 */
X	if (len == -1) {
X	    if (iscurses)
X		off_intr();
X	    return ret;
X	}
X    }
X    for (x = 1; x <= C_HELP; x++) {
X	if (prefix(buf, map_func_names[x].m_str) == MATCH) {
X	    int add_to_ret;
X	    if (debug)
X		print("\"%s\" will execute \"%s\".\n", ascii, buf);
X	    if (map_func_names[x].m_cmd == C_MACRO) {
X		if (argv && *argv) {
X		    (void) argv_to_string(buf, argv);
X		    (void) m_xlate(buf); /* Convert buf to raw chars */
X		    add_to_ret =
X			do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
X		} else {
X		    char exp[MAX_MACRO_LEN*2]; /* printable expansion */
X		    char *mapping = c_macro(NULL, rawstr, *map_list);
X
X		    if (mapping)
X			(void) ctrl_strcpy(exp, mapping, TRUE);
X		    print("\"%s\" = <%s>", ascii, mapping ? exp : "unset");
X		    putchar('\n'), print("New macro: ");
X		    ret -= iscurses; /* To signal screen messed up */
X		    /* we are done with buf, so we can trash over it */
X		    len = Getstr(buf, MAX_MACRO_LEN, 0);
X		    if (len > 0) {
X			if (iscurses)
X			    clr_bot_line();
X			(void) m_xlate(buf); /* Convert buf to raw chars */
X			add_to_ret =
X			    do_bind(rawstr, C_MACRO, buf, map_list);
X			if (debug) {
X			    (void) ctrl_strcpy(exp, buf, TRUE);
X			    print("\"%s\" will execute \"%s\".\n", ascii, exp);
X			}
X		    } else if (len < 0) {
X			if (iscurses)
X			    off_intr();
X			return ret - add_to_ret;
X		    } else
X			print("Can't bind to null macro"), putchar('\n');
X		}
X	    } else /* not a macro */ {
X		(void) argv_to_string(buf, argv);
X		add_to_ret =
X		    do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
X	    }
X	    /* if do_bind had no errors, it returned -1.  If we already
X	     * messed up the screen, then ret is less than -1.  return the
X	     * lesser of the two to make sure that CNTD_CMD gets set right
X	     */
X	    if (iscurses)
X		off_intr();
X	    return min(add_to_ret, ret);
X	}
X    }
X    print("\"%s\": Unknown function.\n", buf);
X    if (iscurses)
X	off_intr();
X    return ret;
X}
X
X/*
X * print current key to command bindings if "str" is NULL.
X * else return the integer "m_cmd" which the str is bound to.
X */
Xc_bind(str, opts)
Xregister char *str;
Xregister struct cmd_map *opts;
X{
X    register int    incurses = iscurses;
X
X    if (!str) {
X	if (!opts) {
X	    print("No command bindings.\n");
X	    return C_ERROR;
X	}
X	if (incurses)
X	    clr_bot_line(), iscurses = FALSE;
X	(void) do_pager(NULL, TRUE);
X	(void) do_pager("Current key to command bindings:\n", FALSE);
X	(void) do_pager("\n", FALSE);
X    }
X
X    for (; opts; opts = opts->m_next) {
X	char buf[BUFSIZ], buf2[MAX_BIND_LEN], exp[MAX_MACRO_LEN*2], *xp;
X	if (!str) {
X	    (void) ctrl_strcpy(buf2, opts->m_str, FALSE);
X	    if ((xp = opts->x_str) && opts->m_cmd == C_MACRO)
X		xp = ctrl_strcpy(exp, opts->x_str, TRUE);
X	    if (do_pager(sprintf(buf, "%s\t%-15.15s %s\n",
X			 buf2, map_func_names[opts->m_cmd].m_str,
X			 xp? xp : ""),
X			 FALSE) == EOF)
X		break;
X	} else
X	    if (strcmp(str, opts->m_str))
X		continue;
X	    else
X		return opts->m_cmd;
X    }
X
X    iscurses = incurses;
X    if (!str)
X	(void) do_pager(NULL, FALSE);
X    return C_NULL;
X}
X
X/*
X * Doesn't touch messages, but changes macros: return -1.
X * Error output causes return < -1.
X *  args is currently the execute string of a macro mapping, but may be
X *  used in the future as an argument string for any curses command.
X */
Xdo_bind(str, func, args, map_list)
Xregister char *str, *args;
Xstruct cmd_map **map_list;
Xlong func;
X{
X    register int ret = -1;
X    register struct cmd_map *list;
X    int match;
X
X    if (func == C_MACRO && !check_mac_bindings(args))
X	--ret;
X    (void) un_bind(str, map_list);
X    for (list = *map_list; list; list = list->m_next)
X	if ((match = prefix(str, list->m_str)) != NO_MATCH) {
X	    ret--;
X	    switch (match) {
X		case MATCH:
X		    puts("Something impossible just happened.");
X		when A_PREFIX_B:
X		    wprint("Warning: \"%s\" prefixes \"%s\" (%s)\n", str,
X			list->m_str, map_func_names[list->m_cmd].m_str);
X		when B_PREFIX_A:
X		    wprint("Warning: \"%s\" (%s) prefixes: \"%s\"\n",
X			list->m_str, map_func_names[list->m_cmd].m_str, str);
X	    }
X	}
X    add_bind(str, func, args, map_list);
X    /* errors decrement ret.  If ret returns less than -1, CNTD_CMD is set
X     * and no redrawing is done so user can see the warning signs
X     */
X    return ret;
X}
X
X/*
X * add a binding to a list.  This may include "map"s or other mappings since
X * the map_list argument can control that.  The "func" is an int defined in
X * bindings.h ... the "str" passed is the string the user would have to type
X * to get the macro/map/binding expanded.  This must in in raw format: no
X * \n's to mean \015.  Convert first using m_xlate().
X */
Xadd_bind(str, func, args, map_list)
Xregister char *str, *args;
Xstruct cmd_map **map_list;
Xlong func;
X{
X    register struct cmd_map *tmp;
X    struct cmd_map *calloc();
X
X    if (!str || !*str)
X	return;
X
X    /* now make a new option struct and set fields */
X    if (!(tmp = calloc((unsigned)1, sizeof(struct cmd_map)))) {
X	error("calloc");
X	return;
X    }
X    tmp->m_next = *map_list;
X    *map_list = tmp;
X
X    tmp->m_str = savestr(str);
X    tmp->m_cmd = func; /* strdup handles the NULL case */
X    if (args && *args)
X	tmp->x_str = savestr(args);
X    else
X	tmp->x_str = NULL;
X}
X
Xstatic
Xun_bind(p, map_list)
Xregister char *p;
Xstruct cmd_map **map_list;
X{
X    register struct cmd_map *list = *map_list, *tmp;
X
X    if (!list || !*list->m_str || !p || !*p)
X	return 0;
X
X    if (!strcmp(p, (*map_list)->m_str)) {
X	*map_list = (*map_list)->m_next;
X	xfree (list->m_str);
X	if (list->x_str)
X	    xfree (list->x_str);
X	xfree((char *)list);
X	return 1;
X    }
X    for ( ; list->m_next; list = list->m_next)
X	if (!strcmp(p, list->m_next->m_str)) {
X	    tmp = list->m_next;
X	    list->m_next = list->m_next->m_next;
X	    xfree (tmp->m_str);
X	    if (tmp->x_str)
X		xfree (tmp->x_str);
X	    xfree ((char *)tmp);
X	    return 1;
X	}
X    return 0;
X}
X
Xprefix(a, b)
Xregister char *a, *b;
X{
X    if (!a || !b)
X	return NO_MATCH;
X
X    while (*a && *b && *a == *b)
X	a++, b++;
X    if (!*a && !*b)
X	return MATCH;
X    if (!*a && *b)
X	return A_PREFIX_B;
X    if (*a && !*b)
X	return B_PREFIX_A;
X    return NO_MATCH;
X}
END_OF_FILE
if test 20444 -ne `wc -c <'bind.c'`; then
    echo shar: \"'bind.c'\" unpacked with wrong size!
fi
# end of 'bind.c'
fi
if test -f 'hdrs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hdrs.c'\"
else
echo shar: Extracting \"'hdrs.c'\" \(18781 characters\)
sed "s/^X//" >'hdrs.c' <<'END_OF_FILE'
X/* hdrs.c 	(c) copyright 1986 (Dan Heller) */
X
X/*
X * Routines that deal with message headers inside messages
X * msg_get(n, from, count) -- get the From_ line in msg n into "from".
X * header_field(n, str) -- get the header named "str" from msg n.
X * do_hdrs(argc, argv, list) -- diplay message headers.
X * specl_hdrs(argv, list) -- display msgs that share common attributes.
X * compose_hdr(cnt) -- compose a message header from msg n.
X * reply_to(n, all, buf) -- construct a header based on the To: header of n.
X * subject_to(n, buf) -- get the subject for replying to msg n.
X * cc_to(n, buf) -- construct a Cc header based on the Cc of message n.
X */
X#include "mush.h"
X
X/*
X * Get a message from the current folder by its offset.
X * Copy the From_ line to the second argument if the third arg > 0,
X * and return the second argument, or NULL on an error.
X */
Xchar *
Xmsg_get(n, from, count)
Xint n, count;
Xchar *from;
X{
X    if (fseek(tmpf, msg[n].m_offset, L_SET) == -1) {
X	error("fseek in %s (msg %d, folder=%s)", tempfile, n+1, mailfile);
X	turnon(glob_flags, READ_ONLY);
X	return NULL;
X    }
X    if (count)
X#ifndef MSG_SEPARATOR
X	return fgets(from, count, tmpf);
X#else
X	*from = '\0';
X#endif
X    return from;
X}
X
X/*
X * get which message via the offset and search for the headers which
X * match the string "str". there may be more than one of a field (like Cc:)
X * so get them all and "cat" them together into the static buffer
X * "buf" and return its address.
X */
Xchar *
Xheader_field(n, str)
Xchar *str;
X{
X    static char    buf[HDRSIZ];
X    char 	   tmp[HDRSIZ];
X    register char  *p, *p2, *b = buf;
X    int contd_hdr;  /* true if next line is a continuation of the hdr we want */
X
X    if (!msg_get(n, tmp, sizeof tmp))
X	return NULL;
X    *b = 0;
X    while((p = fgets(tmp, sizeof(tmp), tmpf)) && *p != '\n') {
X	if (*p != ' ' && *p != '\t') {
X	    contd_hdr = 0;
X	    /* strcmp ignoring case */
X	    for(p2 = str; *p && *p2 && *p2 == lower(*p); ++p, ++p2);
X	    /* MATCH is true if p2 is at the end of str and *p is ':' */
X	    if (*p2 || *p++ != ':')
X		continue;
X	    else
X		contd_hdr = 1;
X	} else if (!contd_hdr)
X	    continue;
X	skipspaces(0);
X	p2 = no_newln(p);
X	*++p2 = ',', *++p2 = ' ', *++p2 = 0;
X	if (strlen(p) + (b-buf) < sizeof (buf))
X	    b += Strcpy(b, p);
X    }
X    if (b > buf) /* now get rid of the trailing ", " */
X	*--b = 0, *--b = 0;
X    return (*buf)? buf: NULL;
X}
X
Xdo_hdrs(argc, argv, list)
Xregister char **argv, list[];
X{
X    register int   pageful = 0;
X    SIGRET 	   (*oldint)(), (*oldquit)();
X    int		   show_deleted;
X    static int     cnt;
X    register char  *p;
X    char 	   first_char = (argc) ? **argv: 'h';
X
X    if (argc > 1 && !strcmp(argv[1], "-?"))
X	return help(0, "headers", cmd_help);
X
X    if (!msg_cnt) {
X	if (ison(glob_flags, DO_PIPE))
X	    return 0;
X#ifdef CURSES
X	if (iscurses)
X	    clear();
X#endif /* CURSES */
X#ifdef SUNTOOL
X	if (istool)
X	    mail_status(0);
X#endif /* SUNTOOL */
X	return 0;
X    }
X    if (first_char == ':' || (argc > 1 && argv[1][0] == ':')) {
X	if (first_char != ':')
X	    argv++;
X	return specl_hdrs(argv, list);
X    }
X
X    on_intr();
X
X    if (argc && (argv[0][1] == '+' || argc > 1 && !strcmp(argv[1], "+")) ||
X	    first_char == 'z' && !argv[1])
X	if (msg_cnt > screen)
X	    cnt = min(msg_cnt - screen, n_array[0] + screen);
X	else
X	    cnt = 0;
X    else if (argc && (argv[0][1] == '-' || argc > 1 && !strcmp(argv[1], "-")))
X	cnt = max((cnt - 2*screen), 0);
X    else if (argc && *++argv &&
X	(isdigit(**argv) || **argv == '^' || **argv == '$' || **argv == '.')
X	 || ison(glob_flags, IS_PIPE)) {
X	/* if we're coming from a pipe, start display at the first msg bit
X	 * set in the msg_list
X	 */
X	int fnd;
X	if (ison(glob_flags, IS_PIPE)) {
X	    if (isoff(glob_flags, DO_PIPE))
X		for (fnd = 0; fnd < msg_cnt; fnd++)
X		    if (msg_bit(list, fnd))
X			wprint("%s\n", compose_hdr(fnd));
X	    off_intr();
X	    return 0;
X	}
X	/* if a number was given, use it */
X	if (!(fnd = chk_msg(*argv))) {
X	    off_intr();
X	    return -1;
X	}
X	for (cnt = fnd - 1; cnt > 0 && cnt + screen > msg_cnt; cnt--)
X	    ;
X    } else if (current_msg < n_array[0] || current_msg > n_array[screen-1])
X	cnt = current_msg; /* adjust if reads have passed screen bounds */
X    else if (cnt >= msg_cnt || !argc || !*argv)
X	cnt = max((cnt - screen), 0); /* adjust window to maintain position */
X
X    show_deleted = !!do_set(set_options, "show_deleted");
X
X    /* Make sure we have at least $screen headers to print */
X    if (cnt > 0 && !iscurses && !show_deleted && first_char != 'h') {
X	int tmp = cnt;
X	/* first count how many messages we can print without adjusting */
X	for (pageful = 0; pageful < screen && cnt < msg_cnt; cnt++)
X	    if (isoff(msg[cnt].m_flags, DELETE))
X		pageful++;
X	/* if we can't print a pagefull of hdrs, back up till we can */
X	for (cnt = tmp; pageful < screen && cnt; --cnt)
X	    if (isoff(msg[cnt].m_flags, DELETE))
X		pageful++;
X	pageful = 0;	/* Used later as an index, so reset */
X    }
X
X    for (;pageful<screen && cnt<msg_cnt && isoff(glob_flags, WAS_INTR); cnt++) {
X	if (!iscurses && !show_deleted && first_char == 'h'
X	    && ison(msg[cnt].m_flags, DELETE))
X	    continue;
X	n_array[pageful++] = cnt;
X	/* this message was displayed -- set the bit */
X	if (list)
X	    set_msg_bit(list, cnt);
X	/* if do_pipe, don't output anything */
X	if (ison(glob_flags, DO_PIPE))
X	    continue;
X	p = compose_hdr(cnt);
X	if (!istool && (!iscurses || ison(glob_flags, IS_GETTING)))
X	    puts(p);
X#ifdef SUNTOOL
X	else if (istool) {
X	    if (cnt == current_msg) /* embolden or reverse-video */
X		highlight(hdr_win, 0,pageful*l_height(DEFAULT), DEFAULT,p);
X	    else
X		pw_text(hdr_win, 0, pageful * l_height(DEFAULT), PIX_SRC,
X							fonts[DEFAULT], p);
X	    Clrtoeol(hdr_win, strlen(p)*l_width(DEFAULT),
X		     pageful*l_height(DEFAULT), DEFAULT);
X	}
X#endif /* SUNTOOL */
X#ifdef CURSES
X        else if (iscurses) {
X	    move(pageful, 0);
X	    printw("%-.*s", COLS-2, p), clrtoeol();
X        }
X#endif /* CURSES */
X    }
X    /* just in case a signal stopped us */
X    off_intr();
X    pageful++;
X#ifdef CURSES
X    if (iscurses && pageful < screen)
X	move(pageful, 0), clrtobot();
X#endif /* CURSES */
X    if (cnt == msg_cnt) {
X	while (pageful <= screen) {
X	    n_array[pageful-1] = msg_cnt+1; /* assign out-of-range values */
X#ifdef SUNTOOL
X	    if (istool)
X		Clrtoeol(hdr_win, 0, pageful * l_height(DEFAULT), DEFAULT);
X#endif /* SUNTOOL */
X	    ++pageful;
X	}
X    }
X#ifdef SUNTOOL
X    if (istool) {
X	if (msg_cnt > screen) {
X	    panel_set(next_scr, PANEL_SHOW_ITEM, TRUE, 0);
X	    panel_set(prev_scr, PANEL_SHOW_ITEM, TRUE, 0);
X	}
X	mail_status(0);
X    }
X#endif /* SUNTOOL */
X    return 0;
X}
X
X#define NEW 1
X#define ALL 2
X
Xspecl_hdrs(argv, list)
Xchar **argv, list[];
X{
X    u_long	special = 0;
X    int 	n = 0;
X
X    while (argv[0][++n])
X	switch(argv[0][n]) {
X	    case 'a': special = ALL;
X	    when 'n': special = NEW;
X	    when 'u': special = UNREAD;
X	    when 'o': special = OLD;
X	    when 'd': special = DELETE;
X	    when 'r': special = REPLIED;
X	    when 's': special = SAVED;
X	    when 'p': special = PRESERVE;
X	    otherwise: print("choose from n,u,o,d,r,s,p or a"); return -1;
X	}
X    if (debug)
X	(void) check_flags(special);
X
X    for (n = 0; n < msg_cnt; n++) {
X	/*
X	 * First, see if we're looking for NEW messages.
X	 * If so, then check to see if the msg is unread and not old.
X	 * If special > ALL, then special has a mask of bits describing
X	 * the state of the message.
X	 */
X	if (ison(glob_flags, IS_PIPE)&& !msg_bit(list, n))
X	    continue;
X	if (special == ALL || special == NEW &&
X	       (ison(msg[n].m_flags, UNREAD) && isoff(msg[n].m_flags, OLD))) {
X	    if (isoff(glob_flags, DO_PIPE))
X		print("%s\n", compose_hdr(n));
X	    if (list)
X		set_msg_bit(list, n);
X#ifndef M_XENIX
X	/*
X	 * XENIX compiler can't handle "special" in ison() macro.
X	 * It only works if the second argument is a constant!
X	 */
X	} else if (special > ALL && ison(msg[n].m_flags, special)) {
X	    if (isoff(glob_flags, DO_PIPE))
X		print("%s\n", compose_hdr(n));
X	    if (list)
X		set_msg_bit(list, n);
X#endif /* M_XENIX */
X	} else {
X	    if (list)
X		unset_msg_bit(list, n);
X	    if (debug) {
X		printf("msg[%d].m_flags: %d", n, msg[n].m_flags);
X		(void) check_flags(msg[n].m_flags);
X	    }
X	}
X    }
X    return 0;
X}
X
X#define Strncpy(buf,p) (void)(strncpy(buf,p,sizeof(buf)),buf[sizeof(buf)-1]=0)
X
X/*
X * compose a header from the information about a message (from, to, date,
X * subject, etc..).  The header for message number "cnt" is built and is
X * returned in the static buffer "buf".  There will be *at least* 9 chars
X * in the buffer which will be something like: " 123 >N " The breakdown
X * is as follows: 4 chars for the message number, 1 space, 1 char for '>'
X * (if current message) and two spaces for message status (new, unread, etc)
X * followed by 1 terminating space.
X * Read other comments in the routine for more info.
X */
Xchar *
Xcompose_hdr(cnt)
X{
X    static char		buf[256];
X    register char	*p, *p2, *b;
X    int			len, do_pad = FALSE, val, pad, got_dot, isauthor = 0, n;
X    char from[HDRSIZ], subject[256], date[64], lines[16];
X    char to[256], addr[256], name[256], status[4];
X    char Day[3], Mon[4], Tm[8], Yr[5], Wkday[4], *date_p;
X
X    /* status of the message */
X    if (ison(msg[cnt].m_flags, DELETE))
X	status[0] = '*';
X    else if (ison(msg[cnt].m_flags, PRESERVE))
X	status[0] = 'P';
X    else if (ison(msg[cnt].m_flags, SAVED))
X	status[0] = 'S';
X    else if (ison(msg[cnt].m_flags, OLD) && ison(msg[cnt].m_flags, UNREAD))
X	status[0] = 'U';
X    else if (isoff(msg[cnt].m_flags, UNREAD))
X	status[0] = ' ';
X    else
X	status[0] = 'N';
X
X    if (ison(msg[cnt].m_flags, REPLIED))
X	status[1] = 'r';
X    else
X	status[1] = ' ';
X
X    to[0] = from[0] = subject[0] = date[0] = lines[0] = addr[0] =
X    name[0] = Day[0] = Mon[0] = Tm[0] = Yr[0] = Wkday[0] = 0;
X
X    /* who's the message to */
X    if ((p = header_field(cnt, "resent-to")) ||
X	(p = header_field(cnt, "to")) ||
X	(p = header_field(cnt, "apparently-to")))
X	Strncpy(to, p);
X
X    /* who's the message from */
X    if ((p = header_field(cnt, "from")) && strcpy(from, p)
X	    || (p = reply_to(cnt, 0, from))) {
X	/* NOTE:  this fails if the sender has '<' or '!' in
X	 * the RFC822 comment fields -- leading "comment"
X	 * or trailing (comment) -- but that isn't critical
X	 */
X	if ((p2 = rindex(p, '!')) || (p2 = index(p, '<')))
X	    p = p2 + 1;
X    } else
X	p = strcpy(from, "unknown"); /* just in case */
X    /* If the From field contains the user's login name, then the message is
X     * from the user -- attempt to give more useful information by telling
X     * to whom the message was sent.  This is not possible if the "to" header
X     * failed to get info (which is probably impossible).
X     */
X    if (!strncmp(p, login, strlen(login))) {
X	isauthor = TRUE;
X	(void) get_name_n_addr(to, name+4, addr+4);
X	if (addr[4])
X	    (void) strncpy(addr, "TO: ", 4);
X	if (name[4]) {  /* check to see if a name got added */
X	    (void) strncpy(name, "TO: ", 4);
X	    Strncpy(from, name);
X	} else
X	    Strncpy(from, addr);
X    } else
X	(void) get_name_n_addr(from, name, addr);
X
X    if (ison(glob_flags, DATE_RECV))
X	date_p = msg[cnt].m_date_recv;
X    else
X	date_p = msg[cnt].m_date_sent;
X    date_to_string(date_p, Yr, Mon, Day, Wkday, Tm, date);
X
X    /* and the subject */
X    if (p = header_field(cnt, "subject"))
X	Strncpy(subject, p);
X
X    /* now, construct a header out of a format string */
X    if (!hdr_format)
X	hdr_format = DEF_HDR_FMT;
X
X    (void) sprintf(buf, "%4.d ", cnt+1);
X    b = buf+5;
X    *b++ = ((cnt == current_msg && !iscurses)? '>': ' ');
X    *b++ = status[0], *b++ = status[1];
X    *b++ = ' ';
X    /* Count chars since beginning of buf. Initialize to 9 (strlen(buf) so far)
X     * This magic number is used in other places in msgs.c and mail.c
X     */
X    n = 9;
X    for (p = hdr_format; *p; p++)
X	if (*p == '\\')
X	    switch (*++p) {
X		case 't':
X		    while (n % 8)
X			n++, *b++ = ' ';
X		when 'n':
X		    n = 1, *b++ = '\n';
X		otherwise: n++, *b++ = *p;
X	    }
X	else if (*p == '%') {
X	    char fmt[64];
X
X	    p2 = fmt;
X	    /* first check for string padding: %5n, %.4a, %10.5f, %-.3l etc. */
X	    do_pad = pad = val = got_dot = 0;
X	    *p2++ = '%';
X	    if (p[1] != '-')
X		*p2++ = '-';
X	    else
X		++p;
X	    while (isdigit(*++p) || !got_dot && *p == '.') {
X		if (*p == '.')
X		    got_dot = TRUE, val = pad, pad = 0;
X		else
X		    pad = pad * 10 + *p - '0';
X		*p2++ = *p;
X	    }
X	    if (!got_dot && isdigit(p[-1])) {
X		*p2 = 0; /* assure null termination */
X		val = atoi(fmt+1);
X		if (val < 0)
X		    val = -val;
X		p2 += strlen(sprintf(p2, ".%d", val));
X	    }
X	    pad = min(pad, val);
X	    *p2++ = 's', *p2 = 0;
X	    switch (*p) {
X		case 'f': p2 = from, do_pad = TRUE;
X		when 'a':
X		    if (!*(p2 = addr))
X			p2 = from;
X		    do_pad = TRUE;
X		when 'n':
X		    if (!*(p2 = name))
X			p2 = from, do_pad = TRUE;
X		when '%': p2 = "%";
X		when 't': p2 = to;
X		when 's': p2 = subject;
X		when 'l': p2 = sprintf(lines, "%d", msg[cnt].m_lines);
X		when 'c': p2 = sprintf(lines, "%ld", msg[cnt].m_size);
X		when 'i': p2 = header_field(cnt, "message-id");
X		/* date formatting chars */
X		when 'd': p2 = date; /* the full date */
X		when 'T': p2 = Tm;
X		when 'M': p2 = Mon;
X		when 'Y': p2 = Yr;
X		when 'y': p2 = Yr+2;
X		when 'N': p2 = Day;
X		when 'D': case 'W': p2 = Wkday;
X		otherwise: continue; /* unknown formatting char */
X	    }
X	    if (do_pad && pad && strlen(p2) > pad) {
X		char *old_p2 = p2, *p3;
X		/* if addr is too long, move pointer forward till the
X		 * "important" part is readable only for ! paths/addresses.
X		 */
X		while (p3 = index(p2, '!')) {
X		    int tmp = strlen(p3+1); /* xenix has compiler problems */
X		    p2 = p3+1;
X		    if (tmp + isauthor*4 < pad) {
X			if (isauthor && (p2 -= 4) < old_p2)
X			    p2 = old_p2;
X			break;
X		    }
X		}
X		if (isauthor && p2 > old_p2+4 && !p3 && strlen(p2) + 4 > pad)
X		    p2 -= 4;
X		if (old_p2 != p2 && isauthor)
X		    (void) strncpy(p2, "TO: ", 4); /* doesn't null terminate */
X	    }
X	    len = strlen(sprintf(b, fmt, p2));
X	    n += len, b += len;
X	    /* Get around a bug in 5.5 IBM RT which pads with NULs not ' ' */
X	    while (n && !*(b-1))
X		b--, n--;
X	} else
X	    n++, *b++ = *p;
X    for (*b-- = 0; isspace(*b) && *b != '\n'; --b)
X	*b = 0;
X    return buf;
X}
X
X/*
X * Using message "n", build a list of recipients that you would mail to if
X * you were to reply to this message.  If "all" is true, then it will take
X * everyone from the To line in addition to the original sender.
X * route_addresses() is called from mail.c, not from here.  There are too many
X * other uses for reply_to to always require reconstruction of return paths.
X * Note that we do NOT deal with Cc paths here either.
X * Check to make sure that we in fact return a legit address (i.e. not blanks
X * or null). If such a case occurs, return login name.  Always pad end w/blank.
X */
Xchar *
Xreply_to(n, all, buf)
Xchar buf[];
X{
X    register char *p = NULL, *p2, *b = buf, *field;
X    char line[256], name[256], addr[256];
X
X    if (field = do_set(set_options, "reply_to_hdr")) {
X#ifndef MSG_SEPARATOR
X	if (!*field)
X	    goto DoFrom; /* special case -- get the colon-less From line */
X#endif /* MSG_SEPARATOR */
X	field = lcase_strcpy(line, field);
X	while (*field) {
X	    if (p2 = any(field, " \t,:"))
X		*p2 = 0;
X#ifndef MSG_SEPARATOR
X	    if (!lcase_strncmp(field, "from_", -1))
X		goto DoFrom;
X#endif /* MSG_SEPARATOR */
X	    if ((p = header_field(n, field)) || !p2)
X		break;
X	    else {
X		field = p2+1;
X		while (isspace(*field) || *field == ':' || *field == ',')
X		    field++;
X	    }
X	}
X	if (!p)
X	    print("Warning: message contains no `reply_to_hdr' headers.\n");
X    }
X    if (p || (!p && ((p = header_field(n, field = "reply-to")) ||
X        (p = header_field(n, field = "return-path")) ||
X        (p = header_field(n, field = "from")))))
X	skipspaces(0);
X    else if (!p) {
X#ifndef MSG_SEPARATOR
XDoFrom:
X	field = "from_";
X	/* if all else fails, then get the first token in "From" line */
X	if (p2 = msg_get(n, line, sizeof line))
X	    p = index(p2, ' ');
X	else
X	    return "";
X	skipspaces(1);
X	if (p2 = index(p, ' '))
X	    *p2 = 0;
X	(void) unscramble_addr(p, line); /* p is safely recopied to line */
X	p = line;
X#else /* MSG_SEPARATOR */
X	wprint("Warning: unable to find who msg %d is from!\n", n+1);
X#endif /* MSG_SEPARATOR */
X    }
X    get_name_n_addr(p, name, addr);
X    if (!name[0] && (!lcase_strncmp(field, "return-path", -1) ||
X		     !lcase_strncmp(field, "from_", -1))) {
X	/*
X	 * Get the name of the author of the message we're replying to from the
X	 * From: header since that header contains the author's name.  Only do
X	 * this if the address was gotten from the return-path or from_ lines
X	 * because this is the only way to guarantee that the return address
X	 * matches the author's name.  Reply-To: may not be the same person!
X	 * Check Resent-From: first since that's presumably more recent.
X	 */
X	if ((p = header_field(n, "resent-from")) ||
X		(p = header_field(n, "from")))
X	    get_name_n_addr(p, name, NULL);
X	if (!name[0] && (p = header_field(n, "name")))
X	    (void) strcpy(name, p);
X	if (name[0]) {
X	    if ((p = any(name, "(<,\"")) && (*p == ',' || *p == '<'))
X		*b++ = '"';
X	    b += Strcpy(b, name);
X	    if (p && (*p == ',' || *p == '<'))
X		*b++ = '"';
X	    *b++ = ' ', *b++ = '<';
X	}
X	b += Strcpy(b, addr);
X	if (name[0])
X	    *b++ = '>', *b = 0;
X    } else
X	b += Strcpy(buf, p);
X
X    /*
X     * if `all' is true, append everyone on the "To:" line.
X     * cc_to(), called separately, will catch the cc's
X     */
X    if (all &&
X	((p = header_field(n, "resent-to")) || (p = header_field(n, "to")) ||
X	 (p = header_field(n, "apparently-to"))) && *p) {
X	*b++ = ',', *b++ = ' ';
X	/* The assumption that HDRSIZ is correct is unwise, but I know it
X	 * to be true for Mush.  Be forewarned if you call this routine.
X	 */
X	p[HDRSIZ - (b - buf) - 2] = '\0'; /* prevent overflow */
X	b += Strcpy(b, p);
X    }
X    /* Also append the Resent-From address if there is one. */
X    if (all && (p = header_field(n, "resent-from")) && *p) {
X	*b++ = ',', *b++ = ' ';
X	/* Another trick to prevent overflow.   See warning above. */
X	(void) strncpy(b, p, HDRSIZ - (b - buf) - 2);
X	buf[HDRSIZ - 3] = 0;
X    }
X    fix_up_addr(buf);
X    take_me_off(buf);
X    for (p = buf; *p == ',' || isspace(*p); p++)
X	;
X    if (!*p)
X	(void) strcpy(buf, login);
X    return buf;
X}
X
Xchar *
Xsubject_to(n, buf)
Xregister char *buf;
X{
X    register char *p;
X    buf[0] = 0; /* make sure it's already null terminated */
X    if (!(p = header_field(n, "subject")))
X	return NULL;
X    if (lcase_strncmp(p, "Re:", 3))
X	(void) strcpy(buf, "Re: ");
X    return strcat(buf, p);
X}
X
Xchar *
Xcc_to(n, buf)
Xregister char *buf;
X{
X    register char *p;
X    buf[0] = 0; /* make sure it's already null terminated */
X    if (!(p = header_field(n, "cc")))
X	return NULL;
X    fix_up_addr(p);
X    take_me_off(p);
X    return strcpy(buf, p);
X}
END_OF_FILE
if test 18781 -ne `wc -c <'hdrs.c'`; then
    echo shar: \"'hdrs.c'\" unpacked with wrong size!
fi
# end of 'hdrs.c'
fi
echo shar: End of archive 9 \(of 19\).
cp /dev/null ark9isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 19 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.