From decwrl!ucbvax!tut.cis.ohio-state.edu!cs.utexas.edu!uunet!allbery Mon May  7 07:38:56 PDT 1990
Article 1515 of comp.sources.misc:
Path: decwrl!ucbvax!tut.cis.ohio-state.edu!cs.utexas.edu!uunet!allbery
From: argv@Eng.Sun.COM (Dan Heller)
Newsgroups: comp.sources.misc
Subject: v12i034: Mail User's Shell, Part06/19
Message-ID: <87537@uunet.UU.NET>
Date: 5 May 90 17:18:59 GMT
Sender: allbery@uunet.UU.NET
Lines: 1973
Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)

Posting-number: Volume 12, Issue 34
Submitted-by: argv@Eng.Sun.COM (Dan Heller)
Archive-name: mush/part06

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
#		"End of archive 6 (of 19)."
# Contents:  mush/commands.c mush/curs_io.c
# Wrapped by argv@turnpike on Wed May  2 13:59:24 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'mush/commands.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mush/commands.c'\"
else
echo shar: Extracting \"'mush/commands.c'\" \(33824 characters\)
sed "s/^X//" >'mush/commands.c' <<'END_OF_FILE'
X/* @(#)commands.c	(c) copyright 10/18/86 (Dan Heller) */
X
X#include "mush.h"
X
X/*
X * Note that all of the routines in here act upon and return 0 or -1.
X * if -1, then the main loop will clear message lists.
X */
X
Xstruct cmd cmds[] = {
X#ifdef SIGSTOP
X    { "stop", stop },
X#endif /* SIGSTOP */
X    { "?", 	  question_mark },{ "sh", sh },
X    { "alias", 	  do_alias    },  { "unalias",	do_alias   },
X    { "expand",	  do_alias    },  { "cmd", 	do_alias   },
X    { "uncmd", 	  do_alias    },  { "from",	do_from    },
X    { "un_hdr",	  do_alias    },  { "my_hdr",  	do_alias   },
X    { "fkey", 	  do_alias    },  { "unfkey", 	do_alias   },
X    { "set", 	  set         },  { "unset", 	set 	   },
X    { "ignore",	  set         },  { "unignore", set 	   },
X    { "version",  do_version  },  { "help",	print_help },
X    { "pick", 	  do_pick     },  { "sort", 	sort 	   },
X    { "next",	  readmsg     },  { "previous", readmsg    },
X    { "type",     readmsg     },  { "print",	readmsg    },
X    { "history",  disp_hist   },  { "top",	readmsg	   },
X    { "saveopts", save_opts   },  { "source",   source 	   },
X    { "headers",  do_hdrs     },  { "ls",	ls	   },
X    { "folder",   folder      },  { "update",   folder     },
X    { "cd", 	  cd          },  { "pwd",	cd 	   },
X    { "exit",	  mush_quit   },  { "quit", 	mush_quit  },
X    { "write", 	  save_msg    },  { "save", 	save_msg   },
X    { "copy", 	  save_msg    },  { "folders",  folders    },
X    { "merge",	  merge_folders },
X#ifdef CURSES
X    { "curses",   curses_init },  { "bind",	bind_it    },
X    { "unbind",   bind_it     },  { "bind-macro", bind_it  },
X    { "unbind-macro", bind_it  },
X#endif /* CURSES */
X    { "map",      bind_it     },  { "unmap",       bind_it    },
X    { "map!",     bind_it     },  { "unmap!",      bind_it    },
X    { "preserve", preserve    },  { "unpreserve",  preserve   },
X    { "replyall", respond     },  { "replysender", respond    },
X    { "delete",	  delete      },  { "undelete",    delete     },
X    { "mail", 	  do_mail     },  { "echo",	   do_echo    },
X    { "lpr",      lpr	      },  { "alternates",  alts       },
X    { "edit",	  edit_msg    },  { "flags",	   msg_flags  },
X    { "pipe",     pipe_msg    },  { "eval",	   eval_cmd   },
X    { "undigest", do_undigest },  { "await",	   await      },
X    { NULL, mush_quit }
X};
X
Xstruct cmd ucb_cmds[] = {
X    { "t",   readmsg   }, { "n",  readmsg  }, { "p", readmsg  },
X    { "+",   readmsg   }, { "-",  readmsg  }, { "P", readmsg  },
X    { "Print", readmsg }, { "T",  readmsg  }, { "Type", readmsg },
X    { "x",   mush_quit }, { "q", mush_quit }, { "xit", mush_quit },
X    { ":a",  do_hdrs   }, { ":d", do_hdrs  }, { ":r", do_hdrs },
X    { ":o",  do_hdrs   }, { ":u", do_hdrs  }, { ":n", do_hdrs },
X    { ":s",  do_hdrs   }, { ":p", do_hdrs  },
X    { "z",   do_hdrs   }, { "z-", do_hdrs  }, { "z+", do_hdrs },
X    { "h",   do_hdrs   }, { "H",  do_hdrs  },
X    { "f",   do_from   }, { "m",  do_mail  }, { "alts", alts  },
X    { "d",   delete    }, { "dt", delete   }, { "dp", delete  },
X    { "u",   delete    }, { "fo", folder   },
X    { "s",   save_msg  }, { "co", save_msg }, { "w", save_msg },
X    { "pre", preserve  }, { "unpre", preserve },
X    { "R",   respond   }, { "r",   respond },
X    { "reply", respond }, { "respond", respond },
X    { "v",   edit_msg  }, { "e",   edit_msg },
X    { NULL, mush_quit }
X};
X
Xstruct cmd hidden_cmds[] = {
X    { "debug", toggle_debug }, { "open", 	nopenfiles },
X    { "stty",	my_stty     },
X    { "setenv",	Setenv      }, { "unsetenv", 	Unsetenv   },
X    { "printenv", Printenv  }, { "Pipe",	pipe_msg   },
X    { NULL, mush_quit }
X};
X
Xtoggle_debug(argc, argv)
Xchar **argv;
X{
X    if (argc < 2) /* no value -- toggle "debug" (off/on) */
X	debug = !debug;
X    else
X	debug = atoi(*++argv);
X    print("debugging value: %d\n", debug);
X    return 0;
X}
X
X/* if + was specified, then print messages without headers.
X * n or \n (which will be NULL) will print next unread or undeleted message.
X */
Xreadmsg(x, argv, list)
Xregister char **argv, list[];
X{
X    register char *p = x? *argv : NULL;
X    register long flg = 0;
X    extern FILE *ed_fp;
X
X    if (x && *++argv && !strcmp(*argv, "-?"))
X	return help(0, "readmsg", cmd_help);
X    /* View a message as long as user isn't in the editor.
X     * If ed_fp is not null, then we've got the
X     * file open for typing.  If it's NULL, then an editor is going.
X     */
X    if (ison(glob_flags, IS_GETTING) && !ed_fp) {
X	print("Not while you're in the editor, you don't.\n");
X	return -1;
X    }
X    if (!msg_cnt) {
X	print("No messages.\n");
X	return -1;
X    }
X    if (x)
X	if (!strcmp(p, "top"))
X	    turnon(flg, M_TOP);
X	else if (*p == '+') {
X	    turnon(flg, NO_PAGE);
X	    turnon(flg, NO_HEADER);
X	} else if (isupper(*p))
X	    turnon(flg, NO_IGNORE);
X
X    if (x && (x = get_msg_list(argv, list)) == -1)
X	return -1;
X    else if (x == 0) {  /* no arguments were parsed (or given) */
X	/* get_msg_list sets current msg on */
X	if (isoff(glob_flags, IS_PIPE))
X	    unset_msg_bit(list, current_msg);
X	/* most commands move to the "next" message. type and print don't */
X	if ((!p || !*p || *p == 'n' || *p == '+') && current_msg < msg_cnt &&
X				    isoff(msg[current_msg].m_flags, UNREAD))
X	    current_msg++;
X	if (p && (*p == '-' || !strcmp(p, "previous"))) {
X	    while (--current_msg >= 0 &&
X		(ison(msg[current_msg].m_flags, DELETE) ||
X		 ison(msg[current_msg].m_flags, SAVED)))
X		;
X	    if (current_msg < 0) {
X		print("No previous message.\n");
X		current_msg = 0;
X		return -1;
X	    }
X	} else {
X	    /*
X	     * To be compatible with ucb-mail, find the next available unread
X	     * message.  If at the end, only wrap around if "wrap" is set.
X	     */
X	    if (current_msg == msg_cnt && do_set(set_options, "wrap"))
X		current_msg = 0;
X	    /* "type" or "print" prints the current only -- "next" goes on.. */
X	    if (!p || !*p || *p == 'n')
X		while (current_msg < msg_cnt &&
X		    (ison(msg[current_msg].m_flags, DELETE) ||
X		     ison(msg[current_msg].m_flags, SAVED)))
X			current_msg++;
X	    if (current_msg >= msg_cnt) {
X		print("No more messages.\n");
X		current_msg = msg_cnt - 1;
X		return -1;
X	    }
X	}
X	if (isoff(glob_flags, IS_PIPE))
X	    set_msg_bit(list, current_msg);
X    }
X    current_msg = 0;
X    for (x = 0; x < msg_cnt; x++)
X	if (msg_bit(list, x)) {
X	    current_msg = x;
X#ifdef SUNTOOL
X	    if (istool > 1) {
X		read_mail(NO_ITEM, 0, NO_EVENT);
X		return 0;
X	    }
X#endif /* SUNTOOL */
X	    display_msg(x, flg);
X	}
X    return 0;
X}
X
Xpreserve(n, argv, list)
Xregister int n;		/* no use for argc, so use space for a local variable */
Xregister char **argv, list[];
X{
X    register int unpre;
X
X    unpre = !strncmp(*argv, "un", 2);
X    if (*++argv && !strcmp(*argv, "-?"))
X	return help(0, "preserve", cmd_help);
X    if (get_msg_list(argv, list) == -1)
X	return -1;
X    for (n = 0; n < msg_cnt; n++)
X	if (msg_bit(list, n))
X	    if (unpre) {
X		if (ison(msg[n].m_flags, PRESERVE)) {
X		    turnoff(msg[n].m_flags, PRESERVE);
X		    turnon(glob_flags, DO_UPDATE);
X		}
X	    } else {
X		if (isoff(msg[n].m_flags, PRESERVE)) {
X		    /* || ison(msg[n].m_flags, DELETE)) */
X		    /* turnoff(msg[n].m_flags, DELETE); */
X		    turnon(msg[n].m_flags, PRESERVE);
X		    turnon(glob_flags, DO_UPDATE);
X		}
X	    }
X    if (istool)
X	(void) do_hdrs(0, DUBL_NULL, NULL);
X    return 0;
X}
X
Xlpr(n, argv, list)
Xregister int n;  /* no use for argc, so use its address space for a variable */
Xregister char **argv, list[];
X{
X    register FILE	*pp;
X    register long 	flags = 0;
X    char		print_cmd[128], *printer, c, *cmd;
X    int			total = 0;
X    SIGRET		(*oldint)(), (*oldquit)();
X
X    if (!chk_option("alwaysignore", "printer"))
X	turnon(flags, NO_IGNORE);
X#ifdef MSG_SEPARATOR
X    turnon(flags, NO_SEPARATOR);
X#endif /* MMDF */
X    if (!(printer = do_set(set_options, "printer")) || !*printer)
X	printer = DEF_PRINTER;
X    while (argv && *++argv && **argv == '-') {
X	n = 1;
X	while (c = argv[0][n++])
X	    switch(c) {
X		case 'n': turnon(flags, NO_HEADER);
X		when 'h': turnoff(flags, NO_IGNORE);
X		when 'P': case 'd':
X		    if (!argv[0][n]) {
X		        print("specify printer!\n");
X		        return -1;
X		    }
X		    printer = argv[0] + n;
X		    n += strlen(printer);
X		otherwise: return help(0, "lpr", cmd_help);
X	    }
X    }
X    if (get_msg_list(argv, list) == -1)
X	return -1;
X
X    if (cmd = do_set(set_options, "print_cmd"))
X	(void) strcpy(print_cmd, cmd);
X    else
X#ifdef SYSV
X	(void) sprintf(print_cmd, "%s -d%s", LPR, printer);
X#else
X	(void) sprintf(print_cmd, "%s -P%s", LPR, printer);
X#endif /* SYSV */
X    Debug("print command: %s\n", print_cmd);
X    if (!(pp = popen(print_cmd, "w"))) {
X	error("cannot print");
X	return -1;
X    }
X    on_intr();
X    for (n = 0; isoff(glob_flags, WAS_INTR) && n < msg_cnt; n++) {
X	if (msg_bit(list, n)) {
X	    if (total++)
X		(void) fputc('\f', pp); /* send a formfeed for multiple copies */
X	    print("printing message %d...", n+1);
X	    print_more("(%d lines)\n", copy_msg(n, pp, (u_long) flags, NULL));
X	    turnon(msg[n].m_flags, PRINTED), turnon(glob_flags, DO_UPDATE);
X	}
X    }
X    off_intr();
X    (void) pclose(pp);
X    print_more("%d message%s printed ", total, (total==1)? "": "s");
X    if (cmd)
X	print_more("through \"%s\".\n", cmd);
X    else
X	print_more("at \"%s\".\n", printer);
X    return 0;
X}
X
X/* save [msg_list] [file] */
Xsave_msg(n, argv, list)   /* argc isn't used, so use space for variable 'n' */
Xregister char **argv, list[];
X{
X    register FILE	*mail_fp = NULL_FILE;
X    register char 	*file = NULL, *mode, firstchar = **argv, *tmp = ".";
X    int 		msg_number, force = 0, by_subj = 0, by_author = 0;
X    char		buf[MAXPATHLEN];
X    long 		flg = 0;
X
X    while (*++argv)
X	if (*argv[0] != '-')
X	    break;
X	else
X	    switch (argv[0][1]) {
X		case 'S' :
X		    by_subj = 2;
X		when 's' :
X		    by_subj = 1;
X		when 'A' :
X		    by_author = 2;
X		when 'a' :
X		    by_author = 1;
X		when 'f' :
X		    force = 1;
X		otherwise :
X		    return help(0, "save", cmd_help);
X	    }
X    if (!force && (force = (*argv && !strcmp(*argv, "!"))))
X	argv++;
X    if ((n = get_msg_list(argv, list)) == -1)
X	return -1;
X    argv += n;
X    if (*argv && *(file = *argv) == '\\')
X	file++;
X    else if (!file && !by_subj && !by_author) {
X	/* if no filename specified, save in ~/mbox */
X	if (firstchar == 'w') {
X	    /* mbox should have headers. If he really wants it, specify it */
X	    print("Must specify file name for 'w'\n");
X	    return -1;
X	}
X	if (!(file = do_set(set_options, "mbox")) || !*file)
X	    file = DEF_MBOX;
X    }
X    n = 1; /* tell getpath to ignore no such file or directory */
X    if (file)
X	tmp = getpath(file, &n);
X    if (n < 0) {
X	print("%s: %s\n", file, tmp);
X	return -1;
X    } else if (n && !by_subj && !by_author) {
X	print("%s is a directory\n", file);
X	return -1;
X    }
X    file = tmp;
X    if (force || Access(file, F_OK))
X	mode = "w", force = 0;
X    else
X	mode = "a";
X    if (firstchar != 'w' && *mode == 'a' && !by_author && !by_subj &&
X	    !test_folder(file, "not a folder, save anyway?"))
X	return 0;
X    /*
X     * open the file for writing (appending) unless we're saving by subject
X     * or author name in which case we'll determine the filename later
X     */
X    if (!by_author && !by_subj && !(mail_fp = lock_fopen(file, mode))) {
X	error("cannot save in \"%s\"", file);
X	return -1;
X    }
X
X#ifdef SUNTOOL
X    if (istool)
X	timeout_cursors(TRUE);
X#endif /* SUNTOOL */
X    if (!chk_option("alwaysignore", "save"))
X	turnon(flg, NO_IGNORE);	/* presently overridden by UPDATE_STATUS */
X    if (firstchar == 'w') {
X	turnon(flg, NO_HEADER);
X#ifdef MMDF
X	turnon(flg, NO_SEPARATOR);
X#endif /* MMDF */
X    } else
X	turnon(flg, UPDATE_STATUS);
X
X    for (n = msg_number = 0; msg_number < msg_cnt; msg_number++)
X	if (msg_bit(list, msg_number)) {
X	    if ((by_author || by_subj) && !mail_fp) {
X		char buf2[256], addr[256];
X		register char *p, *p2;
X		if (by_subj) {
X		    if (p = header_field(msg_number, "subject")) {
X			/* convert spaces and non-alpha-numerics to '_' */
X			if (!lcase_strncmp(p, "re: ", 4))
X			    p += 4;
X			for (p2 = p; *p2; p2++)
X			    if (!isalnum(*p2) && !index(".,@#$%-+=", *p2))
X				*p2 = '_';
X		    } else
X			p = "mbox";
X		} else {
X		    (void) reply_to(msg_number, FALSE, buf2);
X		    (void) get_name_n_addr(buf2, NULL, addr);
X		    if (p = rindex(addr, '!'))
X			p++;
X		    else
X			p = addr;
X		    if (p2 = any(p, "@%"))
X			*p2 = 0;
X		}
X		if (!p || !*p)
X		    p = "tmp";
X		(void) sprintf(buf, "%s/%s", file, p);
X		if (force || Access(buf, F_OK))
X		    mode = "w";
X		else
X		    mode = "a";
X		if (firstchar != 'w' && *mode == 'a' &&
X			!test_folder(buf, "not a folder, save anyway?")) {
X		    if (by_author == 2 || by_subj == 2)
X			break;
X		    continue;
X		}
X		if (!(mail_fp = lock_fopen(buf, mode))) {
X		    error("cannot save in \"%s\"", buf);
X		    if (by_author == 2 || by_subj == 2)
X			break;
X		    continue;
X		}
X	    }
X	    print("%sing msg %d ... ",
X		(firstchar == 's')? "Sav" : "Writ", msg_number+1);
X	    print_more("(%d lines)",
X		copy_msg(msg_number, mail_fp, (u_long) flg, NULL));
X	    if (by_author == 1 || by_subj == 1) {
X		print_more(" in \"%s\"", buf);
X		(void) close_lock(buf, mail_fp), mail_fp = NULL_FILE;
X	    }
X	    print_more("\n");
X	    n++;
X	    if (isoff(msg[msg_number].m_flags, SAVED) && firstchar != 'c') {
X		turnon(glob_flags, DO_UPDATE);
X		turnon(msg[msg_number].m_flags, SAVED);
X	    }
X	}
X    if (mail_fp) {
X	(void) close_lock(file, mail_fp);
X	if (!file)
X	    file = buf;
X	print_more("%s %d msg%s to %s\n",
X	    (*mode == 'a')? "Appended" : "Saved", n, (n != 1)? "s": "", file);
X    }
X#ifdef SUNTOOL
X    if (istool) {
X	extern Panel_item folder_item, save_item;
X	timeout_cursors(FALSE);
X	if (firstchar != 'c' && n > 0)
X	    (void) do_hdrs(0, DUBL_NULL, NULL);
X	if (*mode == 'w' && n > 0) {
X	    add_folder_to_menu(folder_item, 3);
X	    add_folder_to_menu(save_item, 1);
X	}
X    }
X#endif /* SUNTOOL */
X    return 0;
X}
X
Xrespond(n, argv, list)
Xregister int n;  /* no use for argc, so use its address space for a variable */
Xregister char **argv, *list;
X{
X    register char *cmd = *argv;
X    char list1[MAXMSGS_BITS];
X    int cur_msg = current_msg, save_cnt = msg_cnt;
X
X    if (*++argv && !strcmp(*argv, "-?"))
X	return help(0, "respond", cmd_help);
X    if ((n = get_msg_list(argv, list)) == -1)
X	return -1;
X
X    /* make into our own list so ~: commands don't overwrite this list */
X    bitput(list, list1, MAXMSGS, =);
X
X    /* back up one arg to replace "cmd" in the new argv[0] */
X    argv += (n-1);
X    if (!strcmp(cmd, "replyall"))
X	Upper(*cmd);
X    strdup(argv[0], cmd);
X
X    /* make sure the *current* message is the one being replied to */
X    for (current_msg = -1, n = 0; n < msg_cnt && current_msg == -1; n++)
X	if (msg_bit(list1, n) && current_msg == -1)
X	    current_msg = n;
X    if (current_msg == -1) { /* "reply -" can cause this to happen */
X	current_msg = cur_msg;
X	return -1;
X    }
X    if (do_mail(1 /* ignored */, argv, list) == -1)
X	return -1;
X    /* New mail may have arrived during do_mail(), which will change
X     * the msg_cnt.  Use the old count when examining the list of bits
X     * to set the replied flag, or the wrong messages can be marked.
X     */
X    for (n = 0; n < save_cnt; n++)
X	if (msg_bit(list1, n)) {
X	    /* set_isread(n); */
X	    set_replied(n); /* only if mail got delivered */
X	}
X    if (istool)
X	(void) do_hdrs(0, DUBL_NULL, NULL);
X    /* copy the specified list back into msg_list */
X    bitput(list1, list, MAXMSGS, =);
X    return 0;
X}
X
X/* cd to a particular directory specified by "p" */
Xcd(x, argv) /* argc, unused -- use space for a non-register variable */
Xregister char **argv;
X{
X    char *cwd, buf[MAXPATHLEN];
X    register char *path, *p = argv[1], *cdpath = NULL, *p2;
X    int err = 0;
X
X    if (argv && argv[1] && !strcmp(argv[1], "-?"))
X	return help(0, argv[0], cmd_help);
X
X    if (!strcmp(*argv, "pwd")) {
X	set_cwd(); /* reset in case some dummy changed $cwd */
X        if ((p = do_set(set_options, "cwd")) && *p) {
X	    print("%s\n", p);
X	    return 0;
X	}
X	return -1;
X    }
X    if (!p || !*p) /* if no args, pwd = ".", cd = ~ */
X	p = (**argv == 'p')? "." : "~";
X    /* if a full path was not specified, loop through cdpath */
X    if (**argv != 'p' && *p != '/' && *p != '~' && *p != '+')
X	cdpath = do_set(set_options, "cdpath");
X    do  {
X	if (cdpath) {
X	    char c;
X	    if (p2 = any(cdpath, " \t:"))
X		c = *p2, *p2 = 0;
X	    (void) sprintf(buf, "%s/%s", cdpath, p);
X	    if (cdpath = p2) /* assign and compare to NULL */
X		*p2 = c;
X	    while (cdpath && (isspace(*cdpath) || *cdpath == ':'))
X		cdpath++;
X	} else
X	    (void) strcpy(buf, p);
X	x = 0;
X	path = getpath(buf, &x);
X	if (x != 1 || chdir(path) == -1)
X	    err = errno;
X	else
X	    err = 0;
X    } while (err && cdpath && *cdpath);
X    if (err)
X	error(p);
X    set_cwd();
X    if ((istool || iscurses || err) && (cwd = do_set(set_options, "cwd"))) {
X	if (err)
X	    turnon(glob_flags, CONT_PRNT);
X	if (iscurses || istool || ison(glob_flags, WARNING))
X	    print("Working dir: %s\n", cwd);
X    }
X    return 0;
X}
X
Xmush_quit(argc, argv)
Xchar **argv;
X{
X    u_long updated = ison(glob_flags, DO_UPDATE);
X
X    if (argc > 1) {
X	if (!strcmp(argv[1], "-?"))
X	    return help(0, "quit", cmd_help);
X	else {
X	    print("%s: too many arguments\n", argv[0]);
X	    return -1;
X	}
X    }
X    if ((!argc || (*argv && **argv == 'q')) && !copyback("Really Quit? "))
X	return -1;
X#ifdef CURSES
X    if (iscurses) {
X	/* we may already be on the bottom line; some cases won't be */
X	move(LINES-1, 0), refresh();
X	if (updated)
X	    putchar('\n');
X    }
X#endif /* CURSES */
X    cleanup(0);
X#ifdef lint
X    return 0;
X#endif /* lint */
X}
X
Xdelete(argc, argv, list)
Xregister int argc;
Xregister char **argv, list[];
X{
X    register int prnt_next, undel = argc && **argv == 'u';
X    int old_msg = current_msg;
X
X    prnt_next = (argv && (!strcmp(*argv, "dt") || !strcmp(*argv, "dp")));
X
X    if (argc && *++argv && !strcmp(*argv, "-?"))
X	return help(0, "delete", cmd_help);
X
X    if (ison(glob_flags, READ_ONLY)) {
X	print("Folder is read-only\n");
X	return -1;
X    }
X
X    if (get_msg_list(argv, list) == -1)
X	return -1;
X    for (argc = 0; argc < msg_cnt; argc++)
X	if (msg_bit(list, argc))
X	    if (undel)
X		turnoff(msg[argc].m_flags, DELETE);
X	    else
X		turnon(msg[argc].m_flags, DELETE);
X
X    /* only if current_msg has been affected && not in curses mode */
X    if (prnt_next == 0 && !iscurses && msg_bit(list, current_msg))
X	prnt_next = !!do_set(set_options, "autoprint"); /* change to boolean */
X
X    turnon(glob_flags, DO_UPDATE);
X
X    /* goto next available message if current was just deleted.
X     * If there are no more messages, turnoff prnt_next.
X     */
X    if (!iscurses && !undel && msg_bit(list, current_msg) &&
X	    (ison(msg[current_msg].m_flags, DELETE) ||
X	    ison(msg[current_msg].m_flags, SAVED)))
X	(void) next_msg();
X    else
X	prnt_next = 0;
X
X    if (prnt_next && !undel && !iscurses && isoff(glob_flags, DO_PIPE))
X	if (old_msg != current_msg && isoff(msg[current_msg].m_flags, DELETE))
X	    display_msg(current_msg, (long)0);
X	else {
X	    if (ison(msg[current_msg].m_flags, DELETE))
X		print("No more messages.\n");
X	    current_msg = old_msg;
X	}
X#ifdef SUNTOOL
X    if (istool && isoff(glob_flags, IS_PIPE)) {
X	char *av[3], buf[8];
X	/* do_hdrs(0, ...) repositions the display, so pass an arg */
X	av[0] = "h";
X	av[1] = sprintf(buf, "%d", n_array[0] + 1);
X	av[2] = NULL;
X	(void) do_hdrs(2, av, NULL);
X    }
X#endif /* SUNTOOL */
X    return 0;
X}
X
X/*
X * historically from the "from" command in ucb-mail, this just prints
X * the composed header of the messages set in list or in pipe.
X */
Xdo_from(n, argv, list)
Xchar **argv, list[];
X{
X    int inc_cur_msg = 0;
X
X    if (argv && *++argv && !strcmp(*argv, "-?"))
X	return help(0, "from", cmd_help);
X    if (argv && *argv && (!strcmp(*argv, "+") || !strcmp(*argv, "-")))
X	if (!strcmp(*argv, "+")) {
X	    if (!*++argv && current_msg < msg_cnt-1)
X		current_msg++;
X	    inc_cur_msg = 1;
X	} else if (!strcmp(*argv, "-")) {
X	    if (!*++argv && current_msg > 0)
X		current_msg--;
X	    inc_cur_msg = -1;
X	}
X    if ((n = get_msg_list(argv, list)) == -1)
X	return -1;
X    else if (argv && argv[n]) {
X	u_long save_flags = glob_flags;
X	char *newargv[6], buf[BUFSIZ];
X	(void) argv_to_string(buf, &argv[n]);
X	newargv[0] = "pick";
X	if (n == 0) {
X	    newargv[++n] = "-r";
X	    newargv[++n] = "*";
X	    turnoff(glob_flags, IS_PIPE);
X	} else {
X	    n = 0;
X	    turnon(glob_flags, IS_PIPE);
X	}
X	newargv[++n] = "-f";
X	newargv[++n] = buf;
X	newargv[++n] = NULL;
X	Debug("calling: "), print_argv(newargv);
X	turnon(glob_flags, DO_PIPE);
X	(void) do_pick(n, newargv, list);
X	glob_flags = save_flags;
X    }
X    for (n = 0; n < msg_cnt; n++)
X	if (msg_bit(list, n)) {
X	    wprint("%s\n", compose_hdr(n));
X	    /* if -/+ given, set current message pointer to this message */
X	    if (inc_cur_msg) {
X		current_msg = n;
X		/* if - was given, then set to first listed message.
X		 * otherwise, + means last listed message -- let it go...
X		 */
X		if (inc_cur_msg < 0)
X		    inc_cur_msg = 0;
X	    }
X	}
X    return 0;
X}
X
X/*
X * Do an ls from the system.
X * Read from a popen and use wprint in case the tool does this command.
X * The folders command uses this command.
X */
Xls(x, argv)
Xchar **argv;
X{
X    register char  *p, *tmp;
X    char	   buf[128];
X    register FILE  *pp;
X
X    if (*++argv && !strcmp(*argv, "-?"))
X	return help(0, "ls", cmd_help);
X    p = buf + strlen(sprintf(buf, "%s -C", LS_COMMAND));
X    for ( ; *argv; ++argv) {
X	x = 0;
X	if (**argv != '-')
X	    tmp = getpath(*argv, &x);
X	else
X	    tmp = *argv;
X	if (x == -1) {
X	    wprint("%s: %s\n", *argv, tmp);
X	    return -1;
X	}
X	*p++ = ' ';
X	p += Strcpy(p, tmp);
X    }
X    if (!(pp = popen(buf, "r"))) {
X	error(buf);
X	return -1;
X    }
X    (void) do_pager(NULL, TRUE);
X    while (fgets(buf, 127, pp) && do_pager(buf, FALSE) != EOF)
X	;
X    (void) pclose(pp);
X    (void) do_pager(NULL, FALSE);
X    return 0;
X}
X
X/*ARGSUSED*/
Xsh(un_used, argv)
Xchar **argv;
X{
X    register char *p;
X    char buf[128];
X
X    if (*++argv && !strcmp(*argv, "-?"))
X	return help(0, "shell", cmd_help);
X    if (!(p = do_set(set_options, "shell")))
X	p = DEF_SHELL;
X    if (!*argv)
X	if (istool) {
X	    print("You can't run an interactive shell from tool mode (yet).");
X	    return -1;
X	} else
X	    (void) strcpy(buf, p);
X    else
X	(void) argv_to_string(buf, argv);
X    if (!istool)
X	echo_on();
X    (void) system(buf);
X    if (!istool)
X	echo_off();
X    return 0;
X}
X
Xstatic
Xsorter(cmd1, cmd2)
Xregister struct cmd *cmd1, *cmd2;
X{
X    return strcmp(cmd1->command, cmd2->command);
X}
X
Xquestion_mark(x, argv)
Xchar **argv;
X{
X    int n = 0, N = sizeof cmds / sizeof (struct cmd);
X    char *Cmds[sizeof cmds/sizeof(struct cmd)], *p, buf[30];
X
X    if (!*++argv) {
X	if (N % 5)
X	    N = N / 5 + 1;
X	else
X	    N = N / 5;
X
X	qsort((char *)cmds, sizeof(cmds)/sizeof(struct cmd)-1,
X			    sizeof(struct cmd), sorter);
X
X	for (x = 0; x < N * 5; x++) {
X	    if (!(x % 5))
X		if (!(p = Cmds[n++] = malloc(80))) {
X		    error("malloc in question_mark()");
X		    free_vec(Cmds);
X		    return -1;
X		}
X	    if (x%5*N+n < sizeof cmds / sizeof (struct cmd))
X		p += strlen(sprintf(p, "%-14.14s ", cmds[x%5*N+n-1].command));
X	}
X	Cmds[n++] = savestr("Type: `command -?' for help with most commands.");
X	Cmds[n] = NULL;
X	(void) help(0, (char *) Cmds, NULL);
X	free_vec(Cmds);
X    } else if (!strcmp(*argv, "-?"))
X	return help(0, "?", cmd_help);
X    else {
X	for (x = 0; cmds[x].command; x++)
X	    if (!strcmp(*argv, cmds[x].command))
X		return cmd_line(sprintf(buf, "\\%s -?", *argv), msg_list);
X	print("Unknown command: %s\n", *argv);
X    }
X    return 0 - in_pipe();
X}
X
X#ifdef SIGSTOP
Xstop(argc, argv)
Xchar **argv;
X{
X    if (istool)
X	print("Not a tool-based option.");
X    if (argc && *++argv && !strcmp(*argv, "-?"))
X	return help(0, "stop", cmd_help);
X    if (kill(getpid(), SIGTSTP) == -1)
X	error("couldn't stop myself");
X    return 0;
X}
X#endif /* SIGSTOP */
X
Xextern char **environ;
Xstatic int spaces = 0;
X
XSetenv(i, argv)
Xchar **argv;
X{
X    char *newstr;
X
X    if (i > 3 || !strcmp(argv[1], "-?"))
X	return help(0, "setenv", cmd_help);
X    else if (i < 2)
X	return Printenv(i, argv);
X
X    if (i == 3) {
X	if (newstr = malloc((unsigned) (strlen(argv[1]) + strlen(argv[2]) + 2)))
X	    (void) sprintf(newstr, "%s=%s", argv[1], argv[2]);
X    } else {
X	if (newstr = malloc((unsigned)(strlen(argv[1]) + 2)))
X	    (void) sprintf(newstr, "%s=", argv[1]);
X    }
X    if (!newstr) {
X	error("setenv: out of memory");
X	return -1;
X    }
X
X    (void) Unsetenv(2, argv);
X
X    for (i = 0; environ[i]; i++);
X    if (!spaces) {
X	char **new_environ =
X		    (char **)malloc((unsigned) ((i+2) * sizeof(char *)));
X	/* add 1 for the new item, and 1 for null-termination */
X	if (!new_environ) {
X	    xfree(newstr);
X	    return -1;
X	}
X	spaces = 1;
X	for (i = 0; new_environ[i] = environ[i]; i++);
X	xfree((char *) environ);
X	environ = new_environ;
X    }
X    environ[i] = newstr;
X    environ[i+1] = NULL;
X    spaces--;
X    return 0;
X}
X
XUnsetenv(n, argv)
Xchar **argv;
X{
X    char **envp, **last;
X
X    if (n != 2 || !strcmp(argv[1], "-?"))
X	return help(0, "unsetenv", cmd_help);
X
X    n = strlen(argv[1]);
X    for (last = environ; *last; last++);
X    last--;
X
X    for (envp = environ; envp <= last; envp++) {
X	if (strncmp(argv[1], *envp, n) == 0 && (*envp)[n] == '=') {
X	    xfree(*envp);
X	    *envp = *last;
X	    *last-- = NULL;
X	    spaces++;
X	}
X    }
X    return 0;
X}
X
XPrintenv(argc, argv)
Xchar **argv;
X{
X    char **e;
X
X    if (argv && argv[1] && !strcmp(argv[1], "-?"))
X	return help(0, "printenv", cmd_help);
X    for (e = environ; *e; e++)
X	if (argc < 2 || !strncmp(*e, argv[1], strlen(argv[1])))
X	    wprint("%s\n", *e);
X    return 0;
X}
X
X/*
X * internal stty call to allow the user to change his tty character
X * settings.  sorry, no way to change cbreak/echo modes.  Save echo_flg
X * so that execute() won't reset it.
X */
X/*ARGSUSED*/
Xmy_stty(un_used, argv)
Xchar **argv;
X{
X    u_long save_echo = ison(glob_flags, ECHO_FLAG);
X
X    if (istool)
X	return 0;
X
X    if (argv && argv[1] && !strcmp(argv[1], "-?"))
X	return help(0, "stty", cmd_help);
X    turnon(glob_flags, ECHO_FLAG);
X    execute(argv);
X    if (save_echo)
X	turnon(glob_flags, ECHO_FLAG);
X    else
X	turnoff(glob_flags, ECHO_FLAG);
X
X    savetty();
X#ifdef TIOCGLTC
X    if (ioctl(0, TIOCGLTC, &ltchars))
X	error("TIOCGLTC");
X#endif /* TIOCGLTC */
X    echo_off();
X    return 0;
X}
X
X/*
X * Edit a message...
X */
Xedit_msg(i, argv, list)
Xchar *argv[], list[];
X{
X    int edited = 0;
X    char buf[MAXPATHLEN], *b, *dir, **edit_cmd, *editor, *mktemp();
X    u_long flags = 0L;
X    char *cmd = *argv;
X    FILE *fp;
X
X    if (istool)
X	return 0;
X
X    if (*++argv && !strcmp(*argv, "-?"))
X	return help(0, "edit_msg", cmd_help);
X
X    if (ison(glob_flags, READ_ONLY)) {
X	print("\"%s\" is read-only.\n", mailfile);
X	return -1;
X    }
X
X    if (get_msg_list(argv, list) == -1)
X	return -1;
X
X    if (!(editor = do_set(set_options,
X	(*cmd == 'v')? "visual" : "editor")) || !*editor)
X	editor = DEF_EDITOR;
X
X    for (i = 0; i < msg_cnt; i++) {
X	if (!msg_bit(list, i))
X	    continue;
X
X	if (edited) {
X	    print("Edit message %d [y/n/q]? ", i+1);
X	    if (Getstr(buf, sizeof (buf), 0) < 0 || lower(buf[0]) == 'q')
X		return 0;
X	    if (buf[0] && buf[0] != 'y')
X		continue;
X	}
X
X	b = buf + Strcpy(buf, editor);
X	*b++ = ' ';
X
X	/* getdir() uses the home directory if no tmpdir */
X	if (!(dir = getdir(do_set(set_options, "tmpdir"))))
Xalted:
X	    dir = ALTERNATE_HOME;
X	(void) mktemp(sprintf(b, "%s/.msgXXXXXXX", dir));
X	if (!(fp = mask_fopen(b, "w+"))) {
X	    if (strcmp(dir, ALTERNATE_HOME))
X		goto alted;
X	    error("can't create %s", b);
X	    return -1;
X	}
X	wprint("editing message %d ...", i+1);
X	/* copy message into file making sure all headers exist. */
X	turnon(flags, UPDATE_STATUS);
X#ifdef MMDF
X	turnon(flags, NO_SEPARATOR);
X#endif /* MMDF */
X	wprint("(%d lines)\n", copy_msg(i, fp, flags, NULL));
X
X	if (edit_cmd = mk_argv(buf, &edited, FALSE)) {
X	    print("Starting \"%s\"...\n", buf);
X	    (void) fclose(fp);
X	    turnon(glob_flags, IS_GETTING);
X	    execute(edit_cmd);
X	    turnoff(glob_flags, IS_GETTING);
X	    free_vec(edit_cmd);
X	    if (load_folder(b, FALSE, (char *)i) > 0) {
X		(void) unlink(b);
X		edited = 1;
X	    }
X	    set_isread(i); /* if you edit it, you read it, right? */
X	}
X    }
X    return 0;
X}
X
X/*
X * Pipe a message list to a unix command.  This function is hacked together
X * from bits of readmsg, above, and other bits of display_msg (misc.c).
X */
Xpipe_msg(x, argv, list)
Xregister char **argv, list[];
X{
X    char *p = x ? *argv : NULL;
X    char buf[256], *pattern = NULL;
X    u_long flg = 0L;
X    extern FILE *ed_fp;
X    int show_deleted = !!do_set(set_options, "show_deleted");
X
X    /* Increment argv only if argv[0] is the mush command "pipe" */
X    if (x && p && (!strcmp(p, "pipe") || !strcmp(p, "Pipe"))) {
X	if (p && *p == 'P')
X	    turnon(flg, NO_HEADER);
X	while (x && *++argv && **argv == '-')
X	    if (!strcmp(*argv, "-?"))
X		return help(0, "pipe_msg", cmd_help);
X	    else if (!strcmp(*argv, "-p") && !(pattern = *++argv)) {
X		print("Specify a pattern with -p\n");
X		return -1;
X	    }
X    }
X    if (!msg_cnt) {
X	print("No messages.\n");
X	return -1;
X    }
X
X    if (x && (x = get_msg_list(argv, list)) == -1)
X	return -1;
X    argv += x;
X    if (!*argv) {
X	turnon(flg, NO_HEADER);
X	/* The constant strings must be constants because user's
X	 * $SHELL might not be appropriate since "sh" scripts are
X	 * usually sent.  User can always (easily) override.
X	 */
X	(void) strcpy(buf, "/bin/sh");
X	if (!pattern)
X	    pattern = "#!";
X    } else
X	(void) argv_to_string(buf, argv);
X    if (!buf[0]) {
X	print("Must specify a legitimate command or shell.\n");
X	return -1;
X    }
X    current_msg = 0;
X    if (!chk_option("alwaysignore", "pipe"))
X	turnon(flg, NO_IGNORE);
X#ifdef MMDF
X    turnon(flg, NO_SEPARATOR);
X#endif /* MMDF */
X    (void) do_pager(buf, -1); /* start pager -- see do_pager() about "-1" */
X    turnoff(glob_flags, WAS_INTR); /* if command interrupts, mush gets it */
X
X    for (x = 0; x < msg_cnt && isoff(glob_flags, WAS_INTR); x++)
X	if (msg_bit(list, x)) {
X	    current_msg = x;
X	    if (!show_deleted && ison(msg[x].m_flags, DELETE)) {
X		print("Message %d deleted; ", x+1);
X		if (iscurses)
X		    print_more("skipping it.");
X		else
X		    print("skipping it.\n");
X		continue;
X	    }
X	    set_isread(x);
X	    if (copy_msg(x, NULL_FILE, flg, pattern) == 0)
X		print("No lines sent to %s!\n", buf);
X	}
X    (void) do_pager(NULL, FALSE); /* end pager */
X    return 0;
X}
X
X/* echo the arguments.  return 0 or -1 if -h given and there are no msgs. */
Xdo_echo(n, argv)
Xregister char **argv;
X{
X    char buf[BUFSIZ], c;
X    int no_return = 0, comp_hdr = 0, as_prompt = 0;
X
X    while (n >= 0 && argv && *++argv && **argv == '-') {
X	n = 1;
X	while (n > 0 && (c = argv[0][n++]))
X	    switch(c) {
X		case 'n': no_return++;
X		when 'h': comp_hdr++;
X		when 'p': as_prompt++;
X		when '?': return help(0, "echo", cmd_help);
X		otherwise: n = -1; break; /* Just echo whatever it was */
X	    }
X    }
X    if (comp_hdr && as_prompt) {
X	print("-h and -p cannot be used together.\n");
X	return -1;
X    }
X
X    (void) argv_to_string(buf, argv);
X    if (comp_hdr) {
X	if (!msg_cnt) {
X	    print("No messages.\n");
X	    return -1;
X	}
X	/* there may be a %-sign, so use %s to print */
X	print("%s", format_hdr(current_msg, buf, FALSE)+9);
X    } else if (as_prompt) {
X	print("%s", format_prompt(current_msg, buf)); /* may be a %-sign */
X    } else
X	print("%s", buf); /* there may be a %-sign in "buf" */
X    if (!no_return)
X	print_more("\n");
X    return 0;
X}
X
Xeval_cmd (argc, argv, list)
Xchar *argv[], list[];
X{
X    int status = -1;
X    u_long save_is_pipe;
X    char **newav, buf[BUFSIZ];
X    int comp_hdr = 0, as_prompt = 0, as_macro = 0;
X
X    while (argv && *++argv && **argv == '-') {
X	int c, n = 1;
X	while (c = argv[0][n++])
X	    switch(c) {
X		case 'h': comp_hdr++;
X		when 'p': as_prompt++;
X		when 'm': as_macro++;
X		otherwise: return help(0, "eval", cmd_help);
X	    }
X    }
X    if (comp_hdr && as_prompt) {
X	print("-h and -p cannot be used together.\n");
X	return -1;
X    }
X
X    (void) argv_to_string(buf, argv);
X    if (as_macro) {
X	m_xlate(buf);
X	mac_queue(buf);
X	return 0;
X    }
X    newav = make_command(buf, TRPL_NULL, &argc);
X    if (comp_hdr) {
X	if (!msg_cnt) {
X	    print("No messages.\n");
X	    return -1;
X	}
X	/* This is inefficient, but the only way to preserve
X	 * imbedded quotes, tabs, etc. in format expansions.
X	 */
X	for (argv = newav; argv && *argv; argv++) {
X	    /* Don't mess with one-character strings */
X	    if (argv[0][1]) {
X		char *format = *argv;
X		*argv = savestr(format_hdr(current_msg, format, FALSE)+9);
X		Debug("expanding (%s) to (%s)\n", format, *argv);
X		xfree(format);
X	    }
X	}
X    } else if (as_prompt) {
X	for (argv = newav; argv && *argv; argv++) {
X	    /* Don't mess with one-character strings */
X	    if (argv[0][1]) {
X		char *tmp = *argv;
X		*argv = savestr(format_prompt(current_msg, tmp));
X		Debug("expanding (%s) to (%s)\n", tmp, *argv);
X		xfree(tmp);
X	    }
X	}
X    }
X    /* Can't use cmd_line() because we want DO_PIPE and IS_PIPE
X     * to remain on -- cmd_line() turns both of them off
X     */
X    if (newav) {
X	save_is_pipe = ison(glob_flags, IS_PIPE);
X	status = do_command(argc, newav, list);
X	if (save_is_pipe)
X	    turnon(glob_flags, IS_PIPE);
X    }
X    return status;
X}
X
Xawait(argc, argv, list)
Xchar *argv[], list[];
X{
X    int done = 0, snooze = 30, last_cnt = msg_cnt;
X
X    if (argc && *++argv) {
X	if (!strcmp(*argv, "-?"))
X	    return help(0, "await", cmd_help);
X	else if (!strcmp(*argv, "-T")) {
X	    if (*++argv && isdigit(**argv) && **argv > '0') {
X		snooze = atoi(*argv);
X	    } else {
X		print("await: integer greater than 0 required for -T\n");
X		return -1;
X	    }
X	}
X    }
X    Debug("snoozing %d\n", snooze);
X
X    do {
X	if (!(done = check_new_mail()))
X	    sleep((unsigned) snooze);
X    } while (!done);
X    /* Known to be safe to pass NULL to chk_two_lists() */
X    if (!chk_option("quiet", "await"))
X	bell();
X
X    while (last_cnt < msg_cnt) {
X	set_msg_bit(list, last_cnt);
X	++last_cnt;
X    }
X
X    return 0;
X}
END_OF_FILE
if test 33824 -ne `wc -c <'mush/commands.c'`; then
    echo shar: \"'mush/commands.c'\" unpacked with wrong size!
fi
# end of 'mush/commands.c'
fi
if test -f 'mush/curs_io.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mush/curs_io.c'\"
else
echo shar: Extracting \"'mush/curs_io.c'\" \(17773 characters\)
sed "s/^X//" >'mush/curs_io.c' <<'END_OF_FILE'
X/* @(#)curs_io.c	(c) copyright 3/18/87 (Dan Heller) */
X
X/* curs_io.c -- curses based I/O */
X#include "mush.h"
X#include "bindings.h"
X#include "glob.h"
X
Xstatic backspace();
X
X#if !defined(M_XENIX) || (defined(M_XENIX) && !defined(CURSES))
Xchar *_unctrl[] = {
X    "^@", "^A", "^B", "^C", "^D", "^E", "^F", "^G", "^H", "^I", "^J", "^K",
X    "^L", "^M", "^N", "^O", "^P", "^Q", "^R", "^S", "^T", "^U", "^V", "^W",
X    "^X", "^Y", "^Z", "^[", "^\\", "^]", "^~", "^_",
X    " ", "!", "\"", "#", "$",  "%", "&", "'", "(", ")", "*", "+", ",", "-",
X    ".", "/", "0",  "1", "2",  "3", "4", "5", "6", "7", "8", "9", ":", ";",
X    "<", "=", ">",  "?", "@",  "A", "B", "C", "D", "E", "F", "G", "H", "I",
X    "J", "K", "L",  "M", "N",  "O", "P", "Q", "R", "S", "T", "U", "V", "W",
X    "X", "Y", "Z",  "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e",
X    "f", "g", "h",  "i", "j",  "k", "l", "m", "n", "o", "p", "q", "r", "s",
X    "t", "u", "v",  "w", "x",  "y", "z", "{", "|", "}", "~", "^?"
X};
X#endif /* !M_XENIX || (M_XENIX && !CURSES) */
X
Xchar	del_line;	/* tty delete line character */
Xchar	del_word;	/* tty delete word character */
Xchar	del_char;	/* backspace */
Xchar	reprint_line;	/* usually ^R */
Xchar	eofc;		/* usually ^D */
Xchar	lit_next;	/* usually ^V */
Xchar	complete;	/* word completion, usually ESC */
Xchar	complist;	/* completion listing, usually ^D */
X
Xtty_settings()
X{
X    savetty();
X
X#ifdef SYSV
X    eofc = _tty.c_cc[VEOF];
X#else
X#ifdef BSD
X    if (ioctl(0, TIOCGETC, &tchars) != -1)
X	eofc = tchars.t_eofc;
X    else
X#endif /* BSD */
X	eofc = CTRL('D');
X#endif /* SYSV */
X
X    if (!isatty(0)) {
X	del_line = CTRL('U');
X	del_char = CTRL('H');
X    } else {
X	del_line = _tty.sg_kill;
X	del_char = _tty.sg_erase;
X    }
X
X#ifdef TIOCGLTC
X    if (ioctl(0, TIOCGLTC, &ltchars) != -1) {
X	del_word = ltchars.t_werasc;
X	reprint_line = ltchars.t_rprntc;
X	lit_next = ltchars.t_lnextc;
X    } else
X#endif /* TIOCGLTC */
X    {
X	del_word = CTRL('W');
X	reprint_line = CTRL('R');
X	lit_next = CTRL('V');
X    }
X}
X
X#ifdef Addch
X#undef Addch
X#endif /* Addch */
X
X#ifndef CURSES
X
X/* Make sure all ifs have matching elses! */
X
X#define Addch(c) \
X    if (ison(glob_flags, ECHO_FLAG)) \
X	{;} \
X    else \
X	(void) fputc(c, stdout), (void) fflush(stdout)
X
X#else
X
X/* see end of Getstr */
X#define Addch(c)  \
X    if (iscurses) \
X	addch(c), refresh(); \
X    else if (ison(glob_flags, ECHO_FLAG)) \
X	{;} \
X    else \
X	(void) fputc(c, stdout), (void) fflush(stdout)
X#endif /* CURSES */
X
X/*
X * get a string of at most 'length' chars.
X * allow backspace-space-backspace, kill word and kill line
X * (options set by user in stty).
X * length is the max length this string can get. offset is from beginning
X * of string.
X * input of ^D returns -1; otherwise, return the number of chars in string.
X */
XGetstr(String, length, offset)
Xchar String[];
Xregister int length;
X{
X    register int c, literal_next = FALSE, lit_bs = FALSE;
X    struct cmd_map *curr_map;
X    int count = offset, save_wc = wrapcolumn;
X
X    (void) fflush(stdout); /* make sure everything is flushed before getting input */
X
X    if (mac_hide) {
X	curr_map = NULL_MAP;
X	wrapcolumn = 0;
X    } else if (ison(glob_flags, IS_GETTING))
X	curr_map = bang_map;
X    else if (iscurses)
X	curr_map = NULL_MAP;
X    else
X	curr_map = line_map;
X
X    while ((c = m_getchar()) != '\n' && c != '\r' && c != EOF &&
X	    isoff(glob_flags, WAS_INTR)) {
X	/* echo isn't set, so whatever the character, enter it */
X	if (ison(glob_flags, QUOTE_MACRO) || ison(glob_flags, ECHO_FLAG)) {
X	    if (count < length) {
X		String[count++] = c;
X		/* Note: Addch includes ECHO_FLAG test */
X		if (iscntrl(c)) {
X		    Addch('^');
X		    Addch(_unctrl[c][1]);
X		} else
X		    Addch(c);
X	    } else {
X		print("\nWarning: string too long. Truncated at %d chars.",
X		    length);
X		if (ison(glob_flags, QUOTE_MACRO)) {
X		    mac_flush();
X		    m_ungetc(reprint_line);
X		    continue;
X		} else
X		    break;
X	    }
X	}
X	/* ^D as the first char on a line or two ^D's in a row is EOF */
X	else if (c == eofc && !count)
X	    break;
X	else if (c == '\\' && count < length) {
X	    literal_next = TRUE, lit_bs = FALSE;
X	    Addch(String[count++] = '\\');
X    	} else if (c == lit_next && count < length) {
X	    literal_next = lit_bs = TRUE;
X	    String[count++] = '\\';
X	    if (!in_macro()) {
X		/* if (iscntrl(c)) */
X		    Addch('^');
X		/* Addch(_unctrl[c][1]); */
X	    }
X	} else if (literal_next) {
X	    struct cmd_map *list;
X
X	    literal_next = FALSE;
X	    if (iscntrl(c) || c == del_line || c == del_char || c == del_word
X		    || c == lit_next || lit_bs)
X		if (!in_macro() || !lit_bs)
X		    backspace(String, &count);
X		else
X		    --count;
X	    else if (in_macro() && c == MAC_LONG_CMD)
X		--count;
X	    /* check to see if user is escaping a map or map! */
X	    else
X		for (list = curr_map; list; list = list->m_next)
X		    if (list->m_str[0] == c) {
X			if (!in_macro())
X			    backspace(String, &count);
X			else
X			    --count;
X			break;
X		    }
X	    /* A literal-next advances the macro offset */
X	    String[count++] = c;
X	    if (iscntrl(c) || c == del_char) {
X		if (iscntrl(c)) {
X		    /*
X		     * Decrement wrapcolumn because two chars added.
X		     * It will be restored from save_wc before return.
X		     */
X		    if (wrapcolumn > 1)
X			wrapcolumn--;
X		    Addch('^');
X		}
X		Addch(_unctrl[c][1]);
X	    } else
X		Addch(c);
X	} else if (complete && (c == complete || c == complist)) {
X	    (void) completion(String, &count, (c == complist));
X	} else if (c == del_line) {
X	    if (count) {
X		do
X		    backspace(String, &count);
X		while (count);
X	    }
X	} else if (c == reprint_line)
X	    String[count] = 0, wprint("\n%s", String);
X	else if (c == del_word) /* word erase */
X	    while (count) {
X		backspace(String, &count);
X		if (!count ||
X		    isspace(String[count-1]) && !isspace(String[count]) ||
X		    !isalnum(String[count-1]) && isalnum(String[count]))
X		    break;
X	    }
X	else if (c == del_char || c == CTRL('H') || c == 127 /* CTRL('?') */) {
X	    if (count)
X		backspace(String, &count);
X	    /* if iscurses, then backspacing too far cancels a function */
X	    else if (!count && iscurses && isoff(glob_flags, LINE_MACRO)) {
X		mac_flush();
X		String[0] = '\0';
X		wrapcolumn = save_wc;
X		return -1;
X	    }
X	} else if (count == length)
X	    bell();
X	else if (c == '\t')
X	    do  {
X		/* Yuck -- tabs break map! */
X		Addch(' ');
X		String[count] = ' ';
X	    } while (++count % 8 && count < length);
X	else if (in_macro() && c == MAC_LONG_CMD) {
X	    char cbuf[MAX_LONG_CMD + 1];
X
X	    if ((c = read_long_cmd(cbuf)) == 0) {
X		c = MAC_LONG_CMD;
X		goto check_expand;	/* How could I avoid this? */
X	    } else if (c > 0) {
X		int ok;
X
X		String[count] = '\0';
X		if ((ok = reserved_cmd(cbuf, TRUE)) > 0) {
X		    /* Reprint the line */
X		    if (iscurses)
X			print(":%s", String);
X		    else
X			wprint("\r%s", String);
X		    continue;	/* Get next char without changing count */
X		} else if (ok < 0) {
X		    String[offset] = '\0';
X		    wrapcolumn = save_wc;
X		    return ok;
X		} else
X		    goto push_back;
X	    } else {
X		/*
X		 * Ooops.  We read a bunch of stuff we should not
X		 * have read, because this isn't really a long command.
X		 * Use a trick to push the whole thing back, ala ungetc.
X		 * Wouldn't it be nifty if stdio worked this way? :-)
X		 */
Xpush_back:
X		if (c > 0) {
X		    cbuf[c++] = MAC_LONG_END;
X		    cbuf[c] = '\0';
X		}
X		c = MAC_LONG_CMD;
X		Ungetstr(cbuf);
X		goto check_expand;	/* How could I avoid this goto? */
X	    }
X	} else {
Xcheck_expand:
X	    if (!curr_map || !check_map(c, curr_map)) {
X	    /* else if (match != MATCH) */
X		if (c != '\t' && iscntrl(c)) {
X		    Addch('^');
X		    Addch(_unctrl[c][1]);
X		    /* Decrement wrapcolumn as above */
X		    if (wrapcolumn > 1)
X			wrapcolumn--;
X		} else
X		    Addch(c);
X		String[count++] = c;
X	    }
X	}
X	/* Null-terminate for macro lookup purposes.
X	 * This will be overwritten by the next character.
X	 */
X	String[count] = '\0';
X	if (line_wrap(String, &count))
X	    break;
X    }
X    (void) fflush(stdout); /* for sys-v folks */
X
X    if (c == eofc || c == EOF || ison(glob_flags, WAS_INTR)) {
X	if (feof(stdin))
X	    clearerr(stdin);
X	wrapcolumn = save_wc;
X	return -1;
X    }
X    if (count && String[count-1] == '\\') {
X	int count2;
X	if (isoff(glob_flags, ECHO_FLAG))
X	    putchar('\n');
X	wrapcolumn = save_wc;
X	/*
X	 * NOTE: If the offset passed here is ever made greater than 0,
X	 * the value of wrapcolumn must again be changed/restored ...
X	 */
X	if ((count2 = Getstr(&String[count-1], length - count + 1, 0)) == -1)
X	    return -1;
X	return count + count2;
X    }
X    if (!iscurses && isoff(glob_flags, ECHO_FLAG))
X	putchar('\n');
X    /* Should be null-terminated already, but just in case */
X    String[count] = '\0';
X    wrapcolumn = save_wc;
X    return count;
X}
X
Xstatic
Xbackspace(str, n)
Xregister char *str;
Xint *n;
X{
X    (*n)--;
X    Addch('\b'); Addch(' '); Addch('\b');
X    if (iscntrl(str[*n])) {
X	Addch('\b'); Addch(' '); Addch('\b');
X	/* Re-increment wrapcolumn -- see Getstr */
X	if (wrapcolumn)
X	    wrapcolumn++;
X    }
X}
X
X#undef Addch
X
X/*
X * Check to see if what the user is typing is supposed to be expanded
X * into a longer string.  The first char is 'c' and the map list to use
X * is in map_list.  Continue looping (reading chars from stdin or a
X * currently active mapping) until a match happens or we've determined
X * that there is no match.
X */
Xcheck_map(c, map_list)
Xchar c;
Xstruct cmd_map *map_list;
X{
X    char mbuf[MAX_MACRO_LEN], *p = mbuf;
X    struct cmd_map *list;
X    int m, n, match;
X
X    *p++ = c;
X
X    while (isoff(glob_flags, WAS_INTR)) {
X	m = 0;
X	*p = 0; /* make sure it's null terminated */
X	/*
X	 * loop thru the list of maps and check to see if the typed
X	 * char matches the mapping.  If it matches completely, substitute
X	 * the stuff in x_str and return.  If a partial match occurs, then
X	 * read the next char until a timeout or no match.
X	 */
X	for (list = map_list; list; list = list->m_next) {
X	    if ((match = prefix(mbuf, list->m_str)) == MATCH) {
X		/* Must turn on flags BEFORE pushing */
X		line_macro(list->x_str);
X		return 1;
X	    } else if (match != NO_MATCH)
X		m++; /* something matched partially */
X	}
X	if (!m)
X	    break;
X	/* see if there's anything on the queue to read... */
X	if (mac_pending()
X#ifdef FIONREAD
X	    || !ioctl(0, FIONREAD, &n) && n > 0
X#else
X#ifdef M_XENIX
X	    || rdchk(0) > 0
X#endif /* M_XENIX */
X#endif /* FIONREAD */
X					       )
X	    *p++ = m_getchar();
X	else {
X	/* The user has typed the first part of a map or macro.  Give him
X	 * a chance to finish it.
X	 */
X#if defined(BSD) || defined(SELECT)
X	    /* If the system has select(), use it.  It's much faster and
X	     * more aesthetic since there is no mandatory timeout.
X	     */
X	    struct timeval timer;
X#ifdef FD_SET
X	    fd_set rmask, wmask, xmask;
X	    FD_SET(0, &rmask);	/* Test stdin for read */
X	    FD_ZERO(&wmask);	/* Don't care about write */
X	    FD_ZERO(&xmask);	/* Don't care about exception */
X#else
X	    int rmask = 1, wmask = 0, xmask = 0;
X#endif /* FD_SET */
X	    timer.tv_sec = 1;
X	    timer.tv_usec = 0;
X	    n = select(1, &rmask, &wmask, &xmask, &timer);
X#else /* !SELECT */
X#ifdef FIONREAD
X	    /* system doesn't have select(), so use FIONREAD to see if
X	     * there are any chars on the queue to read.
X	     */
X	    (void) sleep(1);
X	    (void) ioctl(0, FIONREAD, &n);
X#else
X#ifdef M_XENIX
X	    (void) sleep(1);
X	    n = rdchk(0);
X#else
X
X	    /* system has neither select() nor FIONREAD, so just set n
X	     * and force the user to either complete the map or fail it
X	     * without a timeout.  Chars won't echo till he does one or
X	     * the other.
X	     */
X	    n = 1;
X#endif /* M_XENIX  */
X#endif /* FIONREAD */
X#endif /* SELECT */
X	    if (n > 0)
X		/* don't read all 'n' chars -- there may be a match early */
X		*p++ = m_getchar();	/* To flush macros and reset flags */
X	    else /* still nothing to read? User doesn't want to use map */
X		break;
X	}
X    }
X    /* no match or a timeout.  This isn't a map, just return. */
X    *p = 0;
X    if (mbuf[1])
X	(void) mac_push(mbuf + 1);
X    return 0;
X}
X
X/*
X * Check for line wrap.  This should happen only in composition mode and
X * only when the variable wrapcolumn has a value greater than zero.  Line
X * wrap is implemented using Ungetstr [that is, mac_push()].
X *
X * Returns 1 if the line was wrapped, 0 if not.
X */
Xline_wrap(string, count)
Xchar *string;	/* The string to be wrapped */
Xint *count;	/* Offset of string terminator */
X{
X    char *tail = NULL;
X    int n = *count;
X
X    if (wrapcolumn < 1 || *count <= wrapcolumn
X	    || isoff(glob_flags, IS_GETTING)	/* Wrap only in msg body */
X	    || ison(glob_flags, QUOTE_MACRO)	/* Don't wrap quoted macros */
X	    || ison(glob_flags, ECHO_FLAG))	/* Can't wrap in echo mode */
X	return 0;
X
X    /* Back up past the wrapcolumn point */
X    for (; n > wrapcolumn; --n)
X	;
X    /* Look for a space */
X    while (n && !isspace(string[n]))
X	--n;
X    /* If no break found, return no wrap */
X    if (!n)
X	return 0;
X    tail = &string[n+1];
X    /* Skip the break char and any whitespace */
X    while (n && isspace(string[n]))
X	--n;
X    ++n; /* move back into the whitespace */
X    /* Erase the stuff that will wrap */
X    while (*count > n)
X	backspace(string,count);
X    string[*count] = '\0';
X    /* Push the tail, if any */
X    if (*tail)
X	Ungetstr(tail);
X    return 1;
X}
X
X/*
X * Error bell used by completion()
X */
Xerrbell(ret)
Xint ret;
X{
X    if (ret < 0 || !chk_option("quiet", "completion"))
X	bell();
X    return ret;
X}
X
X/*
X * Perform word completion on the input string
X */
Xcompletion(string, count, showlist)
Xchar *string;	/* The string to be completed */
Xint *count;	/* Offset of string terminator */
Xint showlist;	/* Display list, don't complete */
X{
X    char buf[MAXPATHLEN], *b = buf, **exp;
X    int n = *count, f, len, prefix, trim, overstrike, expandall;
X
X    if (!*string || !*count)
X	return errbell(-1);
X
X    /* Look for a delimiter */
X    while (n > 0 && !index(DELIM, string[--n]))
X	;
X    if (n > 0 || index(DELIM, string[n]))
X	n++;
X    b = buf + (len = Strcpy(buf, &string[n]));
X    Debug("\nexpanding (%s) ... ", buf);
X    if (!any(buf, FMETA)) {
X	expandall = 0;
X	overstrike = (*buf == '+' || *buf == '~' || *buf == '%');
X	trim = (overstrike && len > 1);
X	if (!overstrike || len > 1 || (*buf == '+' && showlist))
X	    *b++ = '*', *b = 0;
X	/* Previous behavior for '+' completions (trailing '/'):
X	if (len > 1 || *buf != '~' || *buf != '%')
X	    *b++ = '*', *b = 0;
X	*/
X	f = filexp(buf, &exp);
X	if (*--b == '*')
X	    *b = 0; /* We need the original buf below */
X    } else {
X	overstrike = 1;
X	trim = (*buf == '+' || *buf == '~');
X	/*
X	 * Check first to see if the base pattern matches.
X	 * If not, append a '*' and try again.
X	 * Don't expand all matches in the latter case.
X	 */
X	if ((f = filexp(buf, &exp)) < 1) {
X	    *b++ = '*', *b = 0;
X	    f = filexp(buf, &exp);
X	    *--b = 0; /* We need the original buf below */
X	    expandall = 0;
X	} else
X	    expandall = 1;
X    }
X    if (!showlist)
X	f = fignore(f, &exp);
X    if (f < 0) {
X	Debug("globbing error!\n%s", string);
X	free_vec(exp);
X	return errbell(-1);
X    } else if (f > 0) {
X	Debug("result is: "), print_argv(exp);
X	if (!expandall && f > 1)
X	    prefix = lcprefix(exp, overstrike ? 0 : len);
X	else
X	    prefix = 0;
X	if (showlist) {
X	    if (!expandall && f > 1)
X		while (prefix && exp[0][prefix - 1] != '/')
X		    --prefix;
X	    putchar('\n');
X	    if (columnate(f, exp, prefix) < 0)
X		(void) errbell(-1);
X	    /* Reprint the line */
X	    if (iscurses) {
X		wprint(":%s", string);
X		turnon(glob_flags, CNTD_CMD);
X	    } else {
X		if (isoff(glob_flags, IS_GETTING))
X		    mail_status(1);
X		wprint("%s", string);
X	    }
X	} else if (expandall || strlen(exp[0]) > len) {
X	    Debug("%s", string);
X	    if (overstrike && (prefix || expandall || f == 1)) {
X		char *tmpv[3];
X		tmpv[0] = buf;
X		if (trim)
X		    tmpv[1] = trim_filename(exp[0]);
X		else
X		    tmpv[1] = exp[0];
X		tmpv[2] = NULL;
X		/* Back up as far as is necessary */
X		len = lcprefix(tmpv, 0);
X		/* If nothing will be erased, we may need to beep */
X		if (n + len == *count) {
X		    if (!expandall && !tmpv[1][len])
X			(void) errbell(0);
X		}
X		/* Erase the stuff that will complete */
X		while (*count > n + len)
X		    backspace(string,count);
X		string[*count] = '\0';
X	    }
X	    if (expandall || f == 1) {
X		/* Unget the names IN REVERSE ORDER! */
X		while (f--) {
X		    if (trim)
X			b = trim_filename(exp[f]);
X		    else
X			b = exp[f];
X		    if (f) {
X			Ungetstr(b);
X			Ungetstr(" ");
X		    } else
X			Ungetstr(b + len);
X		}
X	    } else {
X		if (prefix > len) {
X		    exp[0][prefix] = 0;
X		    Debug("\ncompletion is (%s)\n%s", exp[0], string);
X		    if (trim)
X			Ungetstr(trim_filename(exp[0]) + len);
X		    else
X			Ungetstr(&exp[0][len]);
X		} else
X		    Debug("\nno longer prefix\n%s", string);
X		/* Special case because "+" always tries to expand "+*"
X		 * to get listings and avoid getpath()'s trailing '/'.
X		 * No error bell is needed in those cases.
X		 */
X		if (strcmp(buf, "+") != 0)
X		    (void) errbell(0);
X	    }
X	} else {
X	    Debug("no longer prefix\n%s", string);
X	    (void) errbell(0);
X	}
X    } else {
X	Debug("no match\n%s", string);
X	(void) errbell(0);
X    }
X    free_vec(exp);
X    return 1;
X}
X
Xfignore(argc, argvp)
Xint argc;
Xchar ***argvp;
X{
X    char *fign = do_set(set_options, "fignore");
X    char **flist, buf[MAXPATHLEN], *b = buf;
X    int fcnt, i;
X
X    if (argc < 2 || !fign || !*fign)
X	return argc;
X    if (!argvp || !*argvp && !**argvp)
X	return -1;
X    
X    if ((flist = mk_argv(fign, &fcnt, FALSE)) && fcnt > 0) {
X	*b++ = '*';
X	for (i = 0; i < fcnt; i++) {
X	    if (flist[i][0] == '.' && !any(flist[i], FMETA)) {
X		(void) strcpy(b, flist[i]);
X		(void) strdup(flist[i], buf);
X	    }
X	}
X	Debug("ignoring "), print_argv(flist);
X	fcnt = gdiffv(argc, argvp, fcnt, flist);
X	free_vec(flist);
X	if (fcnt == 0)
X	    fcnt = argc;
X    }
X    return fcnt;
X}
END_OF_FILE
if test 17773 -ne `wc -c <'mush/curs_io.c'`; then
    echo shar: \"'mush/curs_io.c'\" unpacked with wrong size!
fi
# end of 'mush/curs_io.c'
fi
echo shar: End of archive 6 \(of 19\).
cp /dev/null ark6isdone
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


