rsalz@bbn.com (Rich Salz) (04/15/88)
Submitted-by: island!argv@sun.com (Dan Heller) Posting-number: Volume 14, Issue 40 Archive-name: mush6.0/part08 #! /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 8 (of 14)." # Contents: commands.c mush.h # Wrapped by rsalz@fig.bbn.com on Wed Apr 13 20:04:50 1988 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'commands.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'commands.c'\" else echo shar: Extracting \"'commands.c'\" \(21317 characters\) sed "s/^X//" >'commands.c' <<'END_OF_FILE' X/* @(#)cmds.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 struct 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", quit }, { "quit", quit }, X { "write", save_msg }, { "save", save_msg }, X { "copy", save_msg }, { "folders", folders }, X#ifdef CURSES X { "curses", curses_init }, { "bind", bind_it }, { "unbind", bind_it }, X#endif /* CURSES */ 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 { NULL, quit } X}; X struct cmd ucb_cmds[] = { X { "t", readmsg }, { "n", readmsg }, { "p", readmsg }, X { "+", readmsg }, { "-", readmsg }, { "P", readmsg }, X { "Print", readmsg }, { "T", readmsg }, { "Type", readmsg }, X { "x", quit }, { "xit", quit }, { "q", quit }, X { ":a", do_hdrs }, { ":d", do_hdrs }, { ":r", do_hdrs }, X { ":o", do_hdrs }, { ":u", do_hdrs }, { ":n", 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 { NULL, quit } X}; X struct cmd hidden_cmds[] = { X { "debug", toggle_debug }, { "open", nopenfiles }, X { "flags", msg_flags }, { "stty", my_stty }, X { "setenv", Setenv }, { "unsetenv", Unsetenv }, X { "printenv", Printenv }, X { NULL, quit } X}; X toggle_debug(argc, argv) char **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 -1; 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 */ readmsg(x, argv, list) register 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. If is_getting X * is set, then the user is typing in a letter (or something else not X * threatening in tool mode). 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, 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 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 && ison(msg[current_msg].m_flags, DELETE)) 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 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 set_msg_bit(list, current_msg); X } X /* If we're piping messages, just return the message list */ X if (ison(glob_flags, DO_PIPE)) X return 0; 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 preserve(n, argv, list) register int n; /* no use for argc, so use space for a local variable */ register 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_help", 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 turnon(msg[n].m_flags, PRESERVE); X turnon(glob_flags, DO_UPDATE); X turnoff(msg[n].m_flags, DELETE); X } X } X if (istool) X (void) do_hdrs(0, DUBL_NULL, NULL); X return 0; X} X lpr(n, argv, list) register int n; /* no use for argc, so use its address space for a variable */ register 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, (*oldint)(), (*oldquit)(); X X turnon(flags, NO_IGNORE); 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 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, flags)); 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] */ save_msg(n, argv, list) /* argc isn't used, so use space for variable 'n' */ register char **argv, list[]; X{ X register FILE *mail_fp; X register char *file = NULL, *mode, firstchar = **argv, *tmp; X int msg_number, force; X long flg = 0; X X if (*++argv && !strcmp(*argv, "-?")) X return help(0, "save_help", cmd_help); X if (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) { 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 tmp = getpath(file, &n); X if (n < 0) { X print("%s: %s\n", file, tmp); X return -1; X } else if (n) { 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 X if (!(mail_fp = fopen(file, mode))) { X error("cannot save in \"%s\"", file); X return -1; X } X#ifdef SUNTOOL X if (istool) X lock_cursors(); X#endif /* SUNTOOL */ X turnon(flg, NO_IGNORE); X if (firstchar == 'w') X turnon(flg, NO_HEADER); X else X turnon(flg, UPDATE_STATUS); X if (do_set(set_options, "keepsave")) X firstchar = 'c'; X for (n = msg_number = 0; msg_number < msg_cnt; msg_number++) X if (msg_bit(list, msg_number)) { X print("%sing msg %d... ", X (firstchar == 's')? "Sav" : "Writ", msg_number+1); X print_more("(%d lines)\n", copy_msg(msg_number, mail_fp, flg)); X /* only mark "deleted" if mailfile is /usr/spool/mail and X * we're not "copying. If keepsave is set, then firstchar X * will have already been changed to 'c' X */ X if (!strcmp(mailfile, spoolfile) && firstchar != 'c' && X isoff(glob_flags, READ_ONLY)) X turnon(msg[msg_number].m_flags, DELETE); X n++; X } X fclose(mail_fp); X print_more("%s %d msg%s to %s\n", X (*mode == 'a')? "Appended" : "Saved", n, (n != 1)? "s": "", file); X if (!strcmp(mailfile, spoolfile)) X turnon(glob_flags, DO_UPDATE); X#ifdef SUNTOOL X if (istool) { X unlock_cursors(); X (void) do_hdrs(0, DUBL_NULL, NULL); X add_folder_to_menu(folder_item, 3); X add_folder_to_menu(save_item, 1); X } X#endif /* SUNTOOL */ X return 0; X} X respond(n, argv, list) register int n; /* no use for argc, so use its address space for a variable */ register char **argv, *list; X{ X register char *cmd = *argv; X char list1[MAXMSGS_BITS]; X X if (*++argv && !strcmp(*argv, "-?")) X return help(0, "respond_help", cmd_help); X if ((n = get_msg_list(argv, list)) == -1) X return -1; X /* make into our own list so ~: commands don't overwrite this list */ X bitput(list, list1, MAXMSGS, =); 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 current_msg = -1; X for (n = 0; n < msg_cnt; n++) X if (msg_bit(list1, n)) { X if (current_msg > -1 && istool) { X print("tool mode can not respond to multiple messages."); X continue; X } X current_msg = n; X set_isread(n); X if (do_mail(1 /* ignored */, argv, list) == -1) X return -1; X set_replied(n); /* only if mail got delivered */ X#ifdef SUNTOOL X if (istool) X do_hdrs(0, DUBL_NULL, NULL); X#endif /* SUNTOOL */ X } X return 0; X} X X/* cd to a particular directory specified by "p" */ cd(x, argv) /* argc, unused -- use space for a non-register variable */ register char **argv; X{ X char cwd[MAXPATHLEN], buf[MAXPATHLEN]; X register char *path, *p = argv[1], *cdpath = NULL, *p2; X#ifdef SYSV X char *getcwd(); X#else X char *getwd(); X#endif /* SYSV */ X int err = 0; X X if (!strcmp(*argv, "pwd") && (p = do_set(set_options, "cwd")) && *p) { X print("%s\n", p); 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 thru 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)) 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#ifdef SYSV X if (getcwd(cwd, 128) == NULL) X#else X if (getwd(cwd) == NULL) X#endif /* SYSV */ X print("can't get cwd: %s.\n", cwd), err++; X else { X char **new_argv; X (void) sprintf(buf, "set cwd = \"%s\"", cwd); X Debug("%s\n", buf); X if (new_argv = mk_argv(buf, &x, 1)) X (void) add_option(&set_options, new_argv), free_vec(new_argv); X } X if (istool || iscurses || err) { X if (err) X turnon(glob_flags, CONT_PRNT); X print("Working dir: %s\n", cwd); X } X return -1; X} X quit(argc, argv) char **argv; X{ X u_long updated = ison(glob_flags, DO_UPDATE); X X if (argc > 1 && !strcmp(argv[1], "-?")) X return help(0, "quit_help", cmd_help); X if ((!argc || (*argv && **argv == 'q')) && ison(glob_flags, DO_UPDATE) X && !copyback()) 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 delete(argc, argv, list) register int argc; register 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 print("usage: delete/undelete [msg_list]\n"); X return -1; X } 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 && ison(msg[current_msg].m_flags, DELETE)) X next_msg(); X X if (prnt_next && !undel && !iscurses) X if (old_msg != current_msg && isoff(msg[current_msg].m_flags, DELETE)) X display_msg(current_msg, (long)0); X else X print("No more messages.\n"), current_msg = old_msg; X#ifdef SUNTOOL X if (istool) X (void) do_hdrs(0, DUBL_NULL, NULL); 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 */ do_from(n, argv, list) char **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 (get_msg_list(argv, list) == -1) X return -1; 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 */ ls(x, argv) char **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 if (!(p = do_set(set_options, "lister"))) X p = ""; X (void) sprintf(buf, "%s -C%s ", LS_COMMAND, p); X p = buf+strlen(buf); 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 += strlen(sprintf(p, " %s", tmp)); X } X if (!(pp = popen(buf, "r"))) { X error(buf); X return -1; X } X turnon(glob_flags, IGN_SIGS); X while (fgets(buf, 127, pp)) X wprint(buf); X (void) pclose(pp); X turnoff(glob_flags, IGN_SIGS); X return 0; X} X sh(un_used, argv) char **argv; X{ X register char *p; X char buf[128]; X X if (istool > 1 || *++argv && !strcmp(*argv, "-?")) X return help(0, "shell", cmd_help); X if (!(p = do_set(set_options, "shell")) X && !(p = do_set(set_options, "SHELL"))) X p = DEF_SHELL; X if (!*argv) X (void) strcpy(buf, p); X else X (void) argv_to_string(buf, argv); X echo_on(); X (void) system(buf); X echo_off(); X return 0; X} X static sorter(cmd1, cmd2) register struct cmd *cmd1, *cmd2; X{ X return strcmp(cmd1->command, cmd2->command); X} X question_mark(x, argv) char **argv; X{ X int n = 0; X char *Cmds[50], *p, *malloc(), buf[30]; X X qsort((char *)cmds, sizeof(cmds)/sizeof(struct cmd)-1, X sizeof(struct cmd), sorter); X X if (!*++argv) { X for (x = 0; cmds[x].command; 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 p += strlen(sprintf(p, "%-11.11s ", cmds[x].command)); X } X Cmds[n++] = savestr("Type: `command -?' for help with most commands."); X Cmds[n] = NULL; X (void) help(0, Cmds, NULL); X free_vec(Cmds); 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 -1; X} X X#ifdef SIGSTOP stop(argc, argv) char **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 -1; X} X#endif /* SIGSTOP */ X extern char **environ; static int spaces = 0; X Setenv(i, argv) char **argv; X{ X char *newstr; X X if (i < 2 || i > 3 || !strcmp(argv[1], "-?")) X return help(0, "setenv", cmd_help); X X if (i == 3) { X newstr = malloc(strlen(argv[1]) + strlen(argv[2]) + 2); X (void) sprintf(newstr, "%s=%s", argv[1], argv[2]); X } else { X newstr = malloc(strlen(argv[1]) + 2); X (void) sprintf(newstr, "%s=", argv[1]); X } X X (void) Unsetenv(2, argv); X X for (i = 0; environ[i]; i++); X if (!spaces) { X char **new_environ = (char **)malloc((i+2) * sizeof(char *)); X /* add 1 for the new item, and 1 for null-termination */ X if (!new_environ) { X free(newstr); X return -1; X } X spaces = 1; X for (i = 0; new_environ[i] = environ[i]; i++); X xfree(environ); X environ = new_environ; X } X environ[i] = newstr; X environ[i+1] = NULL; X spaces--; X return -1; X} X Unsetenv(n, argv) char **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 -1; X} X Printenv() X{ X char **e = environ; X while (*e) X wprint("%s\n", *e++); X return -1; 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 */ my_stty(un_used, argv) char **argv; X{ X u_long save_echo = ison(glob_flags, ECHO_FLAG); X 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, <chars)) X error("TIOCGLTC"); X#endif /* TIOCGLTC */ X echo_off(); X return -1; X} END_OF_FILE if test 21317 -ne `wc -c <'commands.c'`; then echo shar: \"'commands.c'\" unpacked with wrong size! fi # end of 'commands.c' fi if test -f 'mush.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'mush.h'\" else echo shar: Extracting \"'mush.h'\" \(20902 characters\) sed "s/^X//" >'mush.h' <<'END_OF_FILE' X/* @(#)mush.h (c) copyright 1986 (Dan Heller) */ X X#define VERSION "Mail User's Shell (Vers 6.0) Sat Apr 2 19:36:07 PST 1988" X X#include "config.h" X X#ifdef CURSES X#ifdef USG X# define _USG X# undef USG X#endif /* USG */ X#include <curses.h> X#if !defined(USG) && defined(_USG) X# define USG X# undef _USG X#endif /* USG && _USG */ X#else /* CURSES */ X#include <stdio.h> X#if defined(SYSV) && defined(USG) X#include <termio.h> X#endif /* SYSV && USG */ X#endif /* CURSES */ X X#include <ctype.h> X#include <errno.h> X#include <setjmp.h> X#include "strings.h" X X#ifdef SUNTOOL X# include <suntool/tool_hs.h> X#else /* SUNTOOL */ X# include <sys/types.h> X# include <signal.h> X# ifndef SYSV X# include <sys/time.h> X# include <sys/ioctl.h> /* for ltchars */ X# else X# include <time.h> X# include <fcntl.h> X# endif /* SYSV */ X#endif /* SUNTOOL */ X X#include <sys/stat.h> X#include <sys/file.h> X X#ifdef SUNTOOL X# include <suntool/gfxsw.h> X# include <suntool/panel.h> X# include <suntool/ttysw.h> X# include <suntool/ttytlsw.h> X# include <suntool/menu.h> X# include <suntool/icon_load.h> X#endif /* SUNTOOL */ X X/* if no maximum number of files can be found, we'll use getdtablesize() */ X#ifdef _NFILE X# define MAXFILES _NFILE X#else X#ifdef NOFILE X# define MAXFILES NOFILE X#endif /* NOFILE */ X#endif /* NFILE */ X X#ifndef CTRL X#define CTRL(c) ('c' & 037) X#endif /* CTRL */ X X#define ESC '\033' X X#define NO_STRING "" X#ifdef NULL X#undef NULL X#endif /* NULL */ X#define NULL (char *)0 X#define NULL_FILE (FILE *)0 X#define DUBL_NULL (char **)0 X#define TRPL_NULL (char ***)0 X#ifdef putchar X#undef putchar X#endif /* putchar */ X#define putchar(c) fputc(c, stdout) X#define bell() fputc('\007', stderr) X X/* For error recovery purposes, send keyboard generated signals to a special X * routine (interrupt) to set a global flag (WAS_INTR) and return to the X * calling routine which is responsible for checking the flag. For both X * on_intr() and off_intr() macros, initialize WAS_INTR to false. X */ X#define on_intr() \ X turnoff(glob_flags, WAS_INTR), oldint = signal(SIGINT, interrupt), \ X oldquit = signal(SIGQUIT, interrupt) X X#define off_intr() \ X turnoff(glob_flags, WAS_INTR), (void) signal(SIGINT, oldint), \ X (void) signal(SIGQUIT, oldquit) X X/* Don't flush input when setting echo or cbreak modes (allow typeahead) */ X#ifdef TIOCSETN X#ifdef stty X#undef stty X#endif /* stty */ X#define stty(fd, sgttybuf) (ioctl(fd, TIOCSETN, sgttybuf)) X#endif /* TIOCSETN */ X X/* for system-V machines that run termio */ X#if defined(SYSV) && defined(USG) unsigned char vmin; X#define sg_erase c_cc[2] X#define sg_flags c_lflag X#define sg_kill c_cc[3] X#define sg_ospeed c_cflag X#define gtty(fd, SGTTYbuf) ioctl(fd, TCGETA, SGTTYbuf) X#undef stty X#define stty(fd, SGTTYbuf) ioctl(fd, TCSETAW, SGTTYbuf) X#define echon() (_tty.sg_flags |= (ECHO|ECHOE), stty(0, &_tty)) X#define echoff() (_tty.sg_flags &= ~ECHO, stty(0, &_tty)) X#define cbrkon() \ X (_tty.sg_flags &= ~ICANON, _tty.c_cc[VMIN] = 1, stty(0, &_tty)) X#define cbrkoff() \ X (_tty.sg_flags |= ICANON, _tty.c_cc[VMIN] = vmin, stty(0, &_tty)) X#define savetty() (void) gtty(0, &_tty), vmin = _tty.c_cc[VMIN] X#define cbreak() cbrkon() X#define nocbreak() cbrkoff() X X/* If curses isn't defined, declare our 'tty' and macros for echo/cbreak */ X#ifndef CURSES typedef struct termio SGTTY; X#define echom() echon() X#define noechom() echoff() X#define crmode() cbrkon() X#define nocrmode() cbrkoff() X X#else /* CURSES */ X/* If curses is defined, use the echo/cbreak commands in library only X * if cursees is running. If curses isn't running, use macros above. X */ X#define echom() ((iscurses) ? echo(): echon()) X#define noechom() ((iscurses) ? noecho(): echoff()) X#define crmode() ((iscurses) ? cbreak() : cbrkon()) X#define nocrmode() ((iscurses) ? nocbreak() : cbrkoff()) X#endif /* CURSES */ X#endif /* SYSV && USG */ X X#if !defined(USG) X#ifndef CURSES X/* if curses is not defined, simulate the same tty based macros */ typedef struct sgttyb SGTTY; X/* Do real ioctl calls to set the tty modes */ X#define crmode() (_tty.sg_flags |= CBREAK, stty(0, &_tty)) X#define nocrmode() (_tty.sg_flags &= ~CBREAK, stty(0, &_tty)) X#define echom() (_tty.sg_flags |= ECHO, stty(0, &_tty)) X#define noechom() (_tty.sg_flags &= ~ECHO, stty(0, &_tty)) X#define savetty() (void) gtty(0, &_tty) X#else /* CURSES */ X#define echom() echo() X#define noechom() noecho() X#endif /* ~CURSES */ X#endif /* ~USG */ X X/* With all that out of the way, we can now declare our tty type */ SGTTY _tty; X X/* These macros now turn on/off echo/cbreak independent of the UNIX running */ X#define echo_on() \ X if (_tty.sg_flags && isoff(glob_flags, ECHO_FLAG)) nocrmode(), echom() X#define echo_off() \ X if (_tty.sg_flags && isoff(glob_flags, ECHO_FLAG)) crmode(), noechom() X X#define strdup(dst, src) (xfree (dst), dst = savestr(src)) X#define Debug if (debug) printf X X#ifdef SYSV X#ifndef L_SET X#define L_SET 0 X#endif /* L_SET */ X#ifndef F_OK X#define F_OK 000 X#define R_OK 004 X#define W_OK 002 X#define E_OK 001 X#endif /* F_OK */ typedef unsigned long u_long; X#define vfork fork X#ifdef SIGCHLD X#undef SIGCHLD X#endif /* SIGCHLD */ X#define SIGCHLD SIGCLD X#endif /* SYSV */ X X#if !defined(SUNTOOL) && !defined(CURSES) X X#define TRUE 1 X#define FALSE 0 X#define print printf X#define wprint printf X#define print_more printf X X#endif /* SUNTOOL && !CURSES */ X X#ifndef max X#define max(a,b) (((a) > (b)) ? (a) : (b)) X#define min(a,b) (((a) < (b)) ? (a) : (b)) X#endif /* max */ X X#if defined(CURSES) && !defined(SUNTOOL) X#define wprint printf X#endif /* !SUNTOOL && CURSES */ X X#if defined(CURSES) || defined(SUNTOOL) X#define print_more turnon(glob_flags, CONT_PRNT), print void print(); /* printf to window or curses or tty accordingly */ X#endif /* CURSES || SUNTOOL */ X X#ifdef SUNTOOL X X#define NO_ITEM (Panel_item)0 X#define NO_EVENT (struct inputevent *)0 X#define TIME_OUT 60 /* sleep 60 secs between mailchecks */ X#define PIX_XOR PIX_SRC ^ PIX_DST X#define ID event.ie_code X#define l_width(font) fonts[font]->pf_defaultsize.x /* width of letter */ X#define l_height(font) fonts[font]->pf_defaultsize.y /* height of letter */ X#define Clrtoeol(w,x,y,f) pw_text(w, x, y, PIX_SRC, fonts[f], blank) X#define type_cursor(op) pw_char(msg_win, txt.x,txt.y, op, fonts[curfont],'_') X X#define highlight(win,x,y,font,s) \ X pw_text(win,x,y, PIX_SRC, fonts[font],s), \ X pw_text(win,x+1,y, \ X (ison(glob_flags, REV_VIDEO))? PIX_NOT(PIX_SRC): PIX_SRC|PIX_DST, \ X fonts[font],s) X X/* Fonts */ X#define FONTDIR "/usr/lib/fonts/fixedwidthfonts" X#define DEFAULT 0 X#define SMALL 1 X#define LARGE 2 X#define MAX_FONTS 3 X X#endif /* SUNTOOL */ X X/* bits and pieces */ X#define turnon(flg,val) ((flg) |= ((u_long)1 << ((u_long)(val)-1L))) X#define turnoff(flg,val) ((flg) &= ~((u_long)1 << ((u_long)(val)-1L))) X#define ison(flg,val) ((u_long)(flg) & ((u_long)1 << ((u_long)(val)-1L))) X#define isoff(flg,val) (!ison((flg), (val))) X#define set_replied(n) \ X if (isoff(msg[n].m_flags, REPLIED)) \ X turnon(glob_flags, DO_UPDATE), turnon(msg[n].m_flags, REPLIED) X#define set_isread(n) \ X if (ison(msg[n].m_flags, UNREAD)) \ X turnon(glob_flags, DO_UPDATE), turnoff(msg[n].m_flags, UNREAD) X X/* msg lists represented by bits (8 should be replaced by sizeof(char) */ X#define clear_msg_list(list) (void) bzero(list, (msg_cnt+7)/8) X#define msg_bit(list, n) ((list[(n) / 8] >> ((n) % 8)) & 1) X#define set_msg_bit(list, n) (list[(n) / 8] |= (1 << ((n) % 8))) X#define unset_msg_bit(list, n) (list[(n) / 8] &= ~(1 << ((n) % 8))) X#define bput(S1, S2, Len, op) \ X { \ X register char *s1 = S1, *s2 = S2; \ X register int len = Len; \ X while(len--) \ X *s2++ op *s1++; \ X } X#define bitput(m1,m2,len,op) bput(m1, m2, (((len)+7)/8), op) X X/* convenience and/or readability */ X#define when break;case X#define otherwise break;default X#define lower(c) (isupper(c)? tolower(c): c) X#define Lower(c) (c = lower(c)) X#define upper(c) (islower(c)? toupper(c): c) X#define Upper(c) (c = upper(c)) X#define skipspaces(n) for(p += (n); *p == ' ' || *p == '\t'; ++p) X#define skipdigits(n) for(p += (n); isdigit(*p); ++p) X X#define NO_FLG 0 X X/* various flags */ long glob_flags; /* global boolean flags thruout the whole program */ X#define DO_UPDATE 1 /* check for changes to avoid unnecessary copyback */ X#define REV_VIDEO 2 /* reverse video for curses or toolmode */ X#define CONT_PRNT 3 /* continue to print (maybe a printf) without a '\n' */ X#define DO_SHELL 4 /* run a shell even if no mail? (true if tool) */ X#define DO_PIPE 5 /* true if commands are piping to another command */ X#define IS_PIPE 6 /* true if commands' "input" is piped from another */ X#define IGN_SIGS 7 /* true if catch() should not longjump */ X#define IGN_BANG 8 /* ignore ! as a history reference (see source()) */ X#define ECHO_FLAG 9 /* if true, echo|cbreak is ON, echo typing (-e) */ X#define IS_GETTING 10 /* true if we're getting input for a letter */ X#define PRE_CURSES 11 /* true if curses will be run, but hasn't started yet */ X#define READ_ONLY 12 /* -r passed to folder() (or main) setting read only */ X#define REDIRECT 13 /* true if stdin is being redirected */ X#define WAS_INTR 14 /* catch interrupts, set this flag (signals.c) */ X#define WARNING 15 /* if set, various warning messages may be printed */ X#define NEW_MAIL 17 /* new mail has arrived; user is busy or in icon mode */ X#define CNTD_CMD 18 /* curses.c -- "...continue..." prompt in curses */ X#define IS_SENDING 19 /* was started to send mail, not to be run as a shell */ X#define MIL_TIME 20 /* if $mil_time is set, use 24hr military time fmt */ X X#define VERBOSE 1 /* verbose flag for sendmail */ X#define INCLUDE 2 /* include msg in response */ X#define INCLUDE_H 3 /* include msg with header */ X#define EDIT 4 /* enter editor by defualt on mailing */ X#define SIGN 5 /* auto-include ~/.signature in mail */ X#define DO_FORTUNE 6 /* add a fortune at end of msgs */ X X/* msg flags */ X#define NO_HEADER 7 /* don't print header of message (top, write) */ X#define DELETE 8 X#define OLD 9 X#define UNREAD 10 X#define UPDATE_STATUS 11 /* change status of msg when copyback */ X#define NO_PAGE 12 /* don't page this message */ X#define INDENT 13 /* indent included msg with string */ X#define NO_IGNORE 14 /* don't ignore headers */ X#define PRESERVE 15 /* preserve in mailbox unless deleted */ X#define TOP 15 /* just print the top of msg (same as pre) */ X#define FORWARD 16 /* Forward messages into the message buffer */ X#define REPLIED 17 /* Messages that have been replied to */ X#define NEW_SUBJECT 18 /* new subject regardless of $ask (mail -s) */ X X#define MAXMSGS_BITS MAXMSGS/sizeof(char) /* number of bits for bitmap */ X struct msg { X u_long m_flags; X long m_offset; /* offset in tempfile of msg */ X long m_size; /* number of bytes in msg */ X int m_lines; /* number of lines in msg */ X} msg[MAXMSGS]; X struct options { X char *option; X char *value; X struct options *next; X} *set_options, *aliases, *ignore_hdr, *functions, *fkeys, *own_hdrs; X#ifdef CURSES struct options *bindings; X#endif /* CURSES */ X struct cmd { X char *command; X int (*func)(); X}; extern struct cmd ucb_cmds[]; extern struct cmd cmds[], hidden_cmds[]; X#ifdef SUNTOOL extern struct cmd fkey_cmds[]; X#endif /* SUNTOOL */ X FILE X *tmpf, /* temporary holding place for all mail */ X *open_file(), /* open a file or program for write/append */ X *popen(); /* this should be in stdio.h */ X extern char X *sys_errlist[], /* system's list of global error messages */ X#ifdef SUNTOOL X *font_files[], /* the names of the files fonts are kept in */ X *alt_fonts[], /* fonts to use if first ones don't work */ X#endif /* SUNTOOL */ X **environ; /* user's environment variables */ X extern int errno; /* global system error number */ jmp_buf jmpbuf; /* longjmp to jmpbuf on sigs (not in tool) */ X char X debug, /* debug causes various print statements in code */ X tempfile[40], /* path to filename of temporary file */ X msg_list[MAXMSGS_BITS], /* MAXMSGS bits of boolean storage */ X *cmd_help, /* filename of location for "command -?" commands. */ X *get_name_n_addr(), /* get name and addr from a well-formed address */ X *login, /* login name of user */ X *mailfile, /* path to filename of current mailfile */ X *ourname[MAX_HOST_NAMES], /* the name and aliases of the current host */ X *prompt, /* the prompt string -- may have %d */ X *escape, /* the "tilde escape" when inputting text to letter */ X *hdrs_only, /* true if -H flag was given --set to args */ X *hdr_format, /* set to the header format string; referenced a lot */ X *argv_to_string(), /* convert a vector of strings into one string */ X **make_command(), /* build a command vector (argv) */ X **mk_argv(), /* given a string, make a vector */ X *itoa(), /* return a string representation of a number */ X *lcase_strcpy(), /* just like strcpy, but convert all chars to lower */ X *variable_stuff(), /* return information about variables */ X *no_newln(), /* remove newline and extra whitespace - return end */ X *savestr(), /* strcpy arg into malloc-ed memory; return address */ X *spoolfile, /* MAILDIR/$USER in a string -- this is used a lot */ X *date_to_string(), /* returns a string described by parse_date() */ X *msg_date(), /* return a string of the date of a message */ X *parse_date(), /* parse an ascii date, and return message-id str */ X *Time(), /* returns string expression of time (takes args) */ X *do_range(), /* parse a string converting to a "range" of numbers */ X *getpath(), /* static char returning path (expanding ~, +, %, #) */ X *compose_hdr(), /* returns a formatted line describing passed msg # */ X *my_atoi(), /* do an atoi, but return the last char parsed */ X *do_set(), /* set/unset an option, alias, ignored-hdr */ X *reply_to(), /* who do we reply to when responding */ X *reply_hdr(), /* compose In-Reply-to: hdr according to format */ X *cc_to(), /* when responding, return str which is the cc-list */ X *subject_to(), /* when responding, return str which is the subject */ X *header_field(), /* the line in msg described by arg (message header) */ X *alias_to_address(),/* convert a name[list] to "real" names */ X *set_header(), /* [interactive] proc to set/display to/subject/cc */ X *getenv(), *prog_name; X int X last_msg_cnt, /* when checking for new mail, save the last msg_cnt */ X msg_cnt, /* total number of messages */ X crt, /* min number of lines msg contains to invoke pager */ X current_msg, /* the current message we're dealing with */ X exec_pid, /* pid of a command that has been "exec"ed */ X hist_no, /* command's history number */ X iscurses, /* if we're running curses */ X istool, /* argv[0] == "xxxxtool", ranges from 0 to 2 */ X n_array[128], /* array of message numbers in the header window */ X screen, /* number of headers window can handle */ X X quit(), cleanup(), catch(), do_alias(), respond(), cd(), sh(), stop(), X folder(), save_msg(), delete(), do_mail(), lpr(), alts(), set(), do_hdrs(), X rm_edfile(), pick(), save_opts(), preserve(), sort(), readmsg(), X do_pick(), print_help(), folders(), question_mark(), do_from(), my_stty(), X do_version(), disp_hist(), source(), do_echo(), sigchldcatcher(), ls(), X nopenfiles(), Setenv(), Unsetenv(), Printenv(), bus_n_seg(), msg_flags(), X toggle_debug(), stop_start(), interrupt(); X long X still_more, /* there is still more message to display */ X spool_size, /* size of sppol mail regardless of current folder */ X last_size, /* the lastsize of the mailfile since last check */ X time(); /* satisfy lint */ X void X xfree(), free_vec(), error(), getmail(), mail_status(), X file_to_fp(), init(), display_msg(); X /* printf(), fclose(), fflush(), fputs(), fputc() */ X#ifdef TIOCGLTC struct ltchars ltchars; /* tty character settings */ X#endif /* TIOCGLTC */ X X#ifdef CURSES X X#define STANDOUT(y,x,s) standout(), mvaddstr(y,x,s), standend() X#define redraw() clearok(curscr, TRUE), wrefresh(curscr) X int X curses_init(), /* interpret commands via the curses interface */ X bind_it(); /* bind chars or strings to functions */ X#endif /* CURSES */ X X#ifdef SUNTOOL void X lock_cursors(), unlock_cursors(), scroll_win(), X set_fkeys(), set_key(), toggle_opt(), help_opt(); X char X *rite(), /* rite a char to msg_win: return string if c == '\n' */ X *find_key(), /* pass x,y coords to find which function key assoc. */ X *key_set_to(), /* pass fkey string, return string describing func */ X *panel_get(), /* returns what has been typed in a panel item */ X *tool_help, /* help for tool-related things (sometimes, overlap) */ X blank[128]; /* use to clear to end of line */ X int X time_out, /* time out interval to wait for new mail */ X rootfd, /* the root window's fd */ X parentfd, /* the parent's window fd */ X getting_opts, /* true if getting/setting opts from msg_win */ X curfont, /* the current font to use for mail message window */ X total_fonts, /* total number of fonts available */ X get_hdr_field, /* bitmask of header fields to be gotten */ X X msg_io(), msgwin_handlesigwinch(), hdr_io(), hdrwin_handlesigwinch(), X sigchldcatcher(), sigtermcatcher(), sigwinchcatcher(), do_sort(), X do_compose(), do_edit(), read_mail(), delete_mail(), respond_mail(), X do_hdr(), display_hdrs(), print_sigwinch(), p_set_opts(), X tool_mgmt(), do_help(), text_done(), msg_num_done(), do_lpr(), X toolquit(), change_font(), do_clear(), do_update(), X file_dir(), do_file_dir(), do_send(), abort_mail(), check_new_mail(), X fkey_cmd(), fkey_settings(); X struct tchars tchars; /* more tty character settings */ X struct tool *tool; /* main tool structure */ struct toolsw X *panel_sw, /* main panel subwindow */ X *hdr_sw, /* subwindow for message headers */ X *hdr_panel_sw, /* panel for headers */ X *tty_sw, /* subwindow which forks a shell (usually editor) */ X *print_sw, /* subwindow for print statements */ X *msg_sw; /* main subwindow to display messages and more */ X struct pixwin X *msg_win, /* main pixwin for message display and more */ X *hdr_win, /* pixwin for message headers */ X *print_win; /* pixwin for printing messages ( print() ) */ X struct pr_pos txt; /* current position of text written */ struct rect msg_rect, hdr_rect; /* sizes of the main and hdr rects */ struct pixfont *fonts[MAX_FONTS]; /* array of fonts */ X Panel X main_panel, /* the main panel dealing with generic items */ X hdr_panel; /* panel which contains message header specific items */ X Panel_item X abort_item, /* abort mail in progress */ X alias_item, /* set/view/change current mail aliases */ X cd_item, /* changes file_item to cd (for cd-ing) */ X comp_item, /* compose a letter */ X delete_item, /* delete/undelete messages */ X edit_item, /* edit a message */ X font_item, /* choose which font to use */ X folder_item, /* change folders */ X file_item, /* text item for files or directories (forlder/cd) */ X hdr_display, /* format message headers are displayed */ X help_item, /* choose this to get various help */ X ignore_item, /* set/view/change message headers to be ignored */ X input_item, /* text item to get values for set/unsetting values */ X msg_num_item, /* text item to explicity state which message to read */ X next_scr, /* display the next screenful of message headers */ X option_item, /* set/view/unset mail options */ X prev_scr, /* display the previous screen of messages */ X print_item, /* send current message to the printer */ X quit_item, /* quit tool/close to icon */ X read_item, /* read the current message */ X respond_item, /* respond to messages */ X save_item, /* saves messages; uses text item input_item */ X send_item, /* when composing letter, this will send it off */ X sort_item, /* sort routine... */ X sub_hdr_item[6], /* display items that just sit there and give help */ X update_item; /* commit changes to folder */ X struct itimerval mail_timer; /* frequency to check for new mail */ X X /* mouse symbols and data */ X/* left, middle and right mouse pixrects */ struct cursor X l_cursor, m_cursor, r_cursor, coffee, read_cursor, write_cursor, X main_cursor, checkmark; X struct pixrect *msg_pix; /* pixrect holding text of a message */ extern struct pixrect mouse_left, mouse_middle, mouse_right; extern struct pixrect dn_arrow, up_arrow, cycle, shade_50; X extern struct icon mail_icon; X#endif /* SUNTOOL */ X END_OF_FILE if test 20902 -ne `wc -c <'mush.h'`; then echo shar: \"'mush.h'\" unpacked with wrong size! fi # end of 'mush.h' fi echo shar: End of archive 8 \(of 14\). cp /dev/null ark8isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 14 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.