From 84ab267e226407ee402eff10aaf8239ed684de77 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Sat, 23 Apr 2005 01:50:55 +0000 Subject: [PATCH] patch for a very alpha busybox ed --- patches/ed.patch | 3443 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3443 insertions(+) create mode 100644 patches/ed.patch diff --git a/patches/ed.patch b/patches/ed.patch new file mode 100644 index 000000000..fc261b88d --- /dev/null +++ b/patches/ed.patch @@ -0,0 +1,3443 @@ +Index: editors/Makefile.in +=================================================================== +--- editors/Makefile.in (revision 10144) ++++ editors/Makefile.in (working copy) +@@ -24,8 +24,9 @@ + srcdir=$(top_srcdir)/editors + + EDITOR-y:= +-EDITOR-$(CONFIG_AWK) += awk.o +-EDITOR-$(CONFIG_PATCH) += patch.o ++EDITOR-$(CONFIG_AWK) += awk.o ++EDITOR-$(CONFIG_ED) += ed.o ++EDITOR-$(CONFIG_PATCH) += patch.o + EDITOR-$(CONFIG_SED) += sed.o + EDITOR-$(CONFIG_VI) += vi.o + EDITOR_SRC:= $(EDITOR-y) +Index: editors/Config.in +=================================================================== +--- editors/Config.in (revision 10144) ++++ editors/Config.in (working copy) +@@ -20,6 +20,12 @@ + Enable math functions of the Awk programming language. + NOTE: This will require libm to be present for linking. + ++config CONFIG_ED ++ bool "ed" ++ default n ++ help ++ ed ++ + config CONFIG_PATCH + bool "patch" + default n +Index: include/usage.h +=================================================================== +--- include/usage.h (revision 10151) ++++ include/usage.h (working copy) +@@ -556,6 +561,9 @@ + "$ echo \"Erik\\nis\\ncool\"\n" \ + "Erik\\nis\\ncool\n") + ++#define ed_trivial_usage "" ++#define ed_full_usage "" ++ + #define env_trivial_usage \ + "[-iu] [-] [name=value]... [command]" + #define env_full_usage \ +Index: include/applets.h +=================================================================== +--- include/applets.h (revision 10151) ++++ include/applets.h (working copy) +@@ -179,6 +179,9 @@ + #ifdef CONFIG_ECHO + APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER) + #endif ++#ifdef CONFIG_ED ++ APPLET(ed, ed_main, _BB_DIR_BIN, _BB_SUID_NEVER) ++#endif + #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS) + APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER) + #endif +--- /dev/null 2005-04-22 11:15:01.120978184 -0400 ++++ editors/ed.c 2005-04-22 11:16:00.000000000 -0400 +@@ -0,0 +1,3120 @@ ++/* main.c: This file contains the main control and user-interface routines ++ for the ed line editor. */ ++/* ed line editor. ++ Copyright (C) 1993, 1994 Andrew Moore, Talke Studio ++ All Rights Reserved ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2, or (at your option) ++ any later version. ++ ++ This program is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++/* ++ * CREDITS ++ * ++ * This program is based on the editor algorithm described in ++ * Brian W. Kernighan and P. J. Plauger's book "Software Tools ++ * in Pascal," Addison-Wesley, 1981. ++ * ++ * The buffering algorithm is attributed to Rodney Ruddock of ++ * the University of Guelph, Guelph, Ontario. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "busybox.h" ++#include "ed.h" ++#include "getopt.h" ++ ++jmp_buf env; ++ ++/* static buffers */ ++char *errmsg; /* error message buffer */ ++char stdinbuf[1]; /* stdin buffer */ ++char *shcmd; /* shell command buffer */ ++int shcmdsz; /* shell command buffer size */ ++int shcmdi; /* shell command buffer index */ ++char *ibuf; /* ed command-line buffer */ ++int ibufsz; /* ed command-line buffer size */ ++char *ibufp; /* pointer to ed command-line buffer */ ++ ++/* global flags */ ++int traditional = 0; /* if set, be backwards compatible */ ++int garrulous = 0; /* if set, print all error messages */ ++int isbinary; /* if set, buffer contains ASCII NULs */ ++int isglobal; /* if set, doing a global command */ ++int modified; /* if set, buffer modified since last write */ ++int mutex = 0; /* if set, signals set "sigflags" */ ++int red = 0; /* if set, restrict shell/directory access */ ++int scripted = 0; /* if set, suppress diagnostics */ ++int sigactive = 0; /* if set, signal handlers are enabled */ ++int sigflags = 0; /* if set, signals received while mutex set */ ++ ++char *old_filename; /* default filename */ ++long current_addr; /* current address in editor buffer */ ++long addr_last; /* last address in editor buffer */ ++int lineno; /* script line number */ ++char *prompt; /* command-line prompt */ ++char *dps = "*"; /* default command-line prompt */ ++long err_status = 0; /* program exit status */ ++ ++/* The name this program was run with. */ ++char *program_name; ++ ++/* If non-zero, display usage information and exit. */ ++int show_help = 0; ++ ++/* If non-zero, print the version on standard output and exit. */ ++int show_version = 0; ++ ++/* Long options equivalences. */ ++struct option long_options[] = ++{ ++ {"help", no_argument, &show_help, 1}, ++ {"prompt", required_argument, NULL, 'p'}, ++ {"quiet", no_argument, NULL, 's'}, ++ {"silent", no_argument, NULL, 's'}, ++ {"traditional", no_argument, NULL, 'G'}, ++ {"version", no_argument, &show_version, 1}, ++ {0, 0, 0, 0}, ++}; ++ ++extern int optind; ++extern char *optarg; ++ ++/* usage: explain usage */ ++ ++#if 0 ++void ++usage (status) ++ int status; ++{ ++ if (status != 0) ++ fprintf (stderr, "Try `%s --help' for more information.\n", program_name); ++ else ++ { ++ printf ("Usage: %s [OPTION]... [FILE]\n", program_name); ++ printf ("\ ++\n\ ++ -G, --traditional use a few backward compatible features\n\ ++ -p, --prompt=STRING use STRING as an interactive prompt\n\ ++ -s, -, --quiet, --silent suppress diagnostics\n"); ++ printf ("\ ++ --help display this help\n\ ++ --version output version information\n\ ++\n\ ++Start edit by reading in FILE if given. Read output of shell command\n\ ++if FILE begins with a `!'.\n"); ++ } ++ exit (status); ++} ++#endif ++#define usage(x) bb_show_usage() ++ ++ ++/* ed: line editor */ ++int ++ed_main (int argc, char **argv) ++{ ++ int c, n; ++ long status = 0; ++ ++ program_name = argv[0]; ++ red = (n = strlen (argv[0])) > 2 && argv[0][n - 3] == 'r'; ++top: ++ while ((c = getopt_long (argc, argv, "Gp:s", long_options, NULL)) != EOF) ++ switch (c) ++ { ++ default: ++ usage (1); ++ case 0: ++ break; ++ case 'G': /* backward compatibility */ ++ traditional = 1; ++ break; ++ case 'p': /* set prompt */ ++ prompt = optarg; ++ break; ++ case 's': /* run script */ ++ scripted = 1; ++ break; ++ } ++ if (show_help) ++ usage (0); ++ argv += optind; ++ argc -= optind; ++ if (argc && **argv == '-') ++ { ++ scripted = 1; ++ if (argc > 1) ++ { ++ optind = 0; ++ goto top; ++ } ++ argv++; ++ argc--; ++ } ++ init_buffers (); ++ ++ /* assert: reliable signals! */ ++#ifdef SIGWINCH ++ handle_winch (SIGWINCH); ++ if (isatty (0)) ++ reliable_signal (SIGWINCH, handle_winch); ++#endif ++ reliable_signal (SIGHUP, signal_hup); ++ reliable_signal (SIGQUIT, SIG_IGN); ++ reliable_signal (SIGINT, signal_int); ++ if ((status = setjmp (env))) ++ { ++ fputs ("\n?\n", stderr); ++ sprintf (errmsg, "Interrupt"); ++ } ++ else ++ { ++ sigactive = 1; /* enable signal handlers */ ++ if (argc && **argv && is_legal_filename (*argv)) ++ { ++ if (read_file (*argv, 0) < 0 && is_regular_file (0)) ++ quit (2); ++ else if (**argv != '!') ++ strcpy (old_filename, *argv); ++ } ++ else if (argc) ++ { ++ fputs ("?\n", stderr); ++ if (**argv == '\0') ++ sprintf (errmsg, "Invalid filename"); ++ if (is_regular_file (0)) ++ quit (2); ++ } ++ } ++ for (;;) ++ { ++ if (status < 0 && garrulous) ++ fprintf (stderr, "%s\n", errmsg); ++ if (prompt) ++ { ++ printf ("%s", prompt); ++ fflush (stdout); ++ } ++ if ((n = get_tty_line ()) < 0) ++ { ++ status = ERR; ++ continue; ++ } ++ else if (n == 0) ++ { ++ if (modified && !scripted) ++ { ++ fputs ("?\n", stderr); ++ sprintf (errmsg, "Warning: file modified"); ++ if (is_regular_file (0)) ++ { ++ fprintf (stderr, garrulous ? ++ "script, line %d: %s\n" : ++ "", lineno, errmsg); ++ quit (2); ++ } ++ clearerr (stdin); ++ modified = 0; ++ status = EMOD; ++ continue; ++ } ++ else ++ quit (err_status); ++ } ++ else if (ibuf[n - 1] != '\n') ++ { ++ /* discard line */ ++ sprintf (errmsg, "Unexpected end-of-file"); ++ clearerr (stdin); ++ status = ERR; ++ continue; ++ } ++ isglobal = 0; ++ if ((status = extract_addr_range ()) >= 0 && ++ (status = exec_command ()) >= 0) ++ if (!status || (status = display_lines (current_addr, current_addr, ++ status)) >= 0) ++ continue; ++ switch (status) ++ { ++ case EOF: ++ quit (err_status); ++ case EMOD: ++ modified = 0; ++ fputs ("?\n", stderr); /* give warning */ ++ sprintf (errmsg, "Warning: file modified"); ++ if (is_regular_file (0)) ++ { ++ fprintf (stderr, garrulous ? ++ "script, line %d: %s\n" : ++ "", lineno, errmsg); ++ quit (2); ++ } ++ break; ++ case FATAL: ++ if (is_regular_file (0)) ++ fprintf (stderr, garrulous ? ++ "script, line %d: %s\n" : "", ++ lineno, errmsg); ++ else ++ fprintf (stderr, garrulous ? "%s\n" : "", ++ errmsg); ++ quit (3); ++ default: ++ fputs ("?\n", stderr); ++ if (is_regular_file (0)) ++ { ++ fprintf (stderr, garrulous ? ++ "script, line %d: %s\n" : "", ++ lineno, errmsg); ++ quit (4); ++ } ++ break; ++ } ++ err_status = -status; ++ } ++ /*NOTREACHED */ ++} ++ ++long first_addr, second_addr, addr_cnt; ++ ++/* extract_addr_range: get line addresses from the command buffer until an ++ illegal address is seen; return status */ ++int ++extract_addr_range () ++{ ++ long addr; ++ ++ addr_cnt = 0; ++ first_addr = second_addr = current_addr; ++ while ((addr = next_addr ()) >= 0) ++ { ++ addr_cnt++; ++ first_addr = second_addr; ++ second_addr = addr; ++ if (*ibufp != ',' && *ibufp != ';') ++ break; ++ else if (*ibufp++ == ';') ++ current_addr = addr; ++ } ++ if ((addr_cnt = min (addr_cnt, 2)) == 1 || second_addr != addr) ++ first_addr = second_addr; ++ return (addr == ERR) ? ERR : 0; ++} ++ ++ ++#define SKIP_BLANKS() \ ++ while (isspace (*ibufp) && *ibufp != '\n') \ ++ ibufp++ ++ ++#define MUST_BE_FIRST() \ ++ do \ ++ { \ ++ if (!first) \ ++ { \ ++ sprintf (errmsg, "Invalid address"); \ ++ return ERR; \ ++ } \ ++ } \ ++ while (0) ++ ++/* next_addr: return the next line address in the command buffer */ ++long ++next_addr () ++{ ++ char *hd; ++ long addr = current_addr; ++ long n; ++ int first = 1; ++ int c; ++ ++ SKIP_BLANKS (); ++ for (hd = ibufp;; first = 0) ++ switch (c = *ibufp) ++ { ++ case '+': ++ case '\t': ++ case ' ': ++ case '-': ++ case '^': ++ ibufp++; ++ SKIP_BLANKS (); ++ if (isdigit (*ibufp)) ++ { ++ STRTOL (n, ibufp); ++ addr += (c == '-' || c == '^') ? -n : n; ++ } ++ else if (!isspace (c)) ++ addr += (c == '-' || c == '^') ? -1 : 1; ++ break; ++ case '0': ++ case '1': ++ case '2': ++ case '3': ++ case '4': ++ case '5': ++ case '6': ++ case '7': ++ case '8': ++ case '9': ++ MUST_BE_FIRST (); ++ STRTOL (addr, ibufp); ++ break; ++ case '.': ++ case '$': ++ MUST_BE_FIRST (); ++ ibufp++; ++ addr = (c == '.') ? current_addr : addr_last; ++ break; ++ case '/': ++ case '?': ++ MUST_BE_FIRST (); ++ if ((addr = get_matching_node_addr ( ++ get_compiled_pattern (), c == '/')) < 0) ++ return ERR; ++ else if (c == *ibufp) ++ ibufp++; ++ break; ++ case '\'': ++ MUST_BE_FIRST (); ++ ibufp++; ++ if ((addr = get_marked_node_addr (*ibufp++)) < 0) ++ return ERR; ++ break; ++ case '%': ++ case ',': ++ case ';': ++ if (first) ++ { ++ ibufp++; ++ addr_cnt++; ++ second_addr = (c == ';') ? current_addr : 1; ++ addr = addr_last; ++ break; ++ } ++ /* FALL THROUGH */ ++ default: ++ if (ibufp == hd) ++ return EOF; ++ else if (addr < 0 || addr_last < addr) ++ { ++ sprintf (errmsg, "Invalid address"); ++ return ERR; ++ } ++ else ++ return addr; ++ } ++ /* NOTREACHED */ ++} ++ ++ ++/* GET_THIRD_ADDR: get a legal address from the command buffer */ ++#define GET_THIRD_ADDR(addr) \ ++ do \ ++ { \ ++ long ol1, ol2; \ ++ ol1 = first_addr, ol2 = second_addr; \ ++ if (extract_addr_range () < 0) \ ++ return ERR; \ ++ else if (traditional && addr_cnt == 0) \ ++ { \ ++ sprintf (errmsg, "Destination expected"); \ ++ return ERR; \ ++ } \ ++ else if (second_addr < 0 || addr_last < second_addr) \ ++ { \ ++ sprintf (errmsg, "Invalid address"); \ ++ return ERR; \ ++ } \ ++ (addr) = second_addr; \ ++ first_addr = ol1, second_addr = ol2; \ ++ } \ ++ while (0) ++ ++/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */ ++#define GET_COMMAND_SUFFIX() \ ++ do \ ++ { \ ++ int done = 0; \ ++ do \ ++ { \ ++ switch (*ibufp) \ ++ { \ ++ case 'p': \ ++ gflag |= GPR, ibufp++; \ ++ break; \ ++ case 'l': \ ++ gflag |= GLS, ibufp++; \ ++ break; \ ++ case 'n': \ ++ gflag |= GNP, ibufp++; \ ++ break; \ ++ default: \ ++ done++; \ ++ } \ ++ } \ ++ while (!done); \ ++ if (*ibufp++ != '\n') \ ++ { \ ++ sprintf (errmsg, "Invalid command suffix"); \ ++ return ERR; \ ++ } \ ++ } \ ++ while (0) ++ ++/* sflags */ ++#define SGG 001 /* complement previous global substitute suffix */ ++#define SGP 002 /* complement previous print suffix */ ++#define SGR 004 /* use last regex instead of last pat */ ++#define SGF 010 /* repeat last substitution */ ++ ++int patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */ ++ ++long rows = 22; /* scroll length: ws_row - 2 */ ++ ++/* exec_command: execute the next command in command buffer; return print ++ request, if any */ ++int ++exec_command () ++{ ++ extern long u_current_addr; ++ extern long u_addr_last; ++ ++ static pattern_t *pat = NULL; ++ static int sgflag = 0; ++ static int sgnum = 0; ++ ++ pattern_t *tpat; ++ char *fnp; ++ int gflag = 0; ++ int sflags = 0; ++ long addr = 0; ++ int n = 0; ++ int c; ++ ++ SKIP_BLANKS (); ++ switch (c = *ibufp++) ++ { ++ case 'a': ++ GET_COMMAND_SUFFIX (); ++ if (!isglobal) ++ clear_undo_stack (); ++ if (append_lines (second_addr) < 0) ++ return ERR; ++ break; ++ case 'c': ++ if (check_addr_range (current_addr, current_addr) < 0) ++ return ERR; ++ GET_COMMAND_SUFFIX (); ++ if (!isglobal) ++ clear_undo_stack (); ++ if (delete_lines (first_addr, second_addr) < 0 || ++ append_lines (current_addr) < 0) ++ return ERR; ++ break; ++ case 'd': ++ if (check_addr_range (current_addr, current_addr) < 0) ++ return ERR; ++ GET_COMMAND_SUFFIX (); ++ if (!isglobal) ++ clear_undo_stack (); ++ if (delete_lines (first_addr, second_addr) < 0) ++ return ERR; ++ else if ((addr = INC_MOD (current_addr, addr_last)) != 0) ++ current_addr = addr; ++ break; ++ case 'e': ++ if (modified && !scripted) ++ return EMOD; ++ /* fall through */ ++ case 'E': ++ if (addr_cnt > 0) ++ { ++ sprintf (errmsg, "Unexpected address"); ++ return ERR; ++ } ++ else if (!isspace (*ibufp)) ++ { ++ sprintf (errmsg, "Unexpected command suffix"); ++ return ERR; ++ } ++ else if ((fnp = get_filename ()) == NULL) ++ return ERR; ++ GET_COMMAND_SUFFIX (); ++ if (delete_lines (1, addr_last) < 0) ++ return ERR; ++ delete_yank_lines (); ++ clear_undo_stack (); ++ if (close_sbuf () < 0) ++ return ERR; ++ else if (open_sbuf () < 0) ++ return FATAL; ++ if (*fnp && *fnp != '!') ++ strcpy (old_filename, fnp); ++ if (traditional && *fnp == '\0' && *old_filename == '\0') ++ { ++ sprintf (errmsg, "No current filename"); ++ return ERR; ++ } ++ else if (read_file (*fnp ? fnp : old_filename, 0) < 0) ++ return ERR; ++ clear_undo_stack (); ++ modified = 0; ++ u_current_addr = u_addr_last = -1; ++ break; ++ case 'f': ++ if (addr_cnt > 0) ++ { ++ sprintf (errmsg, "Unexpected address"); ++ return ERR; ++ } ++ else if (!isspace (*ibufp)) ++ { ++ sprintf (errmsg, "Unexpected command suffix"); ++ return ERR; ++ } ++ else if ((fnp = get_filename ()) == NULL) ++ return ERR; ++ else if (*fnp == '!') ++ { ++ sprintf (errmsg, "Invalid redirection"); ++ return ERR; ++ } ++ GET_COMMAND_SUFFIX (); ++ if (*fnp) ++ strcpy (old_filename, fnp); ++ printf ("%s\n", strip_escapes (old_filename)); ++ break; ++ case 'g': ++ case 'v': ++ case 'G': ++ case 'V': ++ if (isglobal) ++ { ++ sprintf (errmsg, "Cannot nest global commands"); ++ return ERR; ++ } ++ else if (check_addr_range (1, addr_last) < 0) ++ return ERR; ++ else if (build_active_list (c == 'g' || c == 'G') < 0) ++ return ERR; ++ else if ((n = (c == 'G' || c == 'V'))) ++ GET_COMMAND_SUFFIX (); ++ isglobal++; ++ if (exec_global (n, gflag) < 0) ++ return ERR; ++ break; ++ case 'h': ++ if (addr_cnt > 0) ++ { ++ sprintf (errmsg, "Unexpected address"); ++ return ERR; ++ } ++ GET_COMMAND_SUFFIX (); ++ if (*errmsg) ++ fprintf (stderr, "%s\n", errmsg); ++ break; ++ case 'H': ++ if (addr_cnt > 0) ++ { ++ sprintf (errmsg, "Unexpected address"); ++ return ERR; ++ } ++ GET_COMMAND_SUFFIX (); ++ if ((garrulous = 1 - garrulous) && *errmsg) ++ fprintf (stderr, "%s\n", errmsg); ++ break; ++ case 'i': ++ if (second_addr == 0) ++ { ++ sprintf (errmsg, "Invalid address"); ++ return ERR; ++ } ++ GET_COMMAND_SUFFIX (); ++ if (!isglobal) ++ clear_undo_stack (); ++ if (append_lines (second_addr - 1) < 0) ++ return ERR; ++ break; ++ case 'j': ++ if (check_addr_range (current_addr, current_addr + 1) < 0) ++ return ERR; ++ GET_COMMAND_SUFFIX (); ++ if (!isglobal) ++ clear_undo_stack (); ++ if (first_addr != second_addr && ++ join_lines (first_addr, second_addr) < 0) ++ return ERR; ++ break; ++ case 'k': ++ c = *ibufp++; ++ if (second_addr == 0) ++ { ++ sprintf (errmsg, "Invalid address"); ++ return ERR; ++ } ++ GET_COMMAND_SUFFIX (); ++ if (mark_line_node (get_addressed_line_node (second_addr), c) < 0) ++ return ERR; ++ break; ++ case 'l': ++ if (check_addr_range (current_addr, current_addr) < 0) ++ return ERR; ++ GET_COMMAND_SUFFIX (); ++ if (display_lines (first_addr, second_addr, gflag | GLS) < 0) ++ return ERR; ++ gflag = 0; ++ break; ++ case 'm': ++ if (check_addr_range (current_addr, current_addr) < 0) ++ return ERR; ++ GET_THIRD_ADDR (addr); ++ if (first_addr <= addr && addr < second_addr) ++ { ++ sprintf (errmsg, "Invalid destination"); ++ return ERR; ++ } ++ GET_COMMAND_SUFFIX (); ++ if (!isglobal) ++ clear_undo_stack (); ++ if (move_lines (addr) < 0) ++ return ERR; ++ break; ++ case 'n': ++ if (check_addr_range (current_addr, current_addr) < 0) ++ return ERR; ++ GET_COMMAND_SUFFIX (); ++ if (display_lines (first_addr, second_addr, gflag | GNP) < 0) ++ return ERR; ++ gflag = 0; ++ break; ++ case 'p': ++ if (check_addr_range (current_addr, current_addr) < 0) ++ return ERR; ++ GET_COMMAND_SUFFIX (); ++ if (display_lines (first_addr, second_addr, gflag | GPR) < 0) ++ return ERR; ++ gflag = 0; ++ break; ++ case 'P': ++ if (addr_cnt > 0) ++ { ++ sprintf (errmsg, "Unexpected address"); ++ return ERR; ++ } ++ GET_COMMAND_SUFFIX (); ++ prompt = prompt ? NULL : optarg ? optarg : dps; ++ break; ++ case 'q': ++ case 'Q': ++ if (addr_cnt > 0) ++ { ++ sprintf (errmsg, "Unexpected address"); ++ return ERR; ++ } ++ GET_COMMAND_SUFFIX (); ++ gflag = (modified && !scripted && c == 'q') ? EMOD : EOF; ++ break; ++ case 'r': ++ if (!isspace (*ibufp)) ++ { ++ sprintf (errmsg, "Unexpected command suffix"); ++ return ERR; ++ } ++ else if (addr_cnt == 0) ++ second_addr = addr_last; ++ if ((fnp = get_filename ()) == NULL) ++ return ERR; ++ GET_COMMAND_SUFFIX (); ++ if (!isglobal) ++ clear_undo_stack (); ++ if (*old_filename == '\0' && *fnp != '!') ++ strcpy (old_filename, fnp); ++ if (traditional && *fnp == '\0' && *old_filename == '\0') ++ { ++ sprintf (errmsg, "No current filename"); ++ return ERR; ++ } ++ if ((addr = read_file (*fnp ? fnp : old_filename, second_addr)) < 0) ++ return ERR; ++ else if (addr && addr != addr_last) ++ modified = 1; ++ break; ++ case 's': ++ do ++ { ++ switch (*ibufp) ++ { ++ case '\n': ++ sflags |= SGF; ++ break; ++ case 'g': ++ sflags |= SGG; ++ ibufp++; ++ break; ++ case 'p': ++ sflags |= SGP; ++ ibufp++; ++ break; ++ case 'r': ++ sflags |= SGR; ++ ibufp++; ++ break; ++ case '0': ++ case '1': ++ case '2': ++ case '3': ++ case '4': ++ case '5': ++ case '6': ++ case '7': ++ case '8': ++ case '9': { ++ long sgnum_long; ++ STRTOL (sgnum_long, ibufp); ++ sgnum = sgnum_long; ++ sflags |= SGF; ++ sgflag &= ~GSG; /* override GSG */ ++ break; ++ } ++ default: ++ if (sflags) ++ { ++ sprintf (errmsg, "Invalid command suffix"); ++ return ERR; ++ } ++ } ++ } ++ while (sflags && *ibufp != '\n'); ++ if (sflags && !pat) ++ { ++ sprintf (errmsg, "No previous substitution"); ++ return ERR; ++ } ++ else if (sflags & SGG) ++ sgnum = 0; /* override numeric arg */ ++ if (*ibufp != '\n' && *(ibufp + 1) == '\n') ++ { ++ sprintf (errmsg, "Invalid pattern delimiter"); ++ return ERR; ++ } ++ tpat = pat; ++ SPL1 (); ++ if ((!sflags || (sflags & SGR)) && ++ (tpat = get_compiled_pattern ()) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ else if (tpat != pat) ++ { ++ if (pat) ++ { ++ regfree (pat); ++ free (pat); ++ } ++ pat = tpat; ++ patlock = 1; /* reserve pattern */ ++ } ++ SPL0 (); ++ if (!sflags && extract_subst_tail (&sgflag, &sgnum) < 0) ++ return ERR; ++ else if (isglobal) ++ sgflag |= GLB; ++ else ++ sgflag &= ~GLB; ++ if (sflags & SGG) ++ sgflag ^= GSG; ++ if (sflags & SGP) ++ sgflag ^= GPR, sgflag &= ~(GLS | GNP); ++ do ++ { ++ switch (*ibufp) ++ { ++ case 'p': ++ sgflag |= GPR, ibufp++; ++ break; ++ case 'l': ++ sgflag |= GLS, ibufp++; ++ break; ++ case 'n': ++ sgflag |= GNP, ibufp++; ++ break; ++ default: ++ n++; ++ } ++ } ++ while (!n); ++ if (check_addr_range (current_addr, current_addr) < 0) ++ return ERR; ++ GET_COMMAND_SUFFIX (); ++ if (!isglobal) ++ clear_undo_stack (); ++ if (search_and_replace (pat, sgflag, sgnum) < 0) ++ return ERR; ++ break; ++ case 't': ++ if (check_addr_range (current_addr, current_addr) < 0) ++ return ERR; ++ GET_THIRD_ADDR (addr); ++ GET_COMMAND_SUFFIX (); ++ if (!isglobal) ++ clear_undo_stack (); ++ if (copy_lines (addr) < 0) ++ return ERR; ++ break; ++ case 'u': ++ if (addr_cnt > 0) ++ { ++ sprintf (errmsg, "Unexpected address"); ++ return ERR; ++ } ++ GET_COMMAND_SUFFIX (); ++ if (pop_undo_stack () < 0) ++ return ERR; ++ break; ++ case 'w': ++ case 'W': ++ if ((n = *ibufp) == 'q' || n == 'Q') ++ { ++ gflag = EOF; ++ ibufp++; ++ } ++ if (!isspace (*ibufp)) ++ { ++ sprintf (errmsg, "Unexpected command suffix"); ++ return ERR; ++ } ++ else if ((fnp = get_filename ()) == NULL) ++ return ERR; ++ if (addr_cnt == 0 && !addr_last) ++ first_addr = second_addr = 0; ++ else if (check_addr_range (1, addr_last) < 0) ++ return ERR; ++ GET_COMMAND_SUFFIX (); ++ if (*old_filename == '\0' && *fnp != '!') ++ strcpy (old_filename, fnp); ++ if (traditional && *fnp == '\0' && *old_filename == '\0') ++ { ++ sprintf (errmsg, "No current filename"); ++ return ERR; ++ } ++ if ((addr = write_file (*fnp ? fnp : old_filename, ++ (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0) ++ return ERR; ++ else if (addr == addr_last) ++ modified = 0; ++ else if (modified && !scripted && n == 'q') ++ gflag = EMOD; ++ break; ++ case 'x': ++ if (second_addr < 0 || addr_last < second_addr) ++ { ++ sprintf (errmsg, "Invalid address"); ++ return ERR; ++ } ++ GET_COMMAND_SUFFIX (); ++ if (!isglobal) ++ clear_undo_stack (); ++ if (put_lines (second_addr) < 0) ++ return ERR; ++ break; ++ case 'y': ++ if (check_addr_range (current_addr, current_addr) < 0) ++ return ERR; ++ GET_COMMAND_SUFFIX (); ++ if (yank_lines (first_addr, second_addr) < 0) ++ return ERR; ++ break; ++ case 'z': ++ if (check_addr_range ++ (first_addr = 1, current_addr + (traditional ? 1 : !isglobal)) < 0) ++ return ERR; ++ else if ('0' < *ibufp && *ibufp <= '9') ++ STRTOL (rows, ibufp); ++ GET_COMMAND_SUFFIX (); ++ if (display_lines (second_addr, min (addr_last, ++ second_addr + rows), gflag) < 0) ++ return ERR; ++ gflag = 0; ++ break; ++ case '=': ++ GET_COMMAND_SUFFIX (); ++ printf ("%ld\n", addr_cnt ? second_addr : addr_last); ++ break; ++ case '!': ++ if (addr_cnt > 0) ++ { ++ sprintf (errmsg, "Unexpected address"); ++ return ERR; ++ } ++ else if ((sflags = get_shell_command ()) < 0) ++ return ERR; ++ GET_COMMAND_SUFFIX (); ++ if (sflags) ++ printf ("%s\n", shcmd + 1); ++ system (shcmd + 1); ++ if (!scripted) ++ printf ("!\n"); ++ break; ++ case '\n': ++ if (check_addr_range ++ (first_addr = 1, current_addr + (traditional ? 1 : !isglobal)) < 0 || ++ display_lines (second_addr, second_addr, 0) < 0) ++ return ERR; ++ break; ++ case '#': ++ while (*ibufp++ != '\n') ++ ; ++ break; ++ default: ++ sprintf (errmsg, "Unknown command"); ++ return ERR; ++ } ++ return gflag; ++} ++ ++ ++/* check_addr_range: return status of address range check */ ++int ++check_addr_range (n, m) ++ long n, m; ++{ ++ if (addr_cnt == 0) ++ { ++ first_addr = n; ++ second_addr = m; ++ } ++ if (first_addr > second_addr || 1 > first_addr || ++ second_addr > addr_last) ++ { ++ sprintf (errmsg, "Invalid address"); ++ return ERR; ++ } ++ return 0; ++} ++ ++ ++/* get_matching_node_addr: return the address of the next line matching a ++ pattern in a given direction. wrap around begin/end of editor buffer if ++ necessary */ ++long ++get_matching_node_addr (pat, dir) ++ pattern_t *pat; ++ int dir; ++{ ++ char *s; ++ long n = current_addr; ++ line_t *lp; ++ ++ if (!pat) ++ return ERR; ++ do ++ { ++ if ((n = dir ? INC_MOD (n, addr_last) : DEC_MOD (n, addr_last))) ++ { ++ lp = get_addressed_line_node (n); ++ if ((s = get_sbuf_line (lp)) == NULL) ++ return ERR; ++ if (isbinary) ++ NUL_TO_NEWLINE (s, lp->len); ++ if (!regexec (pat, s, 0, NULL, 0)) ++ return n; ++ } ++ } ++ while (n != current_addr); ++ sprintf (errmsg, "No match"); ++ return ERR; ++} ++ ++ ++/* get_filename: return pointer to copy of filename in the command buffer */ ++char * ++get_filename () ++{ ++ static char *file = NULL; ++ static int filesz = 0; ++ ++ int n; ++ ++ if (*ibufp != '\n') ++ { ++ SKIP_BLANKS (); ++ if (*ibufp == '\n') ++ { ++ sprintf (errmsg, "Invalid filename"); ++ return NULL; ++ } ++ else if ((ibufp = get_extended_line (&n, 1)) == NULL) ++ return NULL; ++ else if (*ibufp == '!') ++ { ++ ibufp++; ++ if ((n = get_shell_command ()) < 0) ++ return NULL; ++ if (n) ++ printf ("%s\n", shcmd + 1); ++ return shcmd; ++ } ++ else if (n > PATH_MAX) ++ { ++ sprintf (errmsg, "Filename too long"); ++ return NULL; ++ } ++ } ++ else if (!traditional && *old_filename == '\0') ++ { ++ sprintf (errmsg, "No current filename"); ++ return NULL; ++ } ++ REALLOC (file, filesz, PATH_MAX + 1, NULL); ++ for (n = 0; *ibufp != '\n';) ++ file[n++] = *ibufp++; ++ file[n] = '\0'; ++ return is_legal_filename (file) ? file : NULL; ++} ++ ++ ++/* get_shell_command: read a shell command from stdin; return substitution ++ status */ ++int ++get_shell_command () ++{ ++ static char *buf = NULL; ++ static int n = 0; ++ ++ char *s; /* substitution char pointer */ ++ int i = 0; ++ int j = 0; ++ ++ if (red) ++ { ++ sprintf (errmsg, "Shell access restricted"); ++ return ERR; ++ } ++ else if ((s = ibufp = get_extended_line (&j, 1)) == NULL) ++ return ERR; ++ REALLOC (buf, n, j + 1, ERR); ++ buf[i++] = '!'; /* prefix command w/ bang */ ++ while (*ibufp != '\n') ++ switch (*ibufp) ++ { ++ default: ++ REALLOC (buf, n, i + 2, ERR); ++ buf[i++] = *ibufp; ++ if (*ibufp++ == '\\') ++ buf[i++] = *ibufp++; ++ break; ++ case '!': ++ if (s != ibufp) ++ { ++ REALLOC (buf, n, i + 1, ERR); ++ buf[i++] = *ibufp++; ++ } ++ else if (shcmd == NULL || (traditional && *(shcmd + 1) == '\0')) ++ { ++ sprintf (errmsg, "No previous command"); ++ return ERR; ++ } ++ else ++ { ++ REALLOC (buf, n, i + shcmdi, ERR); ++ for (s = shcmd + 1; s < shcmd + shcmdi;) ++ buf[i++] = *s++; ++ s = ibufp++; ++ } ++ break; ++ case '%': ++ if (*old_filename == '\0') ++ { ++ sprintf (errmsg, "No current filename"); ++ return ERR; ++ } ++ j = strlen (s = strip_escapes (old_filename)); ++ REALLOC (buf, n, i + j, ERR); ++ while (j--) ++ buf[i++] = *s++; ++ s = ibufp++; ++ break; ++ } ++ REALLOC (shcmd, shcmdsz, i + 1, ERR); ++ memcpy (shcmd, buf, i); ++ shcmd[shcmdi = i] = '\0'; ++ return *s == '!' || *s == '%'; ++} ++ ++ ++/* append_lines: insert text from stdin to after line n; stop when either a ++ single period is read or EOF; return status */ ++int ++append_lines (n) ++ long n; ++{ ++ int l; ++ char *lp = ibuf; ++ char *eot; ++ undo_t *up = NULL; ++ ++ for (current_addr = n;;) ++ { ++ if (!isglobal) ++ { ++ if ((l = get_tty_line ()) < 0) ++ return ERR; ++ else if (l == 0 || ibuf[l - 1] != '\n') ++ { ++ clearerr (stdin); ++ return l ? EOF : 0; ++ } ++ lp = ibuf; ++ } ++ else if (*(lp = ibufp) == '\0') ++ return 0; ++ else ++ { ++ while (*ibufp++ != '\n') ++ ; ++ l = ibufp - lp; ++ } ++ if (l == 2 && lp[0] == '.' && lp[1] == '\n') ++ { ++ return 0; ++ } ++ eot = lp + l; ++ SPL1 (); ++ do ++ { ++ if ((lp = put_sbuf_line (lp)) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ else if (up) ++ up->t = get_addressed_line_node (current_addr); ++ else if ((up = push_undo_stack (UADD, current_addr, ++ current_addr)) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ } ++ while (lp != eot); ++ modified = 1; ++ SPL0 (); ++ } ++ /* NOTREACHED */ ++} ++ ++ ++/* join_lines: replace a range of lines with the joined text of those lines */ ++int ++join_lines (from, to) ++ long from; ++ long to; ++{ ++ static char *buf = NULL; ++ static int n; ++ ++ char *s; ++ int size = 0; ++ line_t *bp, *ep; ++ ++ ep = get_addressed_line_node (INC_MOD (to, addr_last)); ++ bp = get_addressed_line_node (from); ++ for (; bp != ep; bp = bp->q_forw) ++ { ++ if ((s = get_sbuf_line (bp)) == NULL) ++ return ERR; ++ REALLOC (buf, n, size + bp->len, ERR); ++ memcpy (buf + size, s, bp->len); ++ size += bp->len; ++ } ++ REALLOC (buf, n, size + 2, ERR); ++ memcpy (buf + size, "\n", 2); ++ if (delete_lines (from, to) < 0) ++ return ERR; ++ current_addr = from - 1; ++ SPL1 (); ++ if (put_sbuf_line (buf) == NULL || ++ push_undo_stack (UADD, current_addr, current_addr) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ modified = 1; ++ SPL0 (); ++ return 0; ++} ++ ++ ++/* move_lines: move a range of lines */ ++int ++move_lines (addr) ++ long addr; ++{ ++ line_t *b1, *a1, *b2, *a2; ++ long n = INC_MOD (second_addr, addr_last); ++ long p = first_addr - 1; ++ int done = (addr == first_addr - 1 || addr == second_addr); ++ ++ SPL1 (); ++ if (done) ++ { ++ a2 = get_addressed_line_node (n); ++ b2 = get_addressed_line_node (p); ++ current_addr = second_addr; ++ } ++ else if (push_undo_stack (UMOV, p, n) == NULL || ++ push_undo_stack (UMOV, addr, INC_MOD (addr, addr_last)) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ else ++ { ++ a1 = get_addressed_line_node (n); ++ if (addr < first_addr) ++ { ++ b1 = get_addressed_line_node (p); ++ b2 = get_addressed_line_node (addr); ++ /* this get_addressed_line_node last! */ ++ } ++ else ++ { ++ b2 = get_addressed_line_node (addr); ++ b1 = get_addressed_line_node (p); ++ /* this get_addressed_line_node last! */ ++ } ++ a2 = b2->q_forw; ++ REQUE (b2, b1->q_forw); ++ REQUE (a1->q_back, a2); ++ REQUE (b1, a1); ++ current_addr = addr + ((addr < first_addr) ? ++ second_addr - first_addr + 1 : 0); ++ } ++ if (isglobal) ++ unset_active_nodes (b2->q_forw, a2); ++ modified = 1; ++ SPL0 (); ++ return 0; ++} ++ ++ ++/* copy_lines: copy a range of lines; return status */ ++int ++copy_lines (addr) ++ long addr; ++{ ++ line_t *lp, *np = get_addressed_line_node (first_addr); ++ undo_t *up = NULL; ++ long n = second_addr - first_addr + 1; ++ long m = 0; ++ ++ current_addr = addr; ++ if (first_addr <= addr && addr < second_addr) ++ { ++ n = addr - first_addr + 1; ++ m = second_addr - addr; ++ } ++ for (; n > 0; n = m, m = 0, np = get_addressed_line_node (current_addr + 1)) ++ for (; n-- > 0; np = np->q_forw) ++ { ++ SPL1 (); ++ if ((lp = dup_line_node (np)) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ add_line_node (lp); ++ if (up) ++ up->t = lp; ++ else if ((up = push_undo_stack (UADD, current_addr, ++ current_addr)) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ modified = 1; ++ SPL0 (); ++ } ++ return 0; ++} ++ ++ ++/* delete_lines: delete a range of lines */ ++int ++delete_lines (from, to) ++ long from, to; ++{ ++ line_t *n, *p; ++ ++ if (yank_lines (from, to) < 0) ++ return ERR; ++ SPL1 (); ++ if (push_undo_stack (UDEL, from, to) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ n = get_addressed_line_node (INC_MOD (to, addr_last)); ++ p = get_addressed_line_node (from - 1); ++ /* this get_addressed_line_node last! */ ++ if (isglobal) ++ unset_active_nodes (p->q_forw, n); ++ REQUE (p, n); ++ addr_last -= to - from + 1; ++ current_addr = from - 1; ++ modified = 1; ++ SPL0 (); ++ return 0; ++} ++ ++ ++int dlcnt = 0; /* # of lines displayed */ ++ ++/* display_lines: print a range of lines to stdout */ ++int ++display_lines (from, to, gflag) ++ long from; ++ long to; ++ int gflag; ++{ ++ line_t *bp; ++ line_t *ep; ++ char *s; ++ ++ if (!from) ++ { ++ sprintf (errmsg, "Invalid address"); ++ return ERR; ++ } ++ ep = get_addressed_line_node (INC_MOD (to, addr_last)); ++ bp = get_addressed_line_node (from); ++ for (dlcnt = 0; bp != ep; bp = bp->q_forw) ++ { ++ if ((s = get_sbuf_line (bp)) == NULL) ++ return ERR; ++ else if (put_tty_line (s, bp->len, current_addr = from++, gflag) < 0) ++ return ERR; ++ else if (!traditional && !scripted && !isglobal && ++ bp->q_forw != ep && ++dlcnt > rows) ++ { ++ dlcnt = 0; ++ fputs ("Press to continue... ", stdout); ++ fflush (stdout); ++ if (get_tty_line () < 0) ++ return ERR; ++ } ++ } ++ return 0; ++} ++ ++ ++line_t yank_buffer_head; /* head of yank buffer */ ++ ++/* yank_lines: copy a range of lines to the cut buffer */ ++int ++yank_lines (from, to) ++ long from; ++ long to; ++{ ++ line_t *bp, *cp, *ep, *lp; ++ ++ ++ delete_yank_lines (); ++ ep = get_addressed_line_node (INC_MOD (to, addr_last)); ++ bp = get_addressed_line_node (from); ++ for (lp = &yank_buffer_head; bp != ep; bp = bp->q_forw, lp = cp) ++ { ++ SPL1 (); ++ if ((cp = dup_line_node (bp)) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ INSQUE (cp, lp); ++ SPL0 (); ++ } ++ return 0; ++} ++ ++ ++/* delete_yank_lines: delete lines from the yank buffer */ ++void ++delete_yank_lines () ++{ ++ line_t *cp, *lp; ++ ++ ++ for (lp = yank_buffer_head.q_forw; lp != &yank_buffer_head; lp = cp) ++ { ++ SPL1 (); ++ cp = lp->q_forw; ++ REQUE (lp->q_back, lp->q_forw); ++ free (lp); ++ SPL0 (); ++ } ++} ++ ++ ++/* put_lines: append lines from the yank buffer */ ++int ++put_lines (addr) ++ long addr; ++{ ++ undo_t *up = NULL; ++ line_t *lp, *cp; ++ ++ if ((lp = yank_buffer_head.q_forw) == &yank_buffer_head) ++ { ++ sprintf (errmsg, "Nothing to put"); ++ return ERR; ++ } ++ current_addr = addr; ++ for (; lp != &yank_buffer_head; lp = lp->q_forw) ++ { ++ SPL1 (); ++ if ((cp = dup_line_node (lp)) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ add_line_node (cp); ++ if (up) ++ up->t = cp; ++ else if ((up = push_undo_stack (UADD, current_addr, ++ current_addr)) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ modified = 1; ++ SPL0 (); ++ } ++ return 0; ++} ++ ++ ++#define MAXMARK 26 /* max number of marks */ ++ ++line_t *mark[MAXMARK]; /* line markers */ ++int markno; /* line marker count */ ++ ++/* mark_line_node: set a line node mark */ ++int ++mark_line_node (lp, n) ++ line_t *lp; ++ int n; ++{ ++ if (!islower (n)) ++ { ++ sprintf (errmsg, "Invalid mark character"); ++ return ERR; ++ } ++ else if (mark[n - 'a'] == NULL) ++ markno++; ++ mark[n - 'a'] = lp; ++ return 0; ++} ++ ++ ++/* get_marked_node_addr: return address of a marked line */ ++long ++get_marked_node_addr (n) ++ int n; ++{ ++ if (!islower (n)) ++ { ++ sprintf (errmsg, "Invalid mark character"); ++ return ERR; ++ } ++ return get_line_node_addr (mark[n - 'a']); ++} ++ ++ ++/* unmark_line_node: clear line node mark */ ++void ++unmark_line_node (lp) ++ line_t *lp; ++{ ++ int i; ++ ++ for (i = 0; markno && i < MAXMARK; i++) ++ if (mark[i] == lp) ++ { ++ mark[i] = NULL; ++ markno--; ++ } ++} ++ ++ ++/* dup_line_node: return a pointer to a copy of a line node */ ++line_t * ++dup_line_node (lp) ++ line_t *lp; ++{ ++ line_t *np; ++ ++ if ((np = (line_t *) malloc (sizeof (line_t))) == NULL) ++ { ++ fprintf (stderr, "%s\n", strerror (errno)); ++ sprintf (errmsg, "Out of memory"); ++ return NULL; ++ } ++ np->seek = lp->seek; ++ np->len = lp->len; ++ return np; ++} ++ ++ ++/* has_trailing_escape: return the parity of escapes preceding a character ++ in a string */ ++int ++has_trailing_escape (s, t) ++ char *s; ++ char *t; ++{ ++ return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape (s, t - 1); ++} ++ ++ ++/* strip_escapes: return copy of escaped string of at most length PATH_MAX */ ++char * ++strip_escapes (s) ++ char *s; ++{ ++ static char *file = NULL; ++ static int filesz = 0; ++ ++ int i = 0; ++ ++ REALLOC (file, filesz, PATH_MAX + 1, NULL); ++ /* assert: no trailing escape */ ++ while ((file[i++] = (*s == '\\') ? *++s : *s)) ++ s++; ++ return file; ++} ++ ++ ++#ifndef S_ISREG ++#define S_ISREG(m) ((m & S_IFMT) == S_IFREG) ++#endif ++ ++/* is_regular_file: if file descriptor of a regular file, then return true; ++ otherwise return false */ ++int ++is_regular_file (fd) ++ int fd; ++{ ++ struct stat sb; ++ ++ return fstat (fd, &sb) < 0 || S_ISREG (sb.st_mode); ++} ++ ++ ++/* is_legal_filename: return a legal filename */ ++int ++is_legal_filename (s) ++ char *s; ++{ ++ if (red && (*s == '!' || !strcmp (s, "..") || strchr (s, '/'))) ++ { ++ sprintf (errmsg, "Shell access restricted"); ++ return 0; ++ } ++ return 1; ++} ++ ++FILE *sfp; /* scratch file pointer */ ++off_t sfseek; /* scratch file position */ ++int seek_write; /* seek before writing */ ++line_t buffer_head; /* incore buffer */ ++ ++/* get_sbuf_line: get a line of text from the scratch file; return pointer ++ to the text */ ++char * ++get_sbuf_line (lp) ++ line_t *lp; ++{ ++ static char *sfbuf = NULL; /* buffer */ ++ static int sfbufsz = 0; /* buffer size */ ++ ++ int len, ct; ++ ++ if (lp == &buffer_head) ++ return NULL; ++ seek_write = 1; /* force seek on write */ ++ /* out of position */ ++ if (sfseek != lp->seek) ++ { ++ sfseek = lp->seek; ++ if (fseek (sfp, sfseek, SEEK_SET) < 0) ++ { ++ fprintf (stderr, "%s\n", strerror (errno)); ++ sprintf (errmsg, "Cannot seek temp file"); ++ return NULL; ++ } ++ } ++ len = lp->len; ++ REALLOC (sfbuf, sfbufsz, len + 1, NULL); ++ if ((ct = fread (sfbuf, sizeof (char), len, sfp)) < 0 || ct != len) ++ { ++ fprintf (stderr, "%s\n", strerror (errno)); ++ sprintf (errmsg, "Cannot read temp file"); ++ return NULL; ++ } ++ sfseek += len; /* update file position */ ++ sfbuf[len] = '\0'; ++ return sfbuf; ++} ++ ++ ++/* put_sbuf_line: write a line of text to the scratch file and add a line node ++ to the editor buffer; return a pointer to the end of the text */ ++char * ++put_sbuf_line (cs) ++ char *cs; ++{ ++ line_t *lp; ++ int len, ct; ++ char *s; ++ ++ if ((lp = (line_t *) malloc (sizeof (line_t))) == NULL) ++ { ++ fprintf (stderr, "%s\n", strerror (errno)); ++ sprintf (errmsg, "Out of memory"); ++ return NULL; ++ } ++ /* assert: cs is '\n' terminated */ ++ for (s = cs; *s != '\n'; s++) ++ ; ++ if (s - cs >= LINECHARS) ++ { ++ sprintf (errmsg, "Line too long"); ++ return NULL; ++ } ++ len = s - cs; ++ /* out of position */ ++ if (seek_write) ++ { ++ if (fseek (sfp, 0L, SEEK_END) < 0) ++ { ++ fprintf (stderr, "%s\n", strerror (errno)); ++ sprintf (errmsg, "Cannot seek temp file"); ++ return NULL; ++ } ++ sfseek = ftell (sfp); ++ seek_write = 0; ++ } ++ /* assert: SPL1() */ ++ if ((ct = fwrite (cs, sizeof (char), len, sfp)) < 0 || ct != len) ++ { ++ sfseek = -1; ++ fprintf (stderr, "%s\n", strerror (errno)); ++ sprintf (errmsg, "Cannot write temp file"); ++ return NULL; ++ } ++ lp->len = len; ++ lp->seek = sfseek; ++ add_line_node (lp); ++ sfseek += len; /* update file position */ ++ return ++s; ++} ++ ++ ++/* add_line_node: add a line node in the editor buffer after the current line */ ++void ++add_line_node (lp) ++ line_t *lp; ++{ ++ line_t *cp; ++ ++ cp = get_addressed_line_node (current_addr); /* this get_addressed_line_node last! */ ++ INSQUE (lp, cp); ++ addr_last++; ++ current_addr++; ++} ++ ++ ++/* get_line_node_addr: return line number of pointer */ ++long ++get_line_node_addr (lp) ++ line_t *lp; ++{ ++ line_t *cp = &buffer_head; ++ long n = 0; ++ ++ while (cp != lp && (cp = cp->q_forw) != &buffer_head) ++ n++; ++ if (n && cp == &buffer_head) ++ { ++ sprintf (errmsg, "Invalid address"); ++ return ERR; ++ } ++ return n; ++} ++ ++ ++/* get_addressed_line_node: return pointer to a line node in the editor buffer */ ++line_t * ++get_addressed_line_node (n) ++ long n; ++{ ++ static line_t *lp = &buffer_head; ++ static long on = 0; ++ ++ SPL1 (); ++ if (n > on) ++ if (n <= (on + addr_last) >> 1) ++ for (; on < n; on++) ++ lp = lp->q_forw; ++ else ++ { ++ lp = buffer_head.q_back; ++ for (on = addr_last; on > n; on--) ++ lp = lp->q_back; ++ } ++ else if (n >= on >> 1) ++ for (; on > n; on--) ++ lp = lp->q_back; ++ else ++ { ++ lp = &buffer_head; ++ for (on = 0; on < n; on++) ++ lp = lp->q_forw; ++ } ++ SPL0 (); ++ return lp; ++} ++ ++ ++extern int newline_added; ++ ++/* open_sbuf: open scratch file */ ++int ++open_sbuf () ++{ ++ char *mktemp (); ++ int u; ++ ++ isbinary = newline_added = 0; ++ u = umask(077); ++ if ((sfp = tmpfile()) == NULL) ++ { ++ fprintf (stderr, "open_sbuf: tmpfile() failed with '%s'\n", strerror (errno)); ++ sprintf (errmsg, "Cannot open temp file"); ++ umask(u); ++ return ERR; ++ } ++ umask(u); ++ return 0; ++} ++ ++ ++/* close_sbuf: close scratch file */ ++int ++close_sbuf () ++{ ++ if (sfp) ++ { ++ if (fclose (sfp) < 0) ++ { ++ fprintf (stderr, "close_sbuf: fclose on temporary file failed with '%s'.\n", strerror (errno)); ++ sprintf (errmsg, "Cannot close temp file"); ++ return ERR; ++ } ++ sfp = NULL; ++ } ++ sfseek = seek_write = 0; ++ ++ return 0; ++} ++ ++ ++/* quit: remove_lines scratch file and exit */ ++void ++quit (n) ++ int n; ++{ ++ if (sfp) ++ { ++ fclose (sfp); ++ } ++ exit (n); ++} ++ ++ ++extern line_t yank_buffer_head; ++extern char *old_filename; ++unsigned char ctab[256]; /* character translation table */ ++ ++/* init_buffers: open scratch buffer; initialize line queue */ ++void ++init_buffers () ++{ ++ int i = 0; ++ ++ /* Read stdin one character at a time to avoid i/o contention ++ with shell escapes invoked by nonterminal input, e.g., ++ ed - < 0; us++) ++ *us = ctab[*us]; ++ return s; ++} ++ ++ ++#ifndef SIG_ERR ++# define SIG_ERR ((void (*)()) -1) ++#endif /* !SIG_ERR */ ++ ++void ++(*reliable_signal (sno, hndlr)) () ++ int sno; ++ void (*hndlr) (); ++{ ++#ifndef HAVE_SIGACTION ++ signal (sno, hndlr); ++#else ++ struct sigaction sa, osa; ++ ++ sa.sa_handler = hndlr; ++ sigemptyset (&sa.sa_mask); ++ sa.sa_flags = 0; ++#ifdef SA_RESTART ++ sa.sa_flags |= SA_RESTART; ++#endif ++ return (sigaction (sno, &sa, &osa) < 0) ? SIG_ERR : osa.sa_handler; ++#endif /* HAVE_SIGACTION */ ++} ++ ++ ++void ++signal_hup (signo) ++ int signo; ++{ ++ if (mutex) ++ sigflags |= (1 << (signo - 1)); ++ else ++ handle_hup (signo); ++} ++ ++ ++void ++signal_int (signo) ++ int signo; ++{ ++ if (mutex) ++ sigflags |= (1 << (signo - 1)); ++ else ++ handle_int (signo); ++} ++ ++ ++#ifdef HAVE_SIGSETJMP ++extern sigjmp_buf env; ++#else ++extern jmp_buf env; ++#endif ++extern int sigactive; ++ ++void ++handle_hup (signo) ++ int signo; ++{ ++ char *hup = NULL; /* hup filename */ ++ char *s; ++ int n; ++ ++ if (!sigactive) ++ quit (1); ++ sigflags &= ~(1 << (signo - 1)); ++ if (addr_last && write_file ("ed.hup", "w", 1, addr_last) < 0 && ++ (s = getenv ("HOME")) != NULL && ++ (n = strlen (s)) + 7 <= PATH_MAX && /* "ed.hup" + '/' */ ++ (hup = (char *) malloc (n + 8)) != NULL) ++ { ++ strcpy (hup, s); ++ if (n && hup[n - 1] != '/') ++ hup[n++] = '/'; ++ strcpy (hup + n, "ed.hup"); ++ write_file (hup, "w", 1, addr_last); ++ } ++ quit (2); ++} ++ ++ ++void ++handle_int (signo) ++ int signo; ++{ ++ if (!sigactive) ++ quit (1); ++ sigflags &= ~(1 << (signo - 1)); ++#ifdef HAVE_SIGSETJMP ++ siglongjmp (env, -1); ++#else ++ longjmp (env, -1); ++#endif ++} ++ ++ ++extern long rows; ++int cols = 72; /* wrap column */ ++ ++void ++handle_winch (signo) ++ int signo; ++{ ++#ifdef TIOCGWINSZ ++ struct winsize ws; /* window size structure */ ++#endif ++ ++ sigflags &= ~(1 << (signo - 1)); ++#ifdef TIOCGWINSZ ++ if (ioctl (0, TIOCGWINSZ, (char *) &ws) >= 0) ++ { ++ if (ws.ws_row > 2) ++ rows = ws.ws_row - 2; ++ if (ws.ws_col > 8) ++ cols = ws.ws_col - 8; ++ } ++#endif ++} ++ ++/* read_file: read a named file/pipe into the buffer; return line count */ ++long ++read_file (fn, n) ++ char *fn; ++ long n; ++{ ++ FILE *fp; ++ long size; ++ ++ ++ fp = (*fn == '!') ? popen (fn + 1, "r") : fopen (strip_escapes (fn), "r"); ++ if (fp == NULL) ++ { ++ fprintf (stderr, "%s: %s\n", fn, strerror (errno)); ++ sprintf (errmsg, "Cannot open input file"); ++ return ERR; ++ } ++ else if ((size = read_stream (fp, n)) < 0) ++ return ERR; ++ else if (((*fn == '!') ? pclose (fp) : fclose (fp)) < 0) ++ { ++ fprintf (stderr, "%s: %s\n", fn, strerror (errno)); ++ sprintf (errmsg, "Cannot close input file"); ++ return ERR; ++ } ++ fprintf (stderr, !scripted ? "%lu\n" : "", size); ++ return current_addr - n; ++} ++ ++ ++char *sbuf; /* file i/o buffer */ ++int sbufsz; /* file i/o buffer size */ ++int newline_added; /* if set, newline appended to input file */ ++ ++/* read_stream: read a stream into the editor buffer; return status */ ++long ++read_stream (fp, n) ++ FILE *fp; ++ long n; ++{ ++ line_t *lp = get_addressed_line_node (n); ++ undo_t *up = NULL; ++ unsigned long size = 0; ++ int o_newline_added = newline_added; ++ int o_isbinary = isbinary; ++ int o_n = n; ++ int appended = n == addr_last; ++ int len; ++ ++ isbinary = newline_added = 0; ++ for (current_addr = n; (len = get_stream_line (fp)) > 0; size += len) ++ { ++ SPL1 (); ++ if (put_sbuf_line (sbuf) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ lp = lp->q_forw; ++ if (up) ++ up->t = lp; ++ else if ((up = push_undo_stack (UADD, current_addr, ++ current_addr)) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ SPL0 (); ++ } ++ if (len < 0) ++ return ERR; ++ if (o_n && appended && size && o_isbinary && o_newline_added) ++ fputs ("Newline inserted\n", stderr); ++ else if (newline_added && (!appended || (!isbinary && !o_isbinary))) ++ fputs ("Newline appended\n", stderr); ++ if (isbinary && newline_added && !appended) ++ size += 1; ++ if (!size) ++ newline_added = 1; ++ newline_added = appended ? newline_added : o_newline_added; ++ isbinary = isbinary | o_isbinary; ++ return size; ++} ++ ++ ++/* get_stream_line: read a line of text from a stream; return line length */ ++int ++get_stream_line (fp) ++ FILE *fp; ++{ ++ register int c; ++ register int i = 0; ++ ++ while (((c = getc (fp)) != EOF || (!feof (fp) && ++ !ferror (fp))) && c != '\n') ++ { ++ REALLOC (sbuf, sbufsz, i + 1, ERR); ++ if (!(sbuf[i++] = c)) ++ isbinary = 1; ++ } ++ REALLOC (sbuf, sbufsz, i + 2, ERR); ++ if (c == '\n') ++ sbuf[i++] = c; ++ else if (ferror (fp)) ++ { ++ fprintf (stderr, "%s\n", strerror (errno)); ++ sprintf (errmsg, "Cannot read input file"); ++ return ERR; ++ } ++ else if (i) ++ { ++ sbuf[i++] = '\n'; ++ newline_added = 1; ++ } ++ sbuf[i] = '\0'; ++ return (isbinary && newline_added && i) ? --i : i; ++} ++ ++ ++/* write_file: write a range of lines to a named file/pipe; return line count */ ++long ++write_file (fn, mode, n, m) ++ char *fn; ++ char *mode; ++ long n; ++ long m; ++{ ++ FILE *fp; ++ long size; ++ ++ fp = (*fn == '!') ? popen (fn + 1, "w") : fopen (strip_escapes (fn), mode); ++ if (fp == NULL) ++ { ++ fprintf (stderr, "%s: %s\n", fn, strerror (errno)); ++ sprintf (errmsg, "Cannot open output file"); ++ return ERR; ++ } ++ else if ((size = write_stream (fp, n, m)) < 0) ++ return ERR; ++ else if (((*fn == '!') ? pclose (fp) : fclose (fp)) < 0) ++ { ++ fprintf (stderr, "%s: %s\n", fn, strerror (errno)); ++ sprintf (errmsg, "Cannot close output file"); ++ return ERR; ++ } ++ fprintf (stderr, !scripted ? "%lu\n" : "", size); ++ return n ? m - n + 1 : 0; ++} ++ ++ ++/* write_stream: write a range of lines to a stream; return status */ ++long ++write_stream (fp, n, m) ++ FILE *fp; ++ long n; ++ long m; ++{ ++ line_t *lp = get_addressed_line_node (n); ++ unsigned long size = 0; ++ char *s; ++ int len; ++ ++ for (; n && n <= m; n++, lp = lp->q_forw) ++ { ++ if ((s = get_sbuf_line (lp)) == NULL) ++ return ERR; ++ len = lp->len; ++ if (n != addr_last || !isbinary || !newline_added) ++ s[len++] = '\n'; ++ if (put_stream_line (fp, s, len) < 0) ++ return ERR; ++ size += len; ++ } ++ return size; ++} ++ ++ ++/* put_stream_line: write a line of text to a stream; return status */ ++int ++put_stream_line (fp, s, len) ++ FILE *fp; ++ char *s; ++ int len; ++{ ++ while (len--) ++ if (fputc (*s++, fp) < 0) ++ { ++ fprintf (stderr, "%s\n", strerror (errno)); ++ sprintf (errmsg, "Cannot write file"); ++ return ERR; ++ } ++ return 0; ++} ++ ++/* get_extended_line: get an extended line from stdin */ ++char * ++get_extended_line (sizep, nonl) ++ int *sizep; ++ int nonl; ++{ ++ static char *cvbuf = NULL; /* buffer */ ++ static int cvbufsz = 0; /* buffer size */ ++ ++ int l, n; ++ char *t = ibufp; ++ ++ while (*t++ != '\n') ++ ; ++ if ((l = t - ibufp) < 2 || !has_trailing_escape (ibufp, ibufp + l - 1)) ++ { ++ *sizep = l; ++ return ibufp; ++ } ++ *sizep = -1; ++ REALLOC (cvbuf, cvbufsz, l, NULL); ++ memcpy (cvbuf, ibufp, l); ++ *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ ++ if (nonl) ++ l--; /* strip newline */ ++ for (;;) ++ { ++ if ((n = get_tty_line ()) < 0) ++ return NULL; ++ else if (n == 0 || ibuf[n - 1] != '\n') ++ { ++ sprintf (errmsg, "Unexpected end-of-file"); ++ return NULL; ++ } ++ REALLOC (cvbuf, cvbufsz, l + n, NULL); ++ memcpy (cvbuf + l, ibuf, n); ++ l += n; ++ if (n < 2 || !has_trailing_escape (cvbuf, cvbuf + l - 1)) ++ break; ++ *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ ++ if (nonl) ++ l--; /* strip newline */ ++ } ++ REALLOC (cvbuf, cvbufsz, l + 1, NULL); ++ cvbuf[l] = '\0'; ++ *sizep = l; ++ return cvbuf; ++} ++ ++ ++/* get_tty_line: read a line of text from stdin; return line length */ ++int ++get_tty_line () ++{ ++ register int oi = 0; ++ register int i = 0; ++ int c; ++ ++ /* Read stdin one character at a time to avoid i/o contention ++ with shell escapes invoked by nonterminal input, e.g., ++ ed - < cols) ++ { ++ fputs ("\\\n", stdout); ++ col = 1; ++ if (!traditional && !scripted && !isglobal && ++dlcnt > rows) ++ { ++ dlcnt = 0; ++ fputs ("Press to continue... ", stdout); ++ fflush (stdout); ++ if (get_tty_line () < 0) ++ return ERR; ++ } ++ } ++ if (gflag & GLS) ++ { ++ if (31 < *s && *s < 127 && *s != '\\') ++ putchar (*s); ++ else ++ { ++ putchar ('\\'); ++ col++; ++ if (*s && (cp = strchr (ESCAPES, *s)) != NULL) ++ putchar (ESCCHARS[cp - ESCAPES]); ++ else ++ { ++ putchar ((((unsigned char) *s & 0300) >> 6) + '0'); ++ putchar ((((unsigned char) *s & 070) >> 3) + '0'); ++ putchar (((unsigned char) *s & 07) + '0'); ++ col += 2; ++ } ++ } ++ ++ } ++ else ++ putchar (*s); ++ } ++ if (!traditional && (gflag & GLS)) ++ putchar ('$'); ++ putchar ('\n'); ++ return 0; ++} ++ ++ ++ ++ ++char *rhbuf; /* rhs substitution buffer */ ++int rhbufsz; /* rhs substitution buffer size */ ++int rhbufi; /* rhs substitution buffer index */ ++ ++/* extract_subst_tail: extract substitution tail from the command buffer */ ++int ++extract_subst_tail (flagp, np) ++ int *flagp; ++ int *np; ++{ ++ char delimiter; ++ ++ *flagp = *np = 0; ++ if ((delimiter = *ibufp) == '\n') ++ { ++ rhbufi = 0; ++ *flagp = GPR; ++ return 0; ++ } ++ else if (extract_subst_template () == NULL) ++ return ERR; ++ else if (*ibufp == '\n') ++ { ++ *flagp = GPR; ++ return 0; ++ } ++ else if (*ibufp == delimiter) ++ ibufp++; ++ if ('1' <= *ibufp && *ibufp <= '9') ++ { ++ STRTOL (*np, ibufp); ++ return 0; ++ } ++ else if (*ibufp == 'g') ++ { ++ ibufp++; ++ *flagp = GSG; ++ return 0; ++ } ++ return 0; ++} ++ ++ ++/* extract_subst_template: return pointer to copy of substitution template ++ in the command buffer */ ++char * ++extract_subst_template () ++{ ++ int n = 0; ++ int i = 0; ++ char c; ++ char delimiter = *ibufp++; ++ ++ if (*ibufp == '%' && *(ibufp + 1) == delimiter) ++ { ++ ibufp++; ++ if (!rhbuf) ++ sprintf (errmsg, "No previous substitution"); ++ return rhbuf; ++ } ++ while (*ibufp != delimiter) ++ { ++ REALLOC (rhbuf, rhbufsz, i + 2, NULL); ++ if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0') ++ { ++ i--, ibufp--; ++ break; ++ } ++ else if (c != '\\') ++ ; ++ else if ((rhbuf[i++] = *ibufp++) != '\n') ++ ; ++ else if (!isglobal) ++ { ++ while ((n = get_tty_line ()) == 0 || ++ (n > 0 && ibuf[n - 1] != '\n')) ++ clearerr (stdin); ++ if (n < 0) ++ return NULL; ++ } ++ } ++ REALLOC (rhbuf, rhbufsz, i + 1, NULL); ++ rhbuf[rhbufi = i] = '\0'; ++ return rhbuf; ++} ++ ++ ++char *rbuf; /* substitute_matching_text buffer */ ++int rbufsz; /* substitute_matching_text buffer size */ ++ ++/* search_and_replace: for each line in a range, change text matching a pattern ++ according to a substitution template; return status */ ++int ++search_and_replace (pat, gflag, kth) ++ pattern_t *pat; ++ int gflag; ++ int kth; ++{ ++ undo_t *up; ++ char *txt; ++ char *eot; ++ long lc; ++ int nsubs = 0; ++ line_t *lp; ++ int len; ++ ++ current_addr = first_addr - 1; ++ for (lc = 0; lc <= second_addr - first_addr; lc++) ++ { ++ lp = get_addressed_line_node (++current_addr); ++ if ((len = substitute_matching_text (pat, lp, gflag, kth)) < 0) ++ return ERR; ++ else if (len) ++ { ++ up = NULL; ++ if (delete_lines (current_addr, current_addr) < 0) ++ return ERR; ++ txt = rbuf; ++ eot = rbuf + len; ++ SPL1 (); ++ do ++ { ++ if ((txt = put_sbuf_line (txt)) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ else if (up) ++ up->t = get_addressed_line_node (current_addr); ++ else if ((up = push_undo_stack (UADD, ++ current_addr, current_addr)) == NULL) ++ { ++ SPL0 (); ++ return ERR; ++ } ++ } ++ while (txt != eot); ++ SPL0 (); ++ nsubs++; ++ } ++ } ++ if (nsubs == 0 && !(gflag & GLB)) ++ { ++ sprintf (errmsg, "No match"); ++ return ERR; ++ } ++ else if ((gflag & (GPR | GLS | GNP)) && ++ display_lines (current_addr, current_addr, gflag) < 0) ++ return ERR; ++ return 0; ++} ++ ++ ++/* substitute_matching_text: replace text matched by a pattern according to ++ a substitution template; return pointer to the modified text */ ++int ++substitute_matching_text (pat, lp, gflag, kth) ++ pattern_t *pat; ++ line_t *lp; ++ int gflag; ++ int kth; ++{ ++ int off = 0; ++ int changed = 0; ++ int matchno = 0; ++ int i = 0; ++ regmatch_t rm[SE_MAX]; ++ char *txt; ++ char *eot; ++ ++ if ((txt = get_sbuf_line (lp)) == NULL) ++ return ERR; ++ if (isbinary) ++ NUL_TO_NEWLINE (txt, lp->len); ++ eot = txt + lp->len; ++ if (!regexec (pat, txt, SE_MAX, rm, 0)) ++ { ++ do ++ { ++ if (!kth || kth == ++matchno) ++ { ++ changed++; ++ i = rm[0].rm_so; ++ REALLOC (rbuf, rbufsz, off + i, ERR); ++ if (isbinary) ++ NEWLINE_TO_NUL (txt, rm[0].rm_eo); ++ memcpy (rbuf + off, txt, i); ++ off += i; ++ if ((off = apply_subst_template (txt, rm, off, ++ pat->re_nsub)) < 0) ++ return ERR; ++ } ++ else ++ { ++ i = rm[0].rm_eo; ++ REALLOC (rbuf, rbufsz, off + i, ERR); ++ if (isbinary) ++ NEWLINE_TO_NUL (txt, i); ++ memcpy (rbuf + off, txt, i); ++ off += i; ++ } ++ txt += rm[0].rm_eo; ++ } ++ while (*txt && (!changed || ((gflag & GSG) && rm[0].rm_eo)) && ++ !regexec (pat, txt, SE_MAX, rm, REG_NOTBOL)); ++ i = eot - txt; ++ REALLOC (rbuf, rbufsz, off + i + 2, ERR); ++ if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) ++ { ++ sprintf (errmsg, "Infinite substitution loop"); ++ return ERR; ++ } ++ if (isbinary) ++ NEWLINE_TO_NUL (txt, i); ++ memcpy (rbuf + off, txt, i); ++ memcpy (rbuf + off + i, "\n", 2); ++ } ++ return changed ? off + i + 1 : 0; ++} ++ ++ ++/* apply_subst_template: modify text according to a substitution template; ++ return offset to end of modified text */ ++int ++apply_subst_template (boln, rm, off, re_nsub) ++ char *boln; ++ regmatch_t *rm; ++ int off; ++ int re_nsub; ++{ ++ int j = 0; ++ int k = 0; ++ int n; ++ char *sub = rhbuf; ++ ++ for (; sub - rhbuf < rhbufi; sub++) ++ if (*sub == '&') ++ { ++ j = rm[0].rm_so; ++ k = rm[0].rm_eo; ++ REALLOC (rbuf, rbufsz, off + k - j, ERR); ++ while (j < k) ++ rbuf[off++] = boln[j++]; ++ } ++ else if (*sub == '\\' && '1' <= *++sub && *sub <= '9' && ++ (n = *sub - '0') <= re_nsub) ++ { ++ j = rm[n].rm_so; ++ k = rm[n].rm_eo; ++ REALLOC (rbuf, rbufsz, off + k - j, ERR); ++ while (j < k) ++ rbuf[off++] = boln[j++]; ++ } ++ else ++ { ++ REALLOC (rbuf, rbufsz, off + 1, ERR); ++ rbuf[off++] = *sub; ++ } ++ REALLOC (rbuf, rbufsz, off + 1, ERR); ++ rbuf[off] = '\0'; ++ return off; ++} ++ ++ ++ ++#define USIZE 100 /* undo stack size */ ++undo_t *ustack = NULL; /* undo stack */ ++long usize = 0; /* stack size variable */ ++long u_p = 0; /* undo stack pointer */ ++ ++/* push_undo_stack: return pointer to intialized undo node */ ++undo_t * ++push_undo_stack (type, from, to) ++ int type; ++ long from; ++ long to; ++{ ++ undo_t *t; ++ ++ if ((t = ustack) == NULL && ++ (t = ustack = (undo_t *) malloc ((usize = USIZE) * sizeof (undo_t))) == NULL) ++ { ++ fprintf (stderr, "%s\n", strerror (errno)); ++ sprintf (errmsg, "Out of memory"); ++ return NULL; ++ } ++ else if (u_p >= usize && ++ (t = (undo_t *) realloc (ustack, (usize += USIZE) * sizeof (undo_t))) == NULL) ++ { ++ /* out of memory - release undo stack */ ++ fprintf (stderr, "%s\n", strerror (errno)); ++ sprintf (errmsg, "Out of memory"); ++ clear_undo_stack (); ++ free (ustack); ++ ustack = NULL; ++ usize = 0; ++ return NULL; ++ } ++ ustack = t; ++ ustack[u_p].type = type; ++ ustack[u_p].t = get_addressed_line_node (to); ++ ustack[u_p].h = get_addressed_line_node (from); ++ return ustack + u_p++; ++} ++ ++ ++/* USWAP: swap undo nodes */ ++#define USWAP(x, y) \ ++ do \ ++ { \ ++ undo_t utmp; \ ++ utmp = (x), (x) = (y), (y) = utmp; \ ++ } \ ++ while (0) ++ ++ ++long u_current_addr = -1; /* if >= 0, undo enabled */ ++long u_addr_last = -1; /* if >= 0, undo enabled */ ++ ++/* pop_undo_stack: undo last change to the editor buffer */ ++int ++pop_undo_stack () ++{ ++ long n; ++ long o_current_addr = current_addr; ++ long o_addr_last = addr_last; ++ ++ if (u_current_addr == -1 || u_addr_last == -1) ++ { ++ sprintf (errmsg, "Nothing to undo"); ++ return ERR; ++ } ++ else if (u_p) ++ modified = 1; ++ get_addressed_line_node (0); /* this get_addressed_line_node last! */ ++ SPL1 (); ++ for (n = u_p; n-- > 0;) ++ { ++ switch (ustack[n].type) ++ { ++ case UADD: ++ REQUE (ustack[n].h->q_back, ustack[n].t->q_forw); ++ break; ++ case UDEL: ++ REQUE (ustack[n].h->q_back, ustack[n].h); ++ REQUE (ustack[n].t, ustack[n].t->q_forw); ++ break; ++ case UMOV: ++ case VMOV: ++ REQUE (ustack[n - 1].h, ustack[n].h->q_forw); ++ REQUE (ustack[n].t->q_back, ustack[n - 1].t); ++ REQUE (ustack[n].h, ustack[n].t); ++ n--; ++ break; ++ default: ++ /*NOTREACHED */ ++ ; ++ } ++ ustack[n].type ^= 1; ++ } ++ /* reverse undo stack order */ ++ for (n = u_p; n-- > (u_p + 1) / 2;) ++ USWAP (ustack[n], ustack[u_p - 1 - n]); ++ if (isglobal) ++ clear_active_list (); ++ current_addr = u_current_addr, u_current_addr = o_current_addr; ++ addr_last = u_addr_last, u_addr_last = o_addr_last; ++ SPL0 (); ++ return 0; ++} ++ ++ ++/* clear_undo_stack: clear the undo stack */ ++void ++clear_undo_stack () ++{ ++ line_t *lp, *ep, *tl; ++ ++ while (u_p--) ++ if (ustack[u_p].type == UDEL) ++ { ++ ep = ustack[u_p].t->q_forw; ++ for (lp = ustack[u_p].h; lp != ep; lp = tl) ++ { ++ unmark_line_node (lp); ++ tl = lp->q_forw; ++ free (lp); ++ } ++ } ++ u_p = 0; ++ u_current_addr = current_addr; ++ u_addr_last = addr_last; ++} ++ ++ ++ ++ ++/* build_active_list: add line matching a pattern to the global-active list */ ++int ++build_active_list (isgcmd) ++ int isgcmd; ++{ ++ pattern_t *pat; ++ line_t *lp; ++ long n; ++ char *s; ++ char delimiter; ++ ++ if ((delimiter = *ibufp) == ' ' || delimiter == '\n') ++ { ++ sprintf (errmsg, "Invalid pattern delimiter"); ++ return ERR; ++ } ++ else if ((pat = get_compiled_pattern ()) == NULL) ++ return ERR; ++ else if (*ibufp == delimiter) ++ ibufp++; ++ clear_active_list (); ++ lp = get_addressed_line_node (first_addr); ++ for (n = first_addr; n <= second_addr; n++, lp = lp->q_forw) ++ { ++ if ((s = get_sbuf_line (lp)) == NULL) ++ return ERR; ++ if (isbinary) ++ NUL_TO_NEWLINE (s, lp->len); ++ if (!regexec (pat, s, 0, NULL, 0) == isgcmd && ++ set_active_node (lp) < 0) ++ return ERR; ++ } ++ return 0; ++} ++ ++ ++/* exec_global: apply command list in the command buffer to the active ++ lines in a range; return command status */ ++long ++exec_global (interact, gflag) ++ int interact; ++ int gflag; ++{ ++ static char *ocmd = NULL; ++ static int ocmdsz = 0; ++ ++ line_t *lp = NULL; ++ int status; ++ int n; ++ char *cmd = NULL; ++ ++ if (!interact) ++ if (traditional && !strcmp (ibufp, "\n")) ++ cmd = "p\n"; /* null cmd-list == `p' */ ++ else if ((cmd = get_extended_line (&n, 0)) == NULL) ++ return ERR; ++ clear_undo_stack (); ++ while ((lp = next_active_node ()) != NULL) ++ { ++ if ((current_addr = get_line_node_addr (lp)) < 0) ++ return ERR; ++ if (interact) ++ { ++ /* print current_addr; get a command in global syntax */ ++ if (display_lines (current_addr, current_addr, gflag) < 0) ++ return ERR; ++ while ((n = get_tty_line ()) > 0 && ++ ibuf[n - 1] != '\n') ++ clearerr (stdin); ++ if (n < 0) ++ return ERR; ++ else if (n == 0) ++ { ++ sprintf (errmsg, "Unexpected end-of-file"); ++ return ERR; ++ } ++ else if (n == 1 && !strcmp (ibuf, "\n")) ++ continue; ++ else if (n == 2 && !strcmp (ibuf, "&\n")) ++ { ++ if (cmd == NULL) ++ { ++ sprintf (errmsg, "No previous command"); ++ return ERR; ++ } ++ else ++ cmd = ocmd; ++ } ++ else if ((cmd = get_extended_line (&n, 0)) == NULL) ++ return ERR; ++ else ++ { ++ REALLOC (ocmd, ocmdsz, n + 1, ERR); ++ memcpy (ocmd, cmd, n + 1); ++ cmd = ocmd; ++ } ++ ++ } ++ ibufp = cmd; ++ for (; *ibufp;) ++ if ((status = extract_addr_range ()) < 0 || ++ (status = exec_command ()) < 0 || ++ (status > 0 && (status = display_lines ( ++ current_addr, current_addr, status)) < 0)) ++ return status; ++ } ++ return 0; ++} ++ ++ ++line_t **active_list; /* list of lines active in a global command */ ++long active_last; /* index of last active line in active_list */ ++long active_size; /* size of active_list */ ++long active_ptr; /* active_list index (non-decreasing) */ ++long active_ndx; /* active_list index (modulo active_last) */ ++ ++/* set_active_node: add a line node to the global-active list */ ++int ++set_active_node (lp) ++ line_t *lp; ++{ ++ if (active_last + 1 > active_size) ++ { ++ int ti = active_size; ++ line_t **ts; ++ SPL1 (); ++ if (active_list != NULL) ++ { ++ if ((ts = (line_t **) realloc (active_list, ++ (ti += MINBUFSZ) * sizeof (line_t **))) == NULL) ++ { ++ fprintf (stderr, "%s\n", strerror (errno)); ++ sprintf (errmsg, "Out of memory"); ++ SPL0 (); ++ return ERR; ++ } ++ } ++ else ++ { ++ if ((ts = (line_t **) malloc ((ti += MINBUFSZ) * ++ sizeof (line_t **))) == NULL) ++ { ++ fprintf (stderr, "%s\n", strerror (errno)); ++ sprintf (errmsg, "Out of memory"); ++ SPL0 (); ++ return ERR; ++ } ++ } ++ active_size = ti; ++ active_list = ts; ++ SPL0 (); ++ } ++ active_list[active_last++] = lp; ++ return 0; ++} ++ ++ ++/* unset_active_nodes: remove a range of lines from the global-active list */ ++void ++unset_active_nodes (np, mp) ++ line_t *np, *mp; ++{ ++ line_t *lp; ++ long i; ++ ++ for (lp = np; lp != mp; lp = lp->q_forw) ++ for (i = 0; i < active_last; i++) ++ if (active_list[active_ndx] == lp) ++ { ++ active_list[active_ndx] = NULL; ++ active_ndx = INC_MOD (active_ndx, active_last - 1); ++ break; ++ } ++ else ++ active_ndx = INC_MOD (active_ndx, active_last - 1); ++} ++ ++ ++/* next_active_node: return the next global-active line node */ ++line_t * ++next_active_node () ++{ ++ while (active_ptr < active_last && active_list[active_ptr] == NULL) ++ active_ptr++; ++ return (active_ptr < active_last) ? active_list[active_ptr++] : NULL; ++} ++ ++ ++/* clear_active_list: clear the global-active list */ ++void ++clear_active_list () ++{ ++ SPL1 (); ++ active_size = active_last = active_ptr = active_ndx = 0; ++ if (active_list != NULL) ++ free (active_list); ++ active_list = NULL; ++ SPL0 (); ++} ++ ++ ++ ++/* get_compiled_pattern: return pointer to compiled pattern from command ++ buffer */ ++pattern_t * ++get_compiled_pattern () ++{ ++ static pattern_t *exp = NULL; ++ ++ char *exps; ++ char delimiter; ++ int n; ++ ++ if ((delimiter = *ibufp) == ' ') ++ { ++ sprintf (errmsg, "Invalid pattern delimiter"); ++ return NULL; ++ } ++ else if (delimiter == '\n' || *++ibufp == '\n' || *ibufp == delimiter) ++ { ++ if (!exp) ++ sprintf (errmsg, "No previous pattern"); ++ return exp; ++ } ++ else if ((exps = extract_pattern (delimiter)) == NULL) ++ return NULL; ++ /* buffer alloc'd && not reserved */ ++ if (exp && !patlock) ++ regfree (exp); ++ else if ((exp = (pattern_t *) malloc (sizeof (pattern_t))) == NULL) ++ { ++ fprintf (stderr, "%s\n", strerror (errno)); ++ sprintf (errmsg, "Out of memory"); ++ return NULL; ++ } ++ patlock = 0; ++ if ((n = regcomp (exp, exps, 0))) ++ { ++ regerror (n, exp, errmsg, ERRSZ); ++ free (exp); ++ return exp = NULL; ++ } ++ return exp; ++} ++ ++ ++/* extract_pattern: copy a pattern string from the command buffer; return ++ pointer to the copy */ ++char * ++extract_pattern (delimiter) ++ int delimiter; ++{ ++ static char *lhbuf = NULL; /* buffer */ ++ static int lhbufsz = 0; /* buffer size */ ++ ++ char *nd; ++ int len; ++ ++ for (nd = ibufp; *nd != delimiter && *nd != '\n'; nd++) ++ switch (*nd) ++ { ++ default: ++ break; ++ case '[': ++ if ((nd = parse_char_class (++nd)) == NULL) ++ { ++ sprintf (errmsg, "Unbalanced brackets ([])"); ++ return NULL; ++ } ++ break; ++ case '\\': ++ if (*++nd == '\n') ++ { ++ sprintf (errmsg, "Trailing backslash (\\)"); ++ return NULL; ++ } ++ break; ++ } ++ len = nd - ibufp; ++ REALLOC (lhbuf, lhbufsz, len + 1, NULL); ++ memcpy (lhbuf, ibufp, len); ++ lhbuf[len] = '\0'; ++ ibufp = nd; ++ return (isbinary) ? NUL_TO_NEWLINE (lhbuf, len) : lhbuf; ++} ++ ++ ++/* parse_char_class: expand a POSIX character class */ ++char * ++parse_char_class (s) ++ char *s; ++{ ++ int c, d; ++ ++ if (*s == '^') ++ s++; ++ if (*s == ']') ++ s++; ++ for (; *s != ']' && *s != '\n'; s++) ++ if (*s == '[' && ((d = *(s + 1)) == '.' || d == ':' || d == '=')) ++ for (s++, c = *++s; *s != ']' || c != d; s++) ++ if ((c = *s) == '\n') ++ return NULL; ++ return (*s == ']') ? s : NULL; ++} +--- /dev/null 2005-04-22 11:15:01.120978184 -0400 ++++ editors/ed.h 2005-04-22 11:15:09.000000000 -0400 +@@ -0,0 +1,256 @@ ++/* ed.h: type and constant definitions for the ed editor. */ ++/* ed line editor. ++ Copyright (C) 1993, 1994 Andrew Moore, Talke Studio ++ All Rights Reserved ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2, or (at your option) ++ any later version. ++ ++ This program is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++ @(#)$Id: ed.h,v 1.14 1994/11/13 04:25:44 alm Exp $ ++*/ ++ ++#include ++ ++#define ERR (-2) ++#define EMOD (-3) ++#define FATAL (-4) ++ ++#define ERRSZ (PATH_MAX + 40) /* size of error message buffer */ ++#define MINBUFSZ 512 /* minimum buffer size: must be > 0 */ ++#define SE_MAX 30 /* max subexpressions in a regular expression */ ++ ++#define LINECHARS INT_MAX /* max chars per line */ ++ ++/* gflags */ ++#define GLB 001 /* global command */ ++#define GPR 002 /* print after command */ ++#define GLS 004 /* list after command */ ++#define GNP 010 /* enumerate after command */ ++#define GSG 020 /* global substitute */ ++ ++typedef regex_t pattern_t; ++ ++/* Line node */ ++typedef struct line ++ { ++ struct line *q_forw; ++ struct line *q_back; ++ off_t seek; /* address of line in scratch buffer */ ++ int len; /* length of line */ ++ } ++line_t; ++ ++ ++typedef struct undo ++ { ++ ++/* type of undo nodes */ ++#define UADD 0 ++#define UDEL 1 ++#define UMOV 2 ++#define VMOV 3 ++ ++ int type; /* command type */ ++ line_t *h; /* head of list */ ++ line_t *t; /* tail of list */ ++ } ++undo_t; ++ ++#define INC_MOD(l, k) ((l) + 1 > (k) ? 0 : (l) + 1) ++#define DEC_MOD(l, k) ((l) - 1 < 0 ? (k) : (l) - 1) ++ ++/* SPL1: disable some interrupts (requires reliable signals) */ ++#define SPL1() mutex++ ++ ++/* SPL0: enable all interrupts; check sigflags (requires reliable signals) */ ++#define SPL0() \ ++ do \ ++ { \ ++ if (--mutex == 0) \ ++ { \ ++ if (sigflags & (1 << (SIGHUP - 1))) handle_hup (SIGHUP); \ ++ if (sigflags & (1 << (SIGINT - 1))) handle_int (SIGINT); \ ++ } \ ++ } \ ++ while (0) ++ ++/* STRTOL: convert a string to long */ ++#define STRTOL(i, p) \ ++ do \ ++ { \ ++ if ((((i) = strtol ((p), &(p), 10)) == LONG_MIN \ ++ || (i) == LONG_MAX) && errno == ERANGE) \ ++ { \ ++ sprintf (errmsg, "Number out of range"); \ ++ (i) = 0; \ ++ return ERR; \ ++ } \ ++ } \ ++ while (0) ++ ++/* REALLOC: assure at least a minimum size for buffer b */ ++#define REALLOC(b, n, i, err) \ ++ do \ ++ { \ ++ if ((i) > (n)) \ ++ { \ ++ int ti = (n); \ ++ char *ts; \ ++ SPL1 (); \ ++ if ((b) != NULL) \ ++ { \ ++ if ((ts = (char *) realloc ((b), ti += max ((i), MINBUFSZ))) \ ++ == NULL) \ ++ { \ ++ fprintf (stderr, "%s\n", strerror (errno)); \ ++ sprintf (errmsg, "Out of memory"); \ ++ SPL0 (); \ ++ return err; \ ++ } \ ++ } \ ++ else \ ++ { \ ++ if ((ts = (char *) malloc (ti += max ((i), MINBUFSZ))) \ ++ == NULL) \ ++ { \ ++ fprintf (stderr, "%s\n", strerror (errno)); \ ++ sprintf (errmsg, "Out of memory"); \ ++ SPL0 (); \ ++ return err; \ ++ } \ ++ } \ ++ (n) = ti; \ ++ (b) = ts; \ ++ SPL0 (); \ ++ } \ ++ } \ ++ while (0) ++ ++/* REQUE: link pred before succ */ ++#define REQUE(pred, succ) \ ++ ((pred)->q_forw = (succ), (succ)->q_back = (pred)) ++ ++/* INSQUE: insert elem in circular queue after pred */ ++#define INSQUE(elem, pred) \ ++ do \ ++ { \ ++ REQUE ((elem), (pred)->q_forw); \ ++ REQUE ((pred), (elem)); \ ++ } \ ++ while (0) ++ ++/* REMQUE: remove elem from circular queue */ ++#define REMQUE(elem) \ ++ REQUE ((elem)->q_back, (elem)->q_forw) ++ ++/* NUL_TO_NEWLINE: overwrite ASCII NULs with newlines */ ++#define NUL_TO_NEWLINE(s, l) translit_text(s, l, '\0', '\n') ++ ++/* NEWLINE_TO_NUL: overwrite newlines with ASCII NULs */ ++#define NEWLINE_TO_NUL(s, l) translit_text(s, l, '\n', '\0') ++ ++/* Local Function Declarations */ ++void add_line_node __P ((line_t *)); ++int append_lines __P ((long)); ++int apply_subst_template __P ((char *, regmatch_t *, int, int)); ++int build_active_list __P ((int)); ++int cbc_decode __P ((char *, FILE *)); ++int cbc_encode __P ((char *, int, FILE *)); ++int check_addr_range __P ((long, long)); ++void clear_active_list __P ((void)); ++void clear_undo_stack __P ((void)); ++int close_sbuf __P ((void)); ++int copy_lines __P ((long)); ++int delete_lines __P ((long, long)); ++void delete_yank_lines __P ((void)); ++int display_lines __P ((long, long, int)); ++line_t *dup_line_node __P ((line_t *)); ++int exec_command __P ((void)); ++long exec_global __P ((int, int)); ++int extract_addr_range __P ((void)); ++char *extract_pattern __P ((int)); ++int extract_subst_tail __P ((int *, int *)); ++char *extract_subst_template __P ((void)); ++int filter_lines __P ((long, long, char *)); ++line_t *get_addressed_line_node __P ((long)); ++pattern_t *get_compiled_pattern __P ((void)); ++char *get_extended_line __P ((int *, int)); ++char *get_filename __P ((void)); ++int get_keyword __P ((void)); ++long get_line_node_addr __P ((line_t *)); ++long get_matching_node_addr __P ((pattern_t *, int)); ++long get_marked_node_addr __P ((int)); ++char *get_sbuf_line __P ((line_t *)); ++int get_shell_command __P ((void)); ++int get_stream_line __P ((FILE *)); ++int get_tty_line __P ((void)); ++void handle_hup __P ((int)); ++void handle_int __P ((int)); ++void handle_winch __P ((int)); ++int has_trailing_escape __P ((char *, char *)); ++int hex_to_binary __P ((int, int)); ++void init_buffers __P ((void)); ++int is_legal_filename __P ((char *)); ++int is_regular_file __P ((int)); ++int join_lines __P ((long, long)); ++int mark_line_node __P ((line_t *, int)); ++int move_lines __P ((long)); ++line_t *next_active_node (); ++long next_addr __P ((void)); ++int open_sbuf __P ((void)); ++char *parse_char_class __P ((char *)); ++int pop_undo_stack __P ((void)); ++undo_t *push_undo_stack __P ((int, long, long)); ++int put_lines __P ((long)); ++char *put_sbuf_line __P ((char *)); ++int put_stream_line __P ((FILE *, char *, int)); ++int put_tty_line __P ((char *, int, long, int)); ++void quit __P ((int)); ++long read_file __P ((char *, long)); ++long read_stream __P ((FILE *, long)); ++void (*reliable_signal __P ((int, void (*) ()))) (); ++int search_and_replace __P ((pattern_t *, int, int)); ++int set_active_node __P ((line_t *)); ++void signal_hup __P ((int)); ++void signal_int __P ((int)); ++char *strip_escapes __P ((char *)); ++int substitute_matching_text __P ((pattern_t *, line_t *, int, int)); ++char *translit_text __P ((char *, int, int, int)); ++void unmark_line_node __P ((line_t *)); ++void unset_active_nodes __P ((line_t *, line_t *)); ++long write_file __P ((char *, char *, long, long)); ++long write_stream __P ((FILE *, long, long)); ++int yank_lines __P ((long, long)); ++ ++/* global buffers */ ++extern char stdinbuf[]; ++extern char *errmsg; ++extern char *ibuf; ++extern char *ibufp; ++extern int ibufsz; ++ ++/* global flags */ ++extern int isbinary; ++extern int isglobal; ++extern int modified; ++extern int mutex; ++extern int sigflags; ++extern int traditional; ++ ++/* global vars */ ++extern long addr_last; ++extern long current_addr; ++extern long first_addr; ++extern int lineno; ++extern long second_addr;