v18i031: Mail user's shell version 6.4, Part09/19
Rich Salz
rsalz at bbn.com
Fri Mar 17 02:00:13 AEST 1989
Submitted-by: Dan Heller <island!argv at 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 at 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 at uunet.uu.net.
More information about the Comp.sources.unix
mailing list