argv@zipcode.com (Dan Heller) (04/22/91)
Submitted-by: Dan Heller <argv@zipcode.com> Posting-number: Volume 18, Issue 68 Archive-name: mush/part11 Supersedes: mush: Volume 12, Issue 28-47 #!/bin/sh # do not concatenate these parts, unpack them in order with /bin/sh # file loop.c continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 11; then echo Please unpack part "$Scheck" next! exit 1 else exit 0 fi ) < _shar_seq_.tmp || exit 1 if test ! -f _shar_wnt_.tmp; then echo 'x - still skipping loop.c' else echo 'x - continuing file loop.c' sed 's/^X//' << 'SHAR_EOF' >> 'loop.c' && X /* If final is true, do variable expansions first */ X if (final) { X (void) strcpy(buf, str); X str = buf; X if (!variable_expand(str)) X return DUBL_NULL; X } X *argc = 0; X while (*str && *argc < MAXARGS) { X while (isspace(*str)) X ++str; X /* When we have hit an unquoted `;', final must be true, X * so we're finished. Stuff the rest of the string at the X * end of the argv -- do_command will pass it back later, X * for further processing -- and break out of the loop. X * NOTE: *s is not yet valid the first time through this X * loop, so unq_sep should always be initialized to 0. X */ X if (unq_sep && s && *s == ';') { X if (*str) { /* Don't bother saving a null string */ X newargv[*argc] = savestr(str); X (*argc)++; X } X break; X } X if (*str) { /* found beginning of a word */ X unq_sep = 0; /* innocent until proven guilty */ X s = p = str; X do { X if (p - s >= sizeof buf-1) { X print("argument list too long.\n"); X return DUBL_NULL; X } X if (*str == ';' || *str == '|') X unq_sep = final; /* Mark an unquoted separator */ X if ((*p = *str++) == '\\') { X if (final && (*str == ';' || *str == '|')) X --p; /* Back up to overwrite the backslash */ X if (*++p = *str) /* assign and compare to NUL */ X str++; X continue; X } X if (p2 = index("\"'", *p)) { X register char c2 = *p2; X /* you can't escape quotes inside quotes of the same type */ X if (!(p2 = index(str, c2))) { X if (final) X print("Unmatched %c.\n", c2); X err++; X p2 = str; X } X /* This is the intent of the following loop: X * tmp = (int)(p2 - str) + 1; X * (void) strncpy(p + !final, str, tmp); X * The strncpy() can't be used directly because X * it may be overlapping, which fails sometimes. X */ X /* copy up to and including quote */ X for (tmp = 0; tmp < (int)(p2 - str) + 1; tmp++) X p[tmp+!final] = str[tmp]; X p += tmp - 2 * !!final; /* change final to a boolean */ X if (*(str = p2)) X str++; X } X } while (++p, *str && (!isdelimeter(*str) || str[-1] == '\\')); X if (c = *str) /* set c = *str, check for null */ X str++; X *p = 0; X if (*s) { X /* To differentiate real separators from quoted or X * escaped ones, always store 3 chars: X * 1) The separator character X * 2) A nul (string terminator) X * 3) An additional boolean (0 or 1) X * The boolean is checked by do_command. Note that this X * applies only to "solitary" separators, i.e. those not X * part of a larger word. X */ X if (final && (!strcmp(s, ";") || !strcmp(s, "|"))) { X char *sep = savestr("xx"); /* get 3 char slots */ X sep[0] = *s, sep[1] = '\0', sep[2] = unq_sep; X newargv[*argc] = sep; X } else X newargv[*argc] = savestr(s); X (*argc)++; X } X *p = c; X } X } X if (!*argc) X return DUBL_NULL; X /* newargv[*argc] = NULL; */ X if (!(argv = (char **)calloc((unsigned)((*argc)+1), sizeof(char *)))) { X perror("mk_argv: calloc"); X return DUBL_NULL; X } X for (tmp = 0; tmp < *argc; tmp++) X argv[tmp] = newargv[tmp]; X if (err) X *argc = -1; X else if (debug > 3) X (void) printf("Made argv: "), print_argv(argv); X return argv; } X /* X * Report a history parsing error. X * Suppress the message if nonobang is true. X */ #define hist_error if (nonobang) {;} else print X /* X * reference previous history from syntax of str and place result into buf X * We know we've got a history reference -- we're passed the string starting X * the first char AFTER the '!' (which indicates history reference) X */ char * reference_hist(str, buf, hist_ref) register char *str, **hist_ref; char buf[]; { X int relative; /* relative from current hist_no */ X int old_hist, argstart = 0, lastarg, argend = 0, n = 0; X register char *p, *rb = NULL, **argv = hist_ref; X struct history *hist; X X buf[0] = 0; X if (*str == '{') X if (!(rb = index(str, '}'))) { /* { */ X hist_error("Unmatched '}'"); X return NULL; X } else X *rb = 0, ++str; X relative = *str == '-'; X if (index("!:$*", *str)) { X old_hist = hist_no; X if (*str == '!') X str++; X } else if (isdigit(*(str + relative))) X str = my_atoi(str + relative, &old_hist); X else if (!(p = hist_from_str(str, &old_hist))) { X if (rb) /* { */ X *rb = '}'; X return NULL; X } else X str = p; X if (relative) X old_hist = (hist_no - old_hist) + 1; X if (old_hist == hist_no) { X if (!(argv = hist_ref)) X hist_error("You haven't done anything yet!\n"); X } else { X if (old_hist <= hist_no-hist_size || old_hist > hist_no || X old_hist <= 0) { X if (old_hist <= 0) X hist_error("You haven't done that many commands, yet.\n"); X else X hist_error("Event %d %s.\n", old_hist, X (old_hist > hist_no)? "hasn't happened yet": "expired"); X if (rb) /* { */ X *rb = '}'; X return NULL; X } X hist = hist_head; X while (hist && hist->histno != old_hist) X hist = hist->prev; X if (hist) X argv = hist->argv; X } X if (!argv) { X if (rb) /* { */ X *rb = '}'; X return NULL; X } X while (argv[argend+1]) X argend++; X lastarg = argend; X if (*str && index(":$*-", *str)) { X int isrange; X if (*str == ':' && isdigit(*++str)) X str = my_atoi(str, &argstart); X if (isrange = (*str == '-')) X str++; X if (!isdigit(*str)) { X if (*str == 'p') X str++, print_only = 1; X else if (*str == '*') { X str++; X if (!isrange) { X if (argv[0]) { X if (argv[1]) X argstart = 1; X else { X if (rb) /* { */ X *rb = '}'; X return (rb ? rb + 1 : str); X } X } else X argstart = 0; X } X } else if (*str == '$') { X if (!isrange) X argstart = argend; X str++; X } else if (isrange && argend > argstart) X argend--; /* unspecified end of range implies last-1 arg */ X else X argend = argstart; /* no range specified; use arg given */ X } else X str = my_atoi(str, &argend); X } X if (argstart > lastarg || argend > lastarg || argstart > argend) { X hist_error("Bad argument selector.\n"); X if (rb) /* { */ X *rb = '}'; X return (nonobang ? rb ? rb + 1 : str : NULL); X } X if (debug > 3) X print("history expanding from "), print_argv(argv); X while (argstart <= argend) { X n += Strcpy(&buf[n], argv[argstart++]); X buf[n++] = ' '; X } X buf[--n] = 0; X if (rb) /* { */ X *rb = '}'; X return (rb ? rb + 1 : str); } X /* find a history command that contains the string "str" X * place that history number in "hist" and return the end of the string X * parsed: !?foo (find command with "foo" in it) !?foo?bar (same, but add "bar") X * in the second example, return the pointer to "bar" X */ char * hist_from_str(str, hist_number) register char *str; register int *hist_number; { X register char *p = NULL, c = 0; X int full_search = 0, len, found; X char buf[BUFSIZ]; X struct history *hist; #ifndef REGCMP X extern char *re_comp(); #else X char *rex = NULL; X extern char *regcmp(); #endif /* REGCMP */ X X /* For !{something}, the {} are stripped in reference_hist() */ X if (*str == '?') { X if (p = index(++str, '?')) X c = *p, *p = 0; X else X p = str + strlen(str); X full_search = 1; X } else { X p = str; X while (*p && *p != ':' && !isspace(*p)) X p++; X c = *p, *p = 0; X } X if (*str) { #ifndef REGCMP X if (re_comp(str)) #else X if (!(rex = regcmp(str, NULL))) /* Assign and test */ #endif /* REGCMP */ X { X if (c) X *p = c; X return NULL; X } X } else { X *hist_number = hist_no; X if (c) X *p = c; X return (*p == '?' ? p + 1 : p); X } X len = strlen(str); X /* move thru the history in reverse searching for a string match. */ X for (hist = hist_head; hist; hist = hist->prev) { X if (full_search) { X (void) argv_to_string(buf, hist->argv); X Debug("Checking for (%s) in (#%d: %s)\n", str, hist->histno, buf); X } X if (!full_search) { X (void) strcpy(buf, hist->argv[0]); X Debug("Checking for (%s) in (#%d: %*s)\n", X str, hist->histno, len, buf); X found = !strncmp(buf, str, len); X } else X found = #ifndef REGCMP X re_exec(buf) #else X !!regex(rex, buf, NULL) /* convert to boolean value */ #endif /* REGCMP */ X == 1; X if (found) { X *hist_number = hist->histno; X Debug("Found it in history #%d\n", *hist_number); X *p = c; X return (*p == '?' ? p + 1 : p); X } X } X hist_error("%s: event not found\n", str); X *p = c; X return NULL; } X disp_hist(n, argv) /* argc not used -- use space for the variable, "n" */ register int n; char **argv; { X register int list_num = TRUE, num_of_hists = hist_size; X register int reverse = FALSE; X struct history *hist = hist_tail; X X while (*++argv && *argv[0] == '-') { X n = 1; X do switch(argv[0][n]) { X case 'h': list_num = FALSE; X when 'r': reverse = TRUE; hist = hist_head; X otherwise: return help(0, "history", cmd_help); X } X while (argv[0][++n]); X } X X if (!hist) { X print("No history yet.\n"); X return -1; X } X if (*argv) X if (!isdigit(**argv)) { X print("history: badly formed number\n"); X return -1; X } else X num_of_hists = atoi(*argv); X X if (num_of_hists > hist_size || num_of_hists > hist_no) X num_of_hists = min(hist_size, hist_no); X X if (!reverse) X while (hist_no - hist->histno >= num_of_hists) { X Debug("skipping %d\n", hist->histno); X hist = hist->next; X } X X (void) do_pager(NULL, TRUE); X for (n = 0; n < num_of_hists && hist; n++) { X char buf[256]; X if (list_num) X (void) do_pager(sprintf(buf, "%4.d ", hist->histno), FALSE); X (void) argv_to_string(buf, hist->argv); X (void) do_pager(buf, FALSE); X if (do_pager("\n", FALSE) == -1) X break; X hist = (reverse)? hist->prev : hist->next; X } X (void) do_pager(NULL, FALSE); X return 0; } X init_history(newsize) { X if ((hist_size = newsize) < 1) X hist_size = 1; } SHAR_EOF echo 'File loop.c is complete' && chmod 0644 loop.c || echo 'restore of loop.c failed' Wc_c="`wc -c < 'loop.c'`" test 34934 -eq "$Wc_c" || echo 'loop.c: original size 34934, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= macros.c ============== if test -f 'macros.c' -a X"$1" != X"-c"; then echo 'x - skipping macros.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting macros.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'macros.c' && /* (@)# macros.c (c) copyright 9/19/88 (Bart Schaefer, Dan Heller) */ X #include "bindings.h" #include "mush.h" X extern struct cmd_map map_func_names[]; X struct cmd_map *mac_stack, *mac_hide; X /* X * print current binding to macro mappings if "str" is NULL. X * else return the string "x_str" which the str is bound to. X */ char * c_macro(name, str, opts) char *name; register char *str; register struct cmd_map *opts; { X register int incurses = iscurses; X char buf[MAX_MACRO_LEN], buf2[sizeof buf * 3]; X X if (!str) { X for (; opts; opts = opts->m_next) X if (opts->m_cmd == C_MACRO) X break; X if (!opts) { X print("No %s settings.\n", name); X return (char *)(-1); X } X if (incurses) X clr_bot_line(), iscurses = FALSE; X (void) do_pager(NULL, TRUE); X (void) do_pager(sprintf(buf, "\nCurrent %s settings:\n\n",name), FALSE); X } X X if (!opts) X return NULL; X X for (; opts; opts = opts->m_next) { X if (opts->m_cmd != C_MACRO) X continue; X if (!str) { X (void) do_pager(sprintf(buf, "%-20.20s ", X ctrl_strcpy(buf2, opts->m_str, FALSE)), FALSE); X if (do_pager(sprintf(buf, "%s\n", X ctrl_strcpy(buf2, opts->x_str, TRUE)), FALSE) == EOF) X break; X } else { X if (strcmp(str, opts->m_str)) X continue; X else X return opts->x_str; X } X } X iscurses = incurses; X if (str) X (void) do_pager(NULL, FALSE); X return NULL; } X mac_push(str) register char *str; { X register struct cmd_map *tmp; X X /* now make a new macro struct and set fields */ X if (!(tmp = (struct cmd_map *)calloc((unsigned)1,sizeof(struct cmd_map)))) { X error("calloc"); X return -1; X } X tmp->m_next = mac_stack; X mac_stack = tmp; X tmp->x_str = savestr(str); /* x_str is the text of the expansion */ X tmp->m_str = tmp->x_str; /* m_str is the current read position */ X X /* X * Save the current state of the glob_flags so X * mac_push() can also serve as unget of stdin X */ X tmp->m_cmd = glob_flags; X return 0; } X mac_queue(str) register char *str; { X register struct cmd_map **tmp; /* NOTE pointer to pointer! */ X X /* Find the bottom of the macro stack */ X for (tmp = &mac_stack; *tmp; tmp = &((*tmp)->m_next)) X ; X /* now make a new macro struct and set fields */ X if (!(*tmp = X (struct cmd_map *)calloc((unsigned)1,sizeof(struct cmd_map)))) { X error("calloc"); X return -1; X } X (*tmp)->m_next = (struct cmd_map *)0; /* calloc should do this .... */ X (*tmp)->x_str = savestr(str); /* x_str is the text of the expansion */ X (*tmp)->m_str = (*tmp)->x_str; /* m_str is the current read position */ X X /* X * Save the current state of the glob_flags X */ X (*tmp)->m_cmd = glob_flags; X return 0; } X void mac_pop() { X register struct cmd_map *tmp; X X if (mac_stack) { X tmp = mac_stack; X mac_stack = tmp->m_next; X xfree(tmp->x_str); X xfree((char *) tmp); X } X /* X * Restore saved MACRO glob_flags only (see mac_push()) X */ X if (mac_stack) { X if (ison(mac_stack->m_cmd, IN_MACRO)) X turnon(glob_flags, IN_MACRO); X else X turnoff(glob_flags, IN_MACRO); X if (ison(mac_stack->m_cmd, LINE_MACRO)) X turnon(glob_flags, LINE_MACRO); X else X turnoff(glob_flags, LINE_MACRO); X if (ison(mac_stack->m_cmd, QUOTE_MACRO)) X turnon(glob_flags, QUOTE_MACRO); X else X turnoff(glob_flags, QUOTE_MACRO); X } } X /* Abandon macro processing */ void mac_flush() { X while (mac_stack) X mac_pop(); X if (mac_hide) { X mac_stack = mac_hide; X mac_hide = NULL_MAP; X while (mac_stack) X mac_pop(); X } X turnoff(glob_flags, IN_MACRO); X turnoff(glob_flags, LINE_MACRO); X turnoff(glob_flags, QUOTE_MACRO); } X /* Check for pending input from a macro. */ mac_pending() { X register struct cmd_map *msp; X X for (msp = mac_stack; msp && !*(msp->m_str); msp = msp->m_next) X ; X X return !!msp; } X /* Get input and treat it as a macro. */ get_mac_input(newline) int newline; /* 1 if newline to be appended, 0 otherwise */ { X register int len; X char buf[MAX_MACRO_LEN]; X X /* This call cannot be nested */ X if (mac_hide) X return -1; X X /* Hide the mac_stack so input comes from stdin */ X mac_hide = mac_stack; mac_stack = NULL_MAP; X X if ((len = Getstr(buf, MAX_MACRO_LEN - 1, 0)) < 0) X return len; X if (newline) { X buf[len++] = '\n'; X buf[len] = 0; X } X X /* Restore the mac_stack */ X if (mac_stack) { X /* X * Somehow, a push happened even though mac_hide was X * nonzero -- maybe by line wrap? Fix it as best we can. X */ X struct cmd_map *msp; X for (msp = mac_stack; msp->m_next; msp = msp->m_next) X ; X msp->m_next = mac_hide; X } else X mac_stack = mac_hide; X mac_hide = NULL_MAP; X X /* Restore saved flags */ X if (mac_stack) { X if (ison(mac_stack->m_cmd, IN_MACRO)) X turnon(glob_flags, IN_MACRO); X else X turnoff(glob_flags, IN_MACRO); X if (ison(mac_stack->m_cmd, LINE_MACRO)) X turnon(glob_flags, LINE_MACRO); X else X turnoff(glob_flags, LINE_MACRO); X } X if (len > 0) X Ungetstr(buf); X X return 1; } X /* getchar() substitute -- reads from the current macro if one is active, X * otherwise does a getchar(). X * X * NOTE: In the mac_stack, x_str is the saved text of the current macro, X * and m_str is the current read position within the macro. X */ m_getchar() { X int c; X X while (mac_stack && (! *(mac_stack->m_str))) X mac_pop(); X if (mac_stack) { X c = *((mac_stack->m_str)++); X return c; X } else { X turnoff(glob_flags, IN_MACRO); X turnoff(glob_flags, LINE_MACRO); X turnoff(glob_flags, QUOTE_MACRO); X while ((c = getchar()) == 0) /* Ignore NUL chars from stdin */ X ; /* until better solution found */ X return c; X } } X m_ungetc(c) char c; { X if (mac_stack && (mac_stack->m_str > mac_stack->x_str)) X *(--(mac_stack->m_str)) = c; X else X (void) ungetc(c, stdin); } X /* X * Try to read a long command; assumes MAC_LONG_CMD already seen. X * On immediate failure, return 0. X * On failure after reading some input, return less than zero. X * On success, return greater than 0. X * The absolute value of the return is the number of chars placed in buf. X */ read_long_cmd (buf) char *buf; { X register char c, *p = buf; X register int count = 0; X X /* X * Test in_macro() in this loop because the _entire_ X * long command _must_ be in the macro -- if we run X * out of macro in mid-long-command, it is an error. X */ X while (in_macro() && (count < MAX_LONG_CMD - 1) X && ((c = m_getchar()) != MAC_LONG_END)) { X *p++ = c; ++count; X } X *p = '\0'; X if (c != MAC_LONG_END) X return (-count); X return count; } X /* X * Identify and possibly execute a reserved long macro command X * Executes if do_exec is true. Otherwise, just parse. X */ reserved_cmd (buf, do_exec) char *buf; { X int ret = 1; X X if (!strcmp(buf, MAC_GET_STR)) { X if (do_exec) X ret = get_mac_input(0); X } else if (!strcmp(buf, MAC_GET_LINE)) { X if (do_exec) X ret = get_mac_input(1); X } else X ret = 0; X return ret; } X #ifdef CURSES X /* X * Identify (and possibly execute, if reserved) curses mode commands X * that appear in macro strings enclosed by MAC_LONG_CMD and X * MAC_LONG_END. Return the binding of the command. X */ long_mac_cmd (c, do_exec) int c; { X char buf[MAX_LONG_CMD]; X register int count, binding; X int y, x; X X if (c != MAC_LONG_CMD) X return C_ERROR; X X if ((count = read_long_cmd(buf)) <= 0) { X print("Invalid long macro command"); X if (ison(glob_flags, CNTD_CMD)) X putchar('\n'); X if (do_exec) X mac_flush(); X return C_ERROR; X } X X if (do_exec) { X if (ison(glob_flags, CNTD_CMD)) X clr_bot_line(); X getyx(stdscr, y, x); X move(LINES - 1, 0); X } X if (reserved_cmd(buf, do_exec)) { X if (do_exec) { X if (isoff(glob_flags, CNTD_CMD)) X move(y, x); X return getcmd(); X } else X return C_NULL; X } else if (do_exec) X move(y, x); X X /* Can start at C_NULL because of "no-op" command */ X for (count = 0; count <= C_HELP; count++) { X if (!strcmp(buf, map_func_names[count].m_str)) { X binding = (int)map_func_names[count].m_cmd; X break; X } X } X /* Don't allow C_MACRO to be called directly */ X if (count > C_HELP || binding == C_MACRO) { X print("Invalid long macro command"); X if (ison(glob_flags, CNTD_CMD)) X putchar('\n'); X return C_ERROR; X } else X return binding; } X #endif /* CURSES */ X /* X * Check the validity of a macro binding as far as possible X */ check_mac_bindings(buf) char *buf; { X int ok = TRUE; X X while (ok && buf && *buf) { X if (*buf == MAC_LONG_CMD) { X char *i; #ifdef CURSES X int count; #endif /* CURSES */ X X if (ok) X ok = ((i = index(++buf, MAC_LONG_END)) != NULL); X if (i) X *i = '\0'; /* Don't worry, we'll fix it */ X else X return ok; #ifdef CURSES X /* OK to start at C_NULL because of "no-op" command */ X for (count = 0; count <= C_HELP; count++) X if (! strcmp(buf, map_func_names[count].m_str)) X break; X /* Don't allow C_MACRO to be called directly */ X if (count == C_MACRO) X ok = FALSE; X else if (count > C_HELP) #endif /* CURSES */ X if (ok && !(ok = reserved_cmd(buf, FALSE))) X wprint("Warning: unrecognized curses command: \"%s\"\n", buf); X buf = i; X *buf++ = MAC_LONG_END; X } else if (*buf++ == '\\' && *buf) X ++buf; X } X return ok; } SHAR_EOF chmod 0644 macros.c || echo 'restore of macros.c failed' Wc_c="`wc -c < 'macros.c'`" test 9279 -eq "$Wc_c" || echo 'macros.c: original size 9279, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= mail.c ============== if test -f 'mail.c' -a X"$1" != X"-c"; then echo 'x - skipping mail.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting mail.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'mail.c' && /* @(#)mail.c (c) copyright 1986 (Dan Heller) */ X #include "mush.h" X /* X * mail.c -- X * do_mail() external interface to these functions; parses options. X * mail_someone() called from do_mail() to begin composing the message. X * start_file() creates the editing file and reset signal catching. X * add_to_letter() adds the next line to letter --determine ~ escapes. X * finish_up_letter() prompts for Cc:, verifies send, adds signatures. X * send_it() expands aliases, invokes mailer, sends to record file. X * add_headers() adds all headers to a FILE *, reads draft files X * rm_edfile() signals are directed here. remove letter, longjmp X * dead_letter() make a dead.letter if mail failed. X */ X static char Subject[BUFSIZ],To[HDRSIZ],Cc[HDRSIZ],Bcc[HDRSIZ],in_reply_to[256]; static int killme; static u_long flags; static SIGRET (*oldterm)(), (*oldint)(), (*oldquit)(); static int finish_up_letter(), send_it(), start_file(); static long add_headers(); static jmp_buf cntrl_c_buf; static char *Hfile, *edfile; FILE *ed_fp; char *hfile, *mktemp(); X /* argc and argv could be null if coming from tool mode compose */ do_mail(n, argv, list) register int n; /* no need for "argc", so use the space for a variable */ register char **argv, *list; { X char firstchar = (argv)? **argv: 'm'; X char *to = NULL, *cc = NULL, *addcc = NULL, *bcc = NULL, *subj = NULL; X char *route = NULL; X char inc_list[MAXMSGS_BITS], buf[HDRSIZ]; X u_long flgs = 0; X X if (ison(glob_flags, IS_GETTING)) { X wprint("You must finish the letter you are editing first.\n"); X return -1; X } X if (ison(glob_flags, DO_PIPE)) { X wprint("You can't pipe out of the %s command.\n", argv? *argv : "mail"); X return -1; X } X clear_msg_list(inc_list); X hfile = Hfile = NULL; X X /* If piped to mail, include the messages piped */ X if (ison(glob_flags, IS_PIPE) || X (lower(firstchar) == 'r' && do_set(set_options, "autoinclude"))) { X turnon(flgs, INCLUDE); X bitput(list, inc_list, msg_cnt, =); X } X /* Set SIGN and DO_FORTUNE now so we can turn them off later */ X if (do_set(set_options, "autosign")) X turnon(flgs, SIGN); X if (do_set(set_options, "fortune")) X turnon(flgs, DO_FORTUNE); X while (argv && *argv && *++argv && **argv == '-') { X n = 1; X while (n && argv[0][n]) X switch (argv[0][n]) { #ifdef VERBOSE_ARG X case 'v': turnon(flgs, VERBOSE); n++; break; #endif /* VERBOSE_ARG */ X case 'H': X if (argv[1]) { X n = 0; X Hfile = *++argv; X } else { X wprint("Must specify a file\n"); X return -1; X } X when 'h': X if (argv[1]) { X n = -1; /* it gets incremented below */ X hfile = savestr(*++argv); X if (ison(glob_flags, REDIRECT)) { X turnoff(glob_flags, REDIRECT); X turnon(flgs, SEND_NOW); X } X } else { X wprint("Must specify a file containing headers\n"); X return -1; X } X /* Fall through */ X case 'E': turnon(flgs, EDIT_HDRS); n++; X when 'e': turnon(flgs, EDIT); n++; X when 'F': turnon(flgs, DO_FORTUNE); n++; X when 'b': X if (argv[1]) { X n = 0, bcc = *++argv; X fix_up_addr(bcc); X } else { X wprint("Must specify blind-carbon list\n"); X return -1; X } X when 'c': X if (argv[1]) { X n = 0, addcc = *++argv; X fix_up_addr(addcc); X } else { X wprint("Must specify carbon-copy list\n"); X return -1; X } X when 's': X if (argv[1]) X n = 0, subj = *++argv; X else X n++, turnon(flgs, NEW_SUBJECT); X when 'i': case 'I': case 'f': { X int m; X if (!msg_cnt) { X wprint("No message to include!\n"); X return -1; X } X if (argv[0][n] == 'i') { X turnon(flgs, INCLUDE); X turnoff(flgs, INCLUDE_H); X turnoff(flgs, FORWARD); X } else if (argv[0][n] == 'I') { X turnon(flgs, INCLUDE_H); X turnoff(flgs, INCLUDE); X turnoff(flgs, FORWARD); X } else if (argv[0][n] == 'f') { X turnon(flgs, FORWARD); X turnon(flgs, SEND_NOW); X turnoff(flgs, INCLUDE_H); X turnoff(flgs, INCLUDE); X } X /* "-i 3-5" or "-i3-5" Consider the latter case first */ X if (!argv[0][++n]) X argv++, n = 0; X (*argv) += n; X m = get_msg_list(argv, inc_list); X (*argv) -= n; X if (m == -1) X return -1; X /* if there were args, then go back to the first char X * in the next argv X */ X if (m) X n = 0; X if (!n) /* n may be 0 from above! */ X argv += (m-1); X } X when 'U': X turnon(flgs, SEND_NOW); X n++; X when 'u': X turnoff(flgs, SIGN); X turnoff(flgs, DO_FORTUNE); X n++; X when 'r': X if (lower(firstchar) == 'r') { X route = *++argv; X n = 0; X break; X } X /* fall thru */ X default: X if (argv[0][n] != '?') { X wprint("%c: unknown option\n\n", argv[0][n]); X return -1; X } else X return help(0, "mail", cmd_help); X } X } X if (isoff(flgs, FORWARD)) { X if (ison(flgs, SEND_NOW)) { X if (!hfile && !Hfile) { X wprint("Can't send immediately without draft file.\n"); X return -1; X } X turnoff(flgs, EDIT); /* -U overrides -e */ X } else if (do_set(set_options, "autoedit")) X turnon(flgs, EDIT); X } else if (ison(flgs, EDIT)) /* -e modifies -f */ X turnoff(flgs, SEND_NOW); #ifdef VERBOSE_ARG X if (do_set(set_options, "verbose")) X turnon(flgs, VERBOSE); #endif /* VERBOSE_ARG */ X *in_reply_to = *To = *Subject = *Cc = *Bcc = 0; X if (lower(firstchar) == 'r') { X char *in_reply_fmt, *pcc = NULL; X to = To, cc = Cc; X /* X * Generate a reply to all the messages passed to respond(). This X * list is different than the include-msg list above. Get info about X * whom the messages were sent to for reply-all. X * BUG: currently, redundant addresses aren't pruned from Bcc list! X */ X for (n = 0; n < msg_cnt; n++) X if (msg_bit(list, n)) { X if (to != To) X *to++ = ',', *to++ = ' '; X (void) reply_to(n, (firstchar == 'R'), buf); X if (strlen(buf) + (to - To) > sizeof(To) - 1) { X wprint("# recipients exceeded at msg %d\n", n); X break; X } X to += Strcpy(to, buf); X if (firstchar == 'R') { X if (pcc = cc_to(n, buf)) { X /* if there was a previous cc, append ", " */ X if (cc != Cc) X *cc++ = ',', *cc++ = ' '; X if (strlen(pcc) + (cc - Cc) > sizeof(Cc) - 1) X wprint("# Cc's exceeded at msg %d\n", n); X else X cc += Strcpy(cc, pcc); X } X } X /* remove redundant addresses now, or headers could get too X * long before the list runs out (it still might) X */ X rm_redundant_addrs(To, Cc); X to = To + strlen(To); X cc = Cc + strlen(Cc); X } X /* clean up end of Cc line for replyall's */ X while (*cc == ' ' || *cc == ',') X *cc-- = '\0'; X if (firstchar == 'R' && !do_set(set_options, "metoo")) { X /* Each reply_to() call above will leave at least X * one person in To. If that one person was us, X * we need to get removed from the complete list. X */ X (void) take_me_off(to); X } X to = To, cc = Cc; X if (route || (route = do_set(set_options, "auto_route"))) X /* careful! This routine could add lots-o-bytes and lose addresses X * to avoid writing out of segment. X */ X route_addresses(To, Cc, route); X if (in_reply_fmt = do_set(set_options, "in_reply_to")) X /* "9" here is a magic # --see compose_hdr() */ X (void) strcpy(in_reply_to, X format_hdr(current_msg, in_reply_fmt, FALSE)+9); X } X if (ison(flgs, FORWARD) && ison(flgs, EDIT) || X lower(firstchar) == 'r' && isoff(flgs, NEW_SUBJECT)) { X turnoff(flgs, NEW_SUBJECT); X if (subj && *subj && (isoff(flgs, FORWARD) || ison(flgs, EDIT))) X subj = strcpy(Subject, subj); X else if (subj = subject_to(current_msg, buf)) X subj = strcpy(Subject, buf + 4*(lower(firstchar) != 'r')); X } else if (isoff(flgs, NEW_SUBJECT) && isoff(flgs, FORWARD) && X (do_set(set_options, "ask") || do_set(set_options, "asksub"))) X turnon(flgs, NEW_SUBJECT); X if (argv && *argv) { X char buf[HDRSIZ]; X (void) argv_to_string(buf, argv); X fix_up_addr(buf); X to = &To[strlen(To)]; X if (*To) X *to++ = ',', *to++ = ' '; X (void) strcpy(to, buf); X to = To; X } X if (addcc && *addcc) { X cc = &Cc[strlen(Cc)]; X if (*Cc) X *cc++ = ',', *cc++ = ' '; X (void) strcpy(cc, addcc); /* addcc has already been fixed up */ X cc = Cc; X } X /* remove any redundant addresses that just got added */ X rm_redundant_addrs(To, Cc); X if (bcc && *bcc) X (void) strncpy(Bcc, bcc, sizeof(Bcc)); /* bcc already fixed up */ X bcc = Bcc; X X return mail_someone(to, subj, cc, bcc, flgs, inc_list); } X static mail_someone(to, subject, cc, bcc, flgs, list) register char *to, *subject, *cc, *bcc, *list; u_long flgs; { X register char *p; X X flags = flgs; X if (to && *to) { X if (!*To) X (void) strncpy(To, to, sizeof(To)); X } else X to = NO_STRING; X if (subject && *subject) { X if (!*Subject) X (void) strncpy(Subject, subject, sizeof(Subject)); X } else X subject = NO_STRING; X if (cc && *cc) { X if (!*Cc) X (void) strncpy(Cc, cc, sizeof(Cc)); X } else X Cc[0] = '\0'; X if (bcc && *bcc) { X if (!*Bcc) X (void) strncpy(Bcc, bcc, sizeof(Bcc)); X } else X Bcc[0] = '\0'; X X if (ison(glob_flags, REDIRECT)) { X /* X * NOTE: Could change this to finish_up_letter() to allow X * signatures to be appended. The -U! option to mush would X * be extended to suppress signing when redirection is on. X */ X int sent = send_it(); X if (sent == -1) { X wprint("Message not sent!\n"); X rm_edfile(-1); X } X return sent; X } X /* if (!*to) then prompting will be done */ X if (!istool && !hfile) { X if (p = set_header("To: ", to, !*to)) { X if (!*to) /* if user typed To-line here, fix up the addresses */ X fix_up_addr(p); X (void) strcpy(To, p); X } X if (!*To && ison(flags, FORWARD) && ison(flags, SEND_NOW)) { X turnoff(flags, SEND_NOW); /* user must edit To: line or do again */ X print("(You must add a To: address.)\n"); X } X /* don't prompt for subject if forwarding mail */ X if (isoff(flags, FORWARD) && (*subject || ison(flags, NEW_SUBJECT)) && X (p = set_header("Subject: ", subject, X !*subject && ison(flags, NEW_SUBJECT)))) X (void) strcpy(Subject, p); X if (*Cc || ison(flags, EDIT_HDRS) && do_set(set_options, "askcc")) { X if ((p = set_header("Cc: ", cc, !*Cc)) && *p) { X fix_up_addr(p); X (void) strcpy(Cc, p); X } X } X if (*Bcc) X print("Bcc: %s\n", Bcc); X putchar('\n'); X } X X /* If forwarding w/o editing, start a new file for each. */ X if (ison(flags, FORWARD) && ison(flags, SEND_NOW)) { X char fwd[MAXMSGS_BITS]; X register int i; X clear_msg_list(fwd); X for (i = 0; i < msg_cnt; i++) X if (msg_bit(list, i)) { X set_msg_bit(fwd, i); X if (start_file(fwd) < 0) X return -1; X turnon(msg[i].m_flags, FORWARD); X if (isoff(glob_flags, READ_ONLY)) X turnon(glob_flags, DO_UPDATE); X clear_msg_list(fwd); X } X } else X return start_file(list); X return 0; } X static start_file(list) char *list; { X register char *dir; X register int i; X char line[MAXPATHLEN]; X int had_hfile = FALSE; X X /* getdir() uses the home directory if no tmpdir */ X if (!(dir = getdir(do_set(set_options, "tmpdir")))) alted: X dir = ALTERNATE_HOME; X (void) mktemp(sprintf(line, "%s/%s", dir, EDFILE)); X strdup(edfile, line); X if (!(ed_fp = mask_fopen(edfile, "w+"))) { X if (strcmp(dir, ALTERNATE_HOME)) X goto alted; X error("can't create %s", edfile); X return -1; X } X if (!istool) { X oldint = signal(SIGINT, rm_edfile); X oldquit = signal(SIGQUIT, rm_edfile); X oldterm = signal(SIGTERM, rm_edfile); X } X X if (istool && isoff(flags, SEND_NOW) || X (isoff(flags, FORWARD) || isoff(flags, SEND_NOW)) && X (ison(flags, EDIT_HDRS) || do_set(set_options, "edit_hdrs"))) { X turnon(flags, EDIT_HDRS); X if (hfile) X had_hfile = TRUE; X if (add_headers(NULL_FILE, &ed_fp, 1, flags) == (long) -1) X return -1; X } X if (Hfile) { X (void) file_to_fp(Hfile, ed_fp, "r"); X Hfile = NULL; X had_hfile = TRUE; X } X if (istool && isoff(flags, SEND_NOW)) X strdup(hfile, edfile); X X /* if flags call for it, include current message (with header?) */ X if (ison(flags, INCLUDE|FORWARD|INCLUDE_H)) { X long copy_flgs = 0, is_forw = ison(flags, FORWARD); X char buf[sizeof(To)]; X if (!is_forw) { X turnon(copy_flgs, INDENT); X if (ison(flags, INCLUDE_H) && X !chk_option("alwaysignore", "include")) X turnon(copy_flgs, NO_IGNORE); X else if (ison(flags, INCLUDE)) X turnon(copy_flgs, NO_HEADER); X } else if (ison(flags, SEND_NOW) || X !chk_option("alwaysignore", "forward")) X turnon(copy_flgs, FORWARD); /* FORWARD implies NO_IGNORE */ #ifdef MSG_SEPARATOR X turnon(copy_flgs, NO_SEPARATOR); #endif /* MSG_SEPARATOR */ X for (i = 0; i < msg_cnt; i++) X if (msg_bit(list, i)) { X if (is_forw && isoff(flags, SEND_NOW)) { X (void) reply_to(i, FALSE, buf); X (void) fprintf(ed_fp,"--- Forwarded mail from %s\n\n",buf); X } X wprint("%sing message %d ...", X is_forw? "forward" : "includ", i+1); X wprint("(%d lines)\n", X copy_msg(i, ed_fp, (u_long) copy_flgs, NULL)); X set_isread(i); /* if we included it, we read it, right? */ X if (is_forw && isoff(flags, SEND_NOW)) X (void) fprintf(ed_fp, X "\n--- End of forwarded message from %s\n", buf); X if (!is_forw || isoff(flags, SEND_NOW)) X (void) fputc('\n', ed_fp); X } X (void) fflush(ed_fp); X } X if (!istool && ison(glob_flags, WARNING)) { X if (escape && strncmp(escape, DEF_ESCAPE, 1)) X print("(escape character is set to `%c')\n", *escape); X if (wrapcolumn && wrapcolumn < 20) X print("(warning: wrapping only %d columns from the left!)\n", X wrapcolumn); X } X X /* do an "if" again in case editor not found and EDIT turned off */ X if (!istool && ison(flags, EDIT)) { X char **argv, *edit; X int argc; X if ((!(edit = do_set(set_options, "visual")) || !*edit) && X (!(edit = do_set(set_options, "editor")) || !*edit)) X edit = DEF_EDITOR; X (void) sprintf(line, "%s %s", edit, edfile); X if ((argv = mk_argv(line, &argc, FALSE)) && argc > 0) { X print("Starting \"%s\"...\n", argv[0]); X (void) fclose(ed_fp); X ed_fp = NULL_FILE; X execute(argv); X free_vec(argv); X turnoff(flags, EDIT); X turnoff(flags, FORWARD); /* forwarded messages must be unedited */ X /* upon exit of editor, user must now type eofc or "." to send */ X if (!(ed_fp = fopen(edfile, "r+"))) { X error("can't reopen %s", edfile); X return -1; X } X (void) fseek(ed_fp, 0L, 2); X } else X print("Unable to start \"%s\"\n", edit); X wprint("(continue editing letter or ^%c to send)\n", eofc + '@'); X } else if (ison(flags, SEND_NOW)) { X /* if finish_up_letter() was successful, file was successfully sent. */ X if (!setjmp(cntrl_c_buf) && finish_up_letter() == 0) { X rm_edfile(0); X return 0; X } X } else if (had_hfile) { X /* it's not obvious what's going on -- enlighten user */ X wprint("(continue editing or ^%c to send)\n", eofc + '@'); X } X #ifdef SUNTOOL X if (istool) { X /* toolmode doesn't care if SEND_NOW -- user continues to edit file. X * if SEND_NOW is not set, then the editor file has just been started, X * so again, just return so user can edit file. X */ X if (ed_fp) X fclose(ed_fp), ed_fp = NULL_FILE; X turnon(glob_flags, IS_GETTING); X return 0; X } #endif /* SUNTOOL */ X if (ison(flags, SEND_NOW)) { X /* editing couldn't have been on -- finish_up_letter() failed */ X rm_edfile(0 - ison(flags, FORWARD)); X return -1; X } X X i = 0; X turnon(glob_flags, IS_GETTING); X do { X /* If the user hits ^C in cbreak mode, mush will return to X * Getstr and not clear the buffer. whatever is typed next will X * be appended to the line. jumping here will force the line to X * be cleared cuz it's a new call. X */ X (void) setjmp(cntrl_c_buf); X while (Getstr(line, sizeof(line), 0) > -1) { X if (!istool) /* toolmode checks on a timer -- don't do it here */ X (void) check_new_mail(); /* if new mail comes in, get it */ X if ((i = add_to_letter(line)) <= 0) X break; X } X } while (i >= 0 && finish_up_letter() == -1); X turnoff(glob_flags, IS_GETTING); X return i; /* return -1 if ~x or ~q to terminate letter */ } X char *tilde_commands[] = { X "commands: [OPTIONAL argument]", X "t [list]\tChange list of recipients", X "s [subject]\tModify [set] subject header", X "c [cc list]\tModify [set] carbon copy recipients", X "b [bcc list]\tModify [set] blind carbon recipients", X "h\t\tModify all message headers", X "e [editor]\tEnter editor. Editor used: \"set editor\", env EDITOR, vi", X "v [editor]\tEnter visual editor. \"set visual\", env VISUAL, vi", X "u\t\tEdit previous (last) line in file.", X "p [pager]\tPage message; pager used: \"set pager\", env. PAGER, more", X "i [msg#'s]\tInclude current msg body [msg#'s] indented by \"indent_str\"", X "I [msg#'s]\tSame, but include the message headers from included messages", X "f [msg#'s]\tForward mail. Not indented, but marked as \"forwarded mail\"", X "S[!]\t\tInclude Signature file [suppress file]", X "F[!]\t\tAdd a fortune at end of letter [don't add]", X "w file\t\tWrite msg buffer to file name", X "a file\t\tAppend msg buffer to file name", X "r file\t\tRead filename into message buffer", X "q \t\tQuit message; save in dead.letter (unless \"nosave\" is set).", X "x \t\tQuit message; don't save in dead.letter.", X "$variable\tInsert the string value for \"variable\" into message.", X ":cmd\t\tRun the mail command \"cmd\".", X "|cmd\t\tPipe the message through the unix command \"cmd\".", X "E[!]\t\tClear contents of letter after saving to dead.letter [unless !].", X 0 }; X /* X * TC_EDIT(tc) returns TRUE if tilde_command[tc] involves message X * editing operations. Used when EDIT_HDRS is active. X */ #define TC_EDIT(tc) ((tc) && ((tc) < 6 || !tilde_commands[(tc)+1])) X /* X * Add the line (char *) parameter to the letter. Determine tilde X * escapes and determine what to do. This function returns 0 to X * indicate user wants to end the letter, -1 if the letter cannot X * be sent (~q, ~x no buffer after editor, etc...) or 1 to indicate X * successful addition of the line to the letter. X * This function may be called by toolmode just to change certain mush X * internal variables via tilde escapes. Thus, ed_fp might be null. X */ add_to_letter(line) char line[]; { X register char *p; X char buf[HDRSIZ > MAXPATHLEN ? HDRSIZ : MAXPATHLEN]; X X killme = 0; X if (ed_fp) /* may be null if istool */ X (void) fseek(ed_fp, 0L, 2); X X if (!strcmp(line, ".") && do_set(set_options, "dot")) X return 0; X if (line[0] != *escape || ison(glob_flags, QUOTE_MACRO)) { X (void) fputs(line, ed_fp); X (void) fputc('\n', ed_fp); X (void) fflush(ed_fp); X return 1; X } X /* all commands are "~c" (where 'c' is the command). set p = first X * character after 'c' and skip whitespace X */ X p = &line[2]; X skipspaces(0); X switch (line[1]) { X case 'v' : case 'p': case 'e' : case '|' : { X if (!*p || *p == 'i') X switch (line[1]) { X case 'p' : X if (!*p && !(p = do_set(set_options, "pager"))) X p = DEF_PAGER; X if (!*p || !strcmp(p, "internal")) X p = NULL; X when 'v' : X if (*p && p[1] || (p = do_set(set_options, "visual"))) X break; X /* else fall through */ X default : X if (!(p = do_set(set_options, "editor")) || !*p) X p = DEF_EDITOR; X when '|' : X print("No command for pipe\n"); X return 1; X } X if (line[1] == 'p' || line[1] == '|') X rewind(ed_fp); X if (line[1] == 'p') { X (void) do_pager(p, TRUE); /* start the pager "p" */ X if (isoff(flags, EDIT_HDRS)) { X (void) do_pager(sprintf(buf, "To: %s\n", To), FALSE); X if (Subject[0]) X (void) do_pager(sprintf(buf, "Subject: %s\n", Subject), X FALSE); X if (Cc[0]) X (void) do_pager(sprintf(buf, "Cc: %s\n", Cc), FALSE); X if (Bcc[0]) X (void) do_pager(sprintf(buf, "Bcc: %s\n", Bcc), FALSE); X (void) do_pager(strcpy(buf, X "--------\nMessage contains:\n"), X FALSE); X } X while (fgets(buf, sizeof(buf), ed_fp)) X if (do_pager(buf, FALSE) == EOF) X break; X (void) do_pager(NULL, FALSE); /* end pager */ X } else if (line[1] == '|') { X FILE *pipe_fp; X (void) sprintf(buf, "( %s ) > %s", p, edfile); X if (unlink(edfile) < 0) { X error("Can't unlink %s:", edfile); X break; /* Drop out of switch */ X } X if ((pipe_fp = popen(buf, "w")) == NULL_FILE) { X error("Can't run \"%s\":", p); X (void) file_to_fp(edfile, ed_fp, "w"); X } else { X while (fgets(buf, sizeof(buf), ed_fp)) X if (fputs(buf, pipe_fp) == EOF) { X print("Broken pipe\n"); X break; X } X (void) pclose(pipe_fp); X } X pipe_fp = ed_fp; /* save ed_fp until we can reopen it */ X if (!(ed_fp = fopen(edfile, "r+"))) { X error("can't reopen %s", edfile); X (void) rewind(pipe_fp); X if (file_to_fp(edfile, pipe_fp, "w") < 0 || X !(ed_fp = fopen(edfile, "r+"))) { X error("can't restore old contents of %s", edfile); X ed_fp = pipe_fp; X dead_letter(0); X return -1; X } X } X (void) fclose(pipe_fp); X } else { X char **argv; X int argc; X (void) sprintf(buf, "%s %s", p, edfile); X if ((argv = mk_argv(buf, &argc, FALSE)) && argc > 0) { X (void) fclose(ed_fp); X ed_fp = NULL_FILE; X execute(argv); X free_vec(argv); X /* tool will return even tho editor isn't done */ X if (!(ed_fp = fopen(edfile, "r+"))) { X error("can't reopen %s", edfile); X return -1; X } X } else X print("Unable to start \"%s\"\n", p); X } X } X when '$': { X register char *p2; X if (!(p2 = do_set(set_options, p))) X print("(%s isn't set)\n", p); X else X putstring(p2, ed_fp); X } X when ':': { X char new[MAXMSGS_BITS]; X u_long save_flags = glob_flags; X X turnon(glob_flags, IGN_SIGS); X turnoff(glob_flags, DO_PIPE); X turnoff(glob_flags, IS_PIPE); X (void) cmd_line(p, new); X glob_flags = save_flags; X } X when 'i': case 'f': case 'I': case 'm': { X int n; X u_long copy_flgs = 0; X char list[MAXMSGS_BITS]; X X if (!msg_cnt) { X wprint("No messages.\n"); X break; X } X clear_msg_list(list); X if (line[1] != 'f') { X turnon(copy_flgs, INDENT); X if (line[1] == 'i') X turnon(copy_flgs, NO_HEADER); X else if (!chk_option("alwaysignore", "include")) X turnon(copy_flgs, NO_IGNORE); X } else if (!chk_option("alwaysignore", "forward")) X turnon(copy_flgs, NO_IGNORE); #ifdef MSG_SEPARATOR X turnon(copy_flgs, NO_SEPARATOR); #endif /* MSG_SEPARATOR */ X if (!*p) X set_msg_bit(list, current_msg); X else if (!do_range(p, list)) X return 1; X for (n = 0; n < msg_cnt; n++) X if (msg_bit(list, n)) { X if (line[1] == 'f') { X (void) reply_to(n, FALSE, buf); X (void) fprintf(ed_fp, X "--- Forwarded mail from %s\n\n", buf); X } X wprint("Including message %d ... ", n+1); X wprint("(%d lines)\n", copy_msg(n, ed_fp, copy_flgs, NULL)); X set_isread(n); X if (line[1] == 'f') X (void) fprintf(ed_fp, X "\n--- End of forwarded message from %s\n\n", buf); X else X (void) fputc('\n', ed_fp); X } X } X /* To: Cc: and Bcc: headers */ X when 'b': X case 't': X case 'c': { X char *h = (line[1] == 't')? To : (line[1] == 'c')? Cc : Bcc; X char *Prompt = line[1] == 't'? "To: " : X line[1] == 'c'? "Cc: " : "Bcc: "; X if (ison(flags, EDIT_HDRS)) { X print("You must use an editor to change your headers.\n"); X break; X } X X if (*p) { X fix_up_addr(p); X if (*h) X (void) sprintf(h+strlen(h), ", %s", p); X else X (void) strcpy(h, p); X } else if (!(p = set_header(Prompt, h, TRUE)) || !*p) X *h = 0; X else { X fix_up_addr(p); X (void) strcpy(h, p); X } X } X when 's': X if (ison(flags, EDIT_HDRS)) { X print("You must use an editor to change your headers.\n"); X break; X } X if (*p || (p = set_header("Subject: ", Subject, 1))) X if (!*p) X Subject[0] = 0; X else X (void) strcpy(Subject, p); X when 'h': X if (ison(flags, EDIT_HDRS)) { X print("You must use an editor to change your headers.\n"); X break; X } X while ((p = set_header("To: ", To, 1)) && !*p) X print("(There must be a recipient.)\n"); X (void) strcpy(To, p); X if (p = set_header("Subject: ", Subject, 1)) X if (!*p) X Subject[0] = 0; X else X (void) strcpy(Subject, p); X if (p = set_header("Cc: ", Cc, 1)) X if (!*p) X Cc[0] = 0; X else { X fix_up_addr(p); X (void) strcpy(Cc, p); X } X if (p = set_header("Bcc: ", Bcc, 1)) X if (!*p) X Bcc[0] = 0; X else { X fix_up_addr(p); X (void) strcpy(Bcc, p); X } X when 'F': case 'S' : { X if (*p == '!') X turnoff(flags, line[1] == 'F'? DO_FORTUNE : SIGN); X else X turnon(flags, line[1] == 'F'? DO_FORTUNE : SIGN); X wprint("%sadding %s at end of message.\n", *p == '!'? "not " : "", X line[1] == 'F'? "fortune" : "signature"); X } X when 'w': case 'a': case 'r': X if (!*p) { X print("(you must specify a filename)\n"); X return 1; X } X (void) fseek(ed_fp, 0L, 2); /* append */ X (void) file_to_fp(p, ed_fp, (line[1] == 'r')? "r": X (line[1] == 'w')? "w": "a"); X /* go up one line in the message file and allow the user to edit it */ X when 'u': { X long newpos, pos = ftell(ed_fp); X char oldline[256]; X if (pos <= 0L) { /* pos could be -1 if ftell() failed */ X print("(No previous line in file.)\n"); X return 1; X } X /* get the last 256 bytes written and read backwards from the X * current place until '\n' is found. Start by moving past the X * first \n which is at the end of the line we want to edit X */ X newpos = max(0, pos - 256L); X (void) fseek(ed_fp, newpos, L_SET); X /* don't fgets -- it'll stop at a \n */ X (void) fread(line, sizeof(char), (int)(pos-newpos), ed_fp); X pos--; X /* the last char in line should be a \n cuz it was last input */ X if (line[(int)(pos-newpos)] != '\n') X print("I don't know how, but your last line ended with %c.\n", X line[(int)(pos-newpos)]); X else X line[(int)(pos-newpos)] = 0; /* null terminate \n for ^H-ing */ X for (pos--; pos > newpos && line[(int)(pos-newpos)] != '\n'; pos--) X ; X /* we've gone back to the end of the second previous line. Check X * to see if the char we're pointing to is a \n. It should be, but X * if it's not, we moved back to the first line of the file. X */ X if (line[(int)(pos-newpos)] == '\n') X ++pos; X /* save the old line that's there in case the user boo-boos */ X (void) strcpy(oldline, &line[(int)(pos-newpos)]); X /* let set header print out the line and get the input */ X if (!(p = set_header("", &line[(int)(pos-newpos)], TRUE))) { X print("Something bad happened and I don't know what it is.\n"); X p = oldline; X } else if (*p == *escape) X print("(Warning: %c escapes ignored on %cu lines.)\n", X *escape, *escape); X /* seek to to the position where the new line will go */ X (void) fseek(ed_fp, pos, L_SET); X /* put the newly typed line */ X (void) fputs(p, ed_fp); /* don't add \n. padding may be necessary */ X /* if the new line is less than the old line, we're going to do X * one of two things. The best thing to do is to truncate the X * file to the end of the new line. Sys-v can't do that, so we X * pad the line with blanks. May be messy in some cases, but... X */ X if ((pos = strlen(p) - strlen(oldline)) < 0) { #ifndef SYSV X /* add the \n, flush the file, truncate to the current pos */ X (void) fputc('\n', ed_fp); X (void) fflush(ed_fp); X (void) ftruncate(fileno(ed_fp), (off_t) ftell(ed_fp)); #else /* SYSV */ X /* pad with blanks to the length of the old line. add \n */ X while (pos++ < 0) X (void) fputc(' ', ed_fp); X (void) fputc('\n', ed_fp), (void) fflush(ed_fp); #endif /* SYSV */ X } else { X /* the new line is >= the old line, add \n -- no trunc req. */ X (void) fputc('\n', ed_fp); X (void) fflush(ed_fp); X } X return 1; X } X /* break; not here cuz of "return" (lint). */ X case 'E': X if (ison(flags, EDIT_HDRS)) { X print("You must use an editor to empty the message buffer.\n"); X break; X } X if (*p != '!' && !do_set(set_options, "nosave")) X dead_letter(0); X if (emptyfile(&ed_fp, edfile) == -1) { X error(edfile); X return -1; X } else X print("Message buffer empty\n"); X when 'q': X /* save in dead.letter if nosave not set -- rm_edfile(-2). */ X rm_edfile(-2); /* doesn't return out of tool mode */ X return -1; X /* break; not stated cuz of "return" (lint) */ SHAR_EOF true || echo 'restore of mail.c failed' fi echo 'End of part 11' echo 'File mail.c is continued in part 12' echo 12 > _shar_seq_.tmp exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.