From cf131bb3280d555e40808c28366d0cb33c6a4497 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Sun, 24 Apr 2005 05:18:00 +0000 Subject: new version of ed taken from sash --- patches/ed.patch | 4284 +++++++++++++++--------------------------------------- 1 file changed, 1176 insertions(+), 3108 deletions(-) diff --git a/patches/ed.patch b/patches/ed.patch index fc261b88d..cc3d13853 100644 --- a/patches/ed.patch +++ b/patches/ed.patch @@ -59,3385 +59,1453 @@ Index: include/applets.h #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. -+*/ -+ +--- /dev/null 2005-04-24 01:00:01.350003056 -0400 ++++ editors/ed.c 2005-04-24 01:15:09.000000000 -0400 +@@ -0,0 +1,1447 @@ +/* -+ * 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. ++ * Copyright (c) 2002 by David I. Bell ++ * Permission is granted to use, distribute, or modify this source, ++ * provided that this copyright notice remains intact. + * ++ * The "ed" built-in command (much simplified) + */ + -+#include -+#include -+#include -+#include +#include ++#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[] = ++#include ++#include ++//#include "sash.h" ++ ++#define USERSIZE 1024 /* max line length typed in by user */ ++#define INITBUF_SIZE 1024 /* initial buffer size */ ++ ++typedef int BOOL; ++ ++#define FALSE ((BOOL) 0) ++#define TRUE ((BOOL) 1) ++ ++#define isBlank(ch) (((ch) == ' ') || ((ch) == '\t')) ++#define isDecimal(ch) (((ch) >= '0') && ((ch) <= '9')) ++ ++#define STDOUT 1 ++ ++typedef int NUM; ++typedef int LEN; ++ ++typedef struct LINE LINE; ++ ++struct LINE +{ -+ {"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}, ++ LINE * next; ++ LINE * prev; ++ LEN len; ++ char data[1]; +}; + -+extern int optind; -+extern char *optarg; + -+/* usage: explain usage */ ++static LINE lines; ++static LINE * curLine; ++static NUM curNum; ++static NUM lastNum; ++static NUM marks[26]; ++static BOOL dirty; ++static char * fileName; ++static char searchString[USERSIZE]; ++ ++static char * bufBase; ++static char * bufPtr; ++static LEN bufUsed; ++static LEN bufSize; ++ ++ ++static void doCommands(void); ++static void subCommand(const char * cmd, NUM num1, NUM num2); ++static BOOL getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum); ++static BOOL setCurNum(NUM num); ++static BOOL initEdit(void); ++static void termEdit(void); ++static void addLines(NUM num); ++static BOOL insertLine(NUM num, const char * data, LEN len); ++static BOOL deleteLines(NUM num1, NUM num2); ++static BOOL printLines(NUM num1, NUM num2, BOOL expandFlag); ++static BOOL writeLines(const char * file, NUM num1, NUM num2); ++static BOOL readLines(const char * file, NUM num); ++static NUM searchLines(const char * str, NUM num1, NUM num2); ++static LINE * findLine(NUM num); ++ ++static LEN findString ++ (const LINE * lp, const char * str, LEN len, LEN offset); ++ + -+#if 0 +void -+usage (status) -+ int status; ++ed_main(int argc, const char ** argv) +{ -+ 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() -+ ++ if (!initEdit()) ++ return; + -+/* 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) ++ if (argc > 1) + { -+ status = ERR; -+ continue; -+ } -+ else if (n == 0) -+ { -+ if (modified && !scripted) -+ { -+ fputs ("?\n", stderr); -+ sprintf (errmsg, "Warning: file modified"); -+ if (is_regular_file (0)) ++ fileName = strdup(argv[1]); ++ ++ if (fileName == NULL) + { -+ fprintf (stderr, garrulous ? -+ "script, line %d: %s\n" : -+ "", lineno, errmsg); -+ quit (2); ++ fprintf(stderr, "No memory\n"); ++ termEdit(); ++ ++ return; + } -+ 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; ++ if (!readLines(fileName, 1)) ++ { ++ termEdit(); + -+/* 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; -+} ++ return; ++ } + ++ if (lastNum) ++ setCurNum(1); + -+#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 */ ++ dirty = FALSE; ++ } ++ ++ doCommands(); ++ ++ termEdit(); +} + + -+/* 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 () ++/* ++ * Read commands until we are told to stop. ++ */ ++static void ++doCommands(void) +{ -+ 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 ++ const char * cp; ++ char * endbuf; ++ char * newname; ++ int len; ++ NUM num1; ++ NUM num2; ++ BOOL have1; ++ BOOL have2; ++ char buf[USERSIZE]; ++ ++ while (TRUE) + { -+ 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; ++ printf(": "); ++ fflush(stdout); ++ ++ if (fgets(buf, sizeof(buf), stdin) == NULL) ++ return; ++ ++ len = strlen(buf); ++ ++ if (len == 0) ++ return; ++ ++ endbuf = &buf[len - 1]; ++ ++ if (*endbuf != '\n') ++ { ++ fprintf(stderr, "Command line too long\n"); ++ ++ do ++ { ++ len = fgetc(stdin); ++ } ++ while ((len != EOF) && (len != '\n')); ++ ++ continue; + } -+ default: -+ if (sflags) ++ ++ while ((endbuf > buf) && isBlank(endbuf[-1])) ++ endbuf--; ++ ++ *endbuf = '\0'; ++ ++ cp = buf; ++ ++ while (isBlank(*cp)) ++ cp++; ++ ++ have1 = FALSE; ++ have2 = FALSE; ++ ++ if ((curNum == 0) && (lastNum > 0)) + { -+ sprintf (errmsg, "Invalid command suffix"); -+ return ERR; ++ curNum = 1; ++ curLine = lines.next; + } -+ } -+ } -+ 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; -+} + ++ if (!getNum(&cp, &have1, &num1)) ++ continue; + -+/* 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; -+} ++ while (isBlank(*cp)) ++ cp++; + ++ if (*cp == ',') ++ { ++ cp++; + -+/* 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; -+} ++ if (!getNum(&cp, &have2, &num2)) ++ continue; + ++ if (!have1) ++ num1 = 1; + -+/* get_filename: return pointer to copy of filename in the command buffer */ -+char * -+get_filename () -+{ -+ static char *file = NULL; -+ static int filesz = 0; ++ if (!have2) ++ num2 = lastNum; + -+ int n; ++ have1 = TRUE; ++ have2 = TRUE; ++ } + -+ 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; -+} ++ if (!have1) ++ num1 = curNum; + ++ if (!have2) ++ num2 = num1; + -+/* 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 == '%'; ++ switch (*cp++) ++ { ++ case 'a': ++ addLines(num1 + 1); ++ break; ++ ++ case 'c': ++ deleteLines(num1, num2); ++ addLines(num1); ++ break; ++ ++ case 'd': ++ deleteLines(num1, num2); ++ break; ++ ++ case 'f': ++ if (*cp && !isBlank(*cp)) ++ { ++ fprintf(stderr, "Bad file command\n"); ++ break; ++ } ++ ++ while (isBlank(*cp)) ++ cp++; ++ ++ if (*cp == '\0') ++ { ++ if (fileName) ++ printf("\"%s\"\n", fileName); ++ else ++ printf("No file name\n"); ++ ++ break; ++ } ++ ++ newname = strdup(cp); ++ ++ if (newname == NULL) ++ { ++ fprintf(stderr, "No memory for file name\n"); ++ break; ++ } ++ ++ if (fileName) ++ free(fileName); ++ ++ fileName = newname; ++ break; ++ ++ case 'i': ++ addLines(num1); ++ break; ++ ++ case 'k': ++ while (isBlank(*cp)) ++ cp++; ++ ++ if ((*cp < 'a') || (*cp > 'a') || cp[1]) ++ { ++ fprintf(stderr, "Bad mark name\n"); ++ break; ++ } ++ ++ marks[*cp - 'a'] = num2; ++ break; ++ ++ case 'l': ++ printLines(num1, num2, TRUE); ++ break; ++ ++ case 'p': ++ printLines(num1, num2, FALSE); ++ break; ++ ++ case 'q': ++ while (isBlank(*cp)) ++ cp++; ++ ++ if (have1 || *cp) ++ { ++ fprintf(stderr, "Bad quit command\n"); ++ break; ++ } ++ ++ if (!dirty) ++ return; ++ ++ printf("Really quit? "); ++ fflush(stdout); ++ ++ buf[0] = '\0'; ++ fgets(buf, sizeof(buf), stdin); ++ cp = buf; ++ ++ while (isBlank(*cp)) ++ cp++; ++ ++ if ((*cp == 'y') || (*cp == 'Y')) ++ return; ++ ++ break; ++ ++ case 'r': ++ if (*cp && !isBlank(*cp)) ++ { ++ fprintf(stderr, "Bad read command\n"); ++ break; ++ } ++ ++ while (isBlank(*cp)) ++ cp++; ++ ++ if (*cp == '\0') ++ { ++ fprintf(stderr, "No file name\n"); ++ break; ++ } ++ ++ if (!have1) ++ num1 = lastNum; ++ ++ if (readLines(cp, num1 + 1)) ++ break; ++ ++ if (fileName == NULL) ++ fileName = strdup(cp); ++ ++ break; ++ ++ case 's': ++ subCommand(cp, num1, num2); ++ break; ++ ++ case 'w': ++ if (*cp && !isBlank(*cp)) ++ { ++ fprintf(stderr, "Bad write command\n"); ++ break; ++ } ++ ++ while (isBlank(*cp)) ++ cp++; ++ ++ if (!have1) { ++ num1 = 1; ++ num2 = lastNum; ++ } ++ ++ if (*cp == '\0') ++ cp = fileName; ++ ++ if (cp == NULL) ++ { ++ fprintf(stderr, "No file name specified\n"); ++ break; ++ } ++ ++ writeLines(cp, num1, num2); ++ break; ++ ++ case 'z': ++ switch (*cp) ++ { ++ case '-': ++ printLines(curNum-21, curNum, FALSE); ++ break; ++ case '.': ++ printLines(curNum-11, curNum+10, FALSE); ++ break; ++ default: ++ printLines(curNum, curNum+21, FALSE); ++ break; ++ } ++ break; ++ ++ case '.': ++ if (have1) ++ { ++ fprintf(stderr, "No arguments allowed\n"); ++ break; ++ } ++ ++ printLines(curNum, curNum, FALSE); ++ break; ++ ++ case '-': ++ if (setCurNum(curNum - 1)) ++ printLines(curNum, curNum, FALSE); ++ ++ break; ++ ++ case '=': ++ printf("%d\n", num1); ++ break; ++ ++ case '\0': ++ if (have1) ++ { ++ printLines(num2, num2, FALSE); ++ break; ++ } ++ ++ if (setCurNum(curNum + 1)) ++ printLines(curNum, curNum, FALSE); ++ ++ break; ++ ++ default: ++ fprintf(stderr, "Unimplemented command\n"); ++ break; ++ } ++ } +} + + -+/* 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; ++/* ++ * Do the substitute command. ++ * The current line is set to the last substitution done. ++ */ ++static void ++subCommand(const char * cmd, NUM num1, NUM num2) +{ -+ 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 ++ int delim; ++ char * cp; ++ char * oldStr; ++ char * newStr; ++ LEN oldLen; ++ LEN newLen; ++ LEN deltaLen; ++ LEN offset; ++ LINE * lp; ++ LINE * nlp; ++ BOOL globalFlag; ++ BOOL printFlag; ++ BOOL didSub; ++ BOOL needPrint; ++ char buf[USERSIZE]; ++ ++ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) + { -+ while (*ibufp++ != '\n') -+ ; -+ l = ibufp - lp; ++ fprintf(stderr, "Bad line range for substitute\n"); ++ ++ return; + } -+ if (l == 2 && lp[0] == '.' && lp[1] == '\n') ++ ++ globalFlag = FALSE; ++ printFlag = FALSE; ++ didSub = FALSE; ++ needPrint = FALSE; ++ ++ /* ++ * Copy the command so we can modify it. ++ */ ++ strcpy(buf, cmd); ++ cp = buf; ++ ++ if (isBlank(*cp) || (*cp == '\0')) + { -+ return 0; ++ fprintf(stderr, "Bad delimiter for substitute\n"); ++ ++ return; + } -+ eot = lp + l; -+ SPL1 (); -+ do ++ ++ delim = *cp++; ++ oldStr = cp; ++ ++ cp = strchr(cp, delim); ++ ++ if (cp == NULL) + { -+ 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; -+ } ++ fprintf(stderr, "Missing 2nd delimiter for substitute\n"); ++ ++ return; + } -+ while (lp != eot); -+ modified = 1; -+ SPL0 (); -+ } -+ /* NOTREACHED */ -+} + ++ *cp++ = '\0'; + -+/* 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; -+} ++ newStr = cp; ++ cp = strchr(cp, delim); + ++ if (cp) ++ *cp++ = '\0'; ++ else ++ cp = ""; + -+/* 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) ++ while (*cp) switch (*cp++) + { -+ b1 = get_addressed_line_node (p); -+ b2 = get_addressed_line_node (addr); -+ /* this get_addressed_line_node last! */ ++ case 'g': ++ globalFlag = TRUE; ++ break; ++ ++ case 'p': ++ printFlag = TRUE; ++ break; ++ ++ default: ++ fprintf(stderr, "Unknown option for substitute\n"); ++ ++ return; + } -+ else ++ ++ if (*oldStr == '\0') + { -+ 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; -+} ++ if (searchString[0] == '\0') ++ { ++ fprintf(stderr, "No previous search string\n"); + ++ return; ++ } + -+/* 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; -+} ++ oldStr = searchString; ++ } + ++ if (oldStr != searchString) ++ strcpy(searchString, oldStr); + -+/* 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; -+} ++ lp = findLine(num1); + ++ if (lp == NULL) ++ return; + -+int dlcnt = 0; /* # of lines displayed */ ++ oldLen = strlen(oldStr); ++ newLen = strlen(newStr); ++ deltaLen = newLen - oldLen; ++ offset = 0; ++ nlp = NULL; + -+/* 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) ++ while (num1 <= num2) + { -+ dlcnt = 0; -+ fputs ("Press to continue... ", stdout); -+ fflush (stdout); -+ if (get_tty_line () < 0) -+ return ERR; -+ } -+ } -+ return 0; -+} ++ offset = findString(lp, oldStr, oldLen, offset); + ++ if (offset < 0) ++ { ++ if (needPrint) ++ { ++ printLines(num1, num1, FALSE); ++ needPrint = FALSE; ++ } + -+line_t yank_buffer_head; /* head of yank buffer */ ++ offset = 0; ++ lp = lp->next; ++ num1++; + -+/* 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; ++ continue; ++ } + ++ needPrint = printFlag; ++ didSub = TRUE; ++ dirty = TRUE; + -+ 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; -+} ++ /* ++ * If the replacement string is the same size or shorter ++ * than the old string, then the substitution is easy. ++ */ ++ if (deltaLen <= 0) ++ { ++ memcpy(&lp->data[offset], newStr, newLen); + ++ if (deltaLen) ++ { ++ memcpy(&lp->data[offset + newLen], ++ &lp->data[offset + oldLen], ++ lp->len - offset - oldLen); + -+/* delete_yank_lines: delete lines from the yank buffer */ -+void -+delete_yank_lines () -+{ -+ line_t *cp, *lp; ++ lp->len += deltaLen; ++ } + ++ offset += newLen; + -+ 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 (); -+ } -+} ++ if (globalFlag) ++ continue; + ++ if (needPrint) ++ { ++ printLines(num1, num1, FALSE); ++ needPrint = FALSE; ++ } + -+/* 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; -+} ++ lp = lp->next; ++ num1++; ++ ++ continue; ++ } + ++ /* ++ * The new string is larger, so allocate a new line ++ * structure and use that. Link it in in place of ++ * the old line structure. ++ */ ++ nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen); + -+#define MAXMARK 26 /* max number of marks */ ++ if (nlp == NULL) ++ { ++ fprintf(stderr, "Cannot get memory for line\n"); + -+line_t *mark[MAXMARK]; /* line markers */ -+int markno; /* line marker count */ ++ return; ++ } + -+/* 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; -+} ++ nlp->len = lp->len + deltaLen; + ++ memcpy(nlp->data, lp->data, offset); + -+/* 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']); -+} ++ memcpy(&nlp->data[offset], newStr, newLen); + ++ memcpy(&nlp->data[offset + newLen], ++ &lp->data[offset + oldLen], ++ lp->len - offset - oldLen); + -+/* 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--; -+ } -+} ++ nlp->next = lp->next; ++ nlp->prev = lp->prev; ++ nlp->prev->next = nlp; ++ nlp->next->prev = nlp; + ++ if (curLine == lp) ++ curLine = nlp; + -+/* 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; -+} ++ free(lp); ++ lp = nlp; + ++ offset += newLen; + -+/* 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); ++ if (globalFlag) ++ continue; ++ ++ if (needPrint) ++ { ++ printLines(num1, num1, FALSE); ++ needPrint = FALSE; ++ } ++ ++ lp = lp->next; ++ num1++; ++ } ++ ++ if (!didSub) ++ fprintf(stderr, "No substitutions found for \"%s\"\n", oldStr); +} + + -+/* strip_escapes: return copy of escaped string of at most length PATH_MAX */ -+char * -+strip_escapes (s) -+ char *s; ++/* ++ * Search a line for the specified string starting at the specified ++ * offset in the line. Returns the offset of the found string, or -1. ++ */ ++static LEN ++findString( const LINE * lp, const char * str, LEN len, LEN offset) +{ -+ static char *file = NULL; -+ static int filesz = 0; ++ LEN left; ++ const char * cp; ++ const char * ncp; + -+ int i = 0; ++ cp = &lp->data[offset]; ++ left = lp->len - offset; + -+ REALLOC (file, filesz, PATH_MAX + 1, NULL); -+ /* assert: no trailing escape */ -+ while ((file[i++] = (*s == '\\') ? *++s : *s)) -+ s++; -+ return file; -+} ++ while (left >= len) ++ { ++ ncp = memchr(cp, *str, left); + ++ if (ncp == NULL) ++ return -1; + -+#ifndef S_ISREG -+#define S_ISREG(m) ((m & S_IFMT) == S_IFREG) -+#endif ++ left -= (ncp - cp); + -+/* 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; ++ if (left < len) ++ return -1; + -+ return fstat (fd, &sb) < 0 || S_ISREG (sb.st_mode); -+} ++ cp = ncp; + ++ if (memcmp(cp, str, len) == 0) ++ return (cp - lp->data); + -+/* 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; ++ cp++; ++ left--; ++ } ++ ++ 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; ++/* ++ * Add lines which are typed in by the user. ++ * The lines are inserted just before the specified line number. ++ * The lines are terminated by a line containing a single dot (ugly!), ++ * or by an end of file. ++ */ ++static void ++addLines(NUM num) +{ -+ 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) ++ int len; ++ char buf[USERSIZE + 1]; ++ ++ while (fgets(buf, sizeof(buf), stdin)) + { -+ fprintf (stderr, "%s\n", strerror (errno)); -+ sprintf (errmsg, "Cannot seek temp file"); -+ return NULL; ++ if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0')) ++ return; ++ ++ len = strlen(buf); ++ ++ if (len == 0) ++ return; ++ ++ if (buf[len - 1] != '\n') ++ { ++ fprintf(stderr, "Line too long\n"); ++ ++ do ++ { ++ len = fgetc(stdin); ++ } ++ while ((len != EOF) && (len != '\n')); ++ ++ return; ++ } ++ ++ if (!insertLine(num++, buf, len)) ++ return; + } -+ } -+ 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; ++/* ++ * Parse a line number argument if it is present. This is a sum ++ * or difference of numbers, '.', '$', 'x, or a search string. ++ * Returns TRUE if successful (whether or not there was a number). ++ * Returns FALSE if there was a parsing error, with a message output. ++ * Whether there was a number is returned indirectly, as is the number. ++ * The character pointer which stopped the scan is also returned. ++ */ ++static BOOL ++getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum) +{ -+ 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) ++ const char * cp; ++ char * endStr; ++ char str[USERSIZE]; ++ BOOL haveNum; ++ NUM value; ++ NUM num; ++ NUM sign; ++ ++ cp = *retcp; ++ haveNum = FALSE; ++ value = 0; ++ sign = 1; ++ ++ while (TRUE) + { -+ 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; -+} ++ while (isBlank(*cp)) ++ cp++; + ++ switch (*cp) ++ { ++ case '.': ++ haveNum = TRUE; ++ num = curNum; ++ cp++; ++ break; + -+/* 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; ++ case '$': ++ haveNum = TRUE; ++ num = lastNum; ++ cp++; ++ break; + -+ cp = get_addressed_line_node (current_addr); /* this get_addressed_line_node last! */ -+ INSQUE (lp, cp); -+ addr_last++; -+ current_addr++; -+} ++ case '\'': ++ cp++; + ++ if ((*cp < 'a') || (*cp > 'z')) ++ { ++ fprintf(stderr, "Bad mark name\n"); + -+/* 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; -+} ++ return FALSE; ++ } + ++ haveNum = TRUE; ++ num = marks[*cp++ - 'a']; ++ break; + -+/* 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; -+} ++ case '/': ++ strcpy(str, ++cp); ++ endStr = strchr(str, '/'); + ++ if (endStr) ++ { ++ *endStr++ = '\0'; ++ cp += (endStr - str); ++ } ++ else ++ cp = ""; + -+extern int newline_added; ++ num = searchLines(str, curNum, lastNum); + -+/* 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; -+} ++ if (num == 0) ++ return FALSE; + ++ haveNum = TRUE; ++ break; + -+/* 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; ++ default: ++ if (!isDecimal(*cp)) ++ { ++ *retcp = cp; ++ *retHaveNum = haveNum; ++ *retNum = value; + -+ return 0; -+} ++ return TRUE; ++ } + ++ num = 0; + -+/* quit: remove_lines scratch file and exit */ -+void -+quit (n) -+ int n; -+{ -+ if (sfp) -+ { -+ fclose (sfp); -+ } -+ exit (n); -+} ++ while (isDecimal(*cp)) ++ num = num * 10 + *cp++ - '0'; + ++ haveNum = TRUE; ++ break; ++ } + -+extern line_t yank_buffer_head; -+extern char *old_filename; -+unsigned char ctab[256]; /* character translation table */ ++ value += num * sign; + -+/* 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; -+} ++ if (bufBase == NULL) ++ { ++ fprintf(stderr, "No memory for buffer\n"); + ++ return FALSE; ++ } + -+#ifndef SIG_ERR -+# define SIG_ERR ((void (*)()) -1) -+#endif /* !SIG_ERR */ ++ bufPtr = bufBase; ++ bufUsed = 0; + -+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 */ -+} ++ lines.next = &lines; ++ lines.prev = &lines; + ++ curLine = NULL; ++ curNum = 0; ++ lastNum = 0; ++ dirty = FALSE; ++ fileName = NULL; ++ searchString[0] = '\0'; + -+void -+signal_hup (signo) -+ int signo; -+{ -+ if (mutex) -+ sigflags |= (1 << (signo - 1)); -+ else -+ handle_hup (signo); ++ for (i = 0; i < 26; i++) ++ marks[i] = 0; ++ ++ return TRUE; +} + + -+void -+signal_int (signo) -+ int signo; ++/* ++ * Finish editing. ++ */ ++static void ++termEdit(void) +{ -+ if (mutex) -+ sigflags |= (1 << (signo - 1)); -+ else -+ handle_int (signo); -+} ++ if (bufBase) ++ free(bufBase); + ++ bufBase = NULL; ++ bufPtr = NULL; ++ bufSize = 0; ++ bufUsed = 0; + -+#ifdef HAVE_SIGSETJMP -+extern sigjmp_buf env; -+#else -+extern jmp_buf env; -+#endif -+extern int sigactive; ++ if (fileName) ++ free(fileName); + -+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); ++ fileName = NULL; ++ ++ searchString[0] = '\0'; ++ ++ if (lastNum) ++ deleteLines(1, lastNum); ++ ++ lastNum = 0; ++ curNum = 0; ++ curLine = NULL; +} + + -+void -+handle_int (signo) -+ int signo; ++/* ++ * Read lines from a file at the specified line number. ++ * Returns TRUE if the file was successfully read. ++ */ ++static BOOL ++readLines(const char * file, NUM num) +{ -+ if (!sigactive) -+ quit (1); -+ sigflags &= ~(1 << (signo - 1)); -+#ifdef HAVE_SIGSETJMP -+ siglongjmp (env, -1); -+#else -+ longjmp (env, -1); -+#endif -+} ++ int fd; ++ int cc; ++ LEN len; ++ LEN lineCount; ++ LEN charCount; ++ char * cp; + ++ if ((num < 1) || (num > lastNum + 1)) ++ { ++ fprintf(stderr, "Bad line for read\n"); + -+extern long rows; -+int cols = 72; /* wrap column */ ++ return FALSE; ++ } + -+void -+handle_winch (signo) -+ int signo; -+{ -+#ifdef TIOCGWINSZ -+ struct winsize ws; /* window size structure */ -+#endif ++ fd = open(file, 0); + -+ 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 -+} ++ if (fd < 0) ++ { ++ perror(file); + -+/* 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; -+} ++ return FALSE; ++ } + ++ bufPtr = bufBase; ++ bufUsed = 0; ++ lineCount = 0; ++ charCount = 0; ++ cc = 0; + -+char *sbuf; /* file i/o buffer */ -+int sbufsz; /* file i/o buffer size */ -+int newline_added; /* if set, newline appended to input file */ ++ printf("\"%s\", ", file); ++ fflush(stdout); + -+/* 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) ++ do + { -+ SPL0 (); -+ return ERR; ++ cp = memchr(bufPtr, '\n', bufUsed); ++ ++ if (cp) ++ { ++ len = (cp - bufPtr) + 1; ++ ++ if (!insertLine(num, bufPtr, len)) ++ { ++ close(fd); ++ ++ return FALSE; ++ } ++ ++ bufPtr += len; ++ bufUsed -= len; ++ charCount += len; ++ lineCount++; ++ num++; ++ ++ continue; ++ } ++ ++ if (bufPtr != bufBase) ++ { ++ memcpy(bufBase, bufPtr, bufUsed); ++ bufPtr = bufBase + bufUsed; ++ } ++ ++ if (bufUsed >= bufSize) ++ { ++ len = (bufSize * 3) / 2; ++ cp = realloc(bufBase, len); ++ ++ if (cp == NULL) ++ { ++ fprintf(stderr, "No memory for buffer\n"); ++ close(fd); ++ ++ return FALSE; ++ } ++ ++ bufBase = cp; ++ bufPtr = bufBase + bufUsed; ++ bufSize = len; ++ } ++ ++ cc = read(fd, bufPtr, bufSize - bufUsed); ++ bufUsed += cc; ++ bufPtr = bufBase; ++ + } -+ lp = lp->q_forw; -+ if (up) -+ up->t = lp; -+ else if ((up = push_undo_stack (UADD, current_addr, -+ current_addr)) == NULL) ++ while (cc > 0); ++ ++ if (cc < 0) + { -+ SPL0 (); -+ return ERR; ++ perror(file); ++ close(fd); ++ ++ return FALSE; + } -+ 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; -+} + ++ if (bufUsed) ++ { ++ if (!insertLine(num, bufPtr, bufUsed)) ++ { ++ close(fd); + -+/* 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; -+} ++ return -1; ++ } + ++ lineCount++; ++ charCount += bufUsed; ++ } + -+/* 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; -+} ++ close(fd); + ++ printf("%d lines%s, %d chars\n", lineCount, ++ (bufUsed ? " (incomplete)" : ""), charCount); + -+/* 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; ++ return TRUE; +} + + -+/* 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; ++/* ++ * Write the specified lines out to the specified file. ++ * Returns TRUE if successful, or FALSE on an error with a message output. ++ */ ++static BOOL ++writeLines(const char * file, NUM num1, NUM num2) +{ -+ while (len--) -+ if (fputc (*s++, fp) < 0) -+ { -+ fprintf (stderr, "%s\n", strerror (errno)); -+ sprintf (errmsg, "Cannot write file"); -+ return ERR; -+ } -+ return 0; -+} ++ int fd; ++ LINE * lp; ++ LEN lineCount; ++ LEN charCount; + -+/* 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') ++ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) + { -+ sprintf (errmsg, "Unexpected end-of-file"); -+ return NULL; ++ fprintf(stderr, "Bad line range for write\n"); ++ ++ return FALSE; + } -+ 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; -+} + ++ lineCount = 0; ++ charCount = 0; + -+/* 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) ++ if (lp == NULL) + { -+ 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; -+ } ++ close(fd); ++ ++ return FALSE; + } -+ if (gflag & GLS) ++ ++ while (num1++ <= num2) + { -+ if (31 < *s && *s < 127 && *s != '\\') -+ putchar (*s); -+ else -+ { -+ putchar ('\\'); -+ col++; -+ if (*s && (cp = strchr (ESCAPES, *s)) != NULL) -+ putchar (ESCCHARS[cp - ESCAPES]); -+ else ++ if (write(fd, lp->data, lp->len) != lp->len) + { -+ putchar ((((unsigned char) *s & 0300) >> 6) + '0'); -+ putchar ((((unsigned char) *s & 070) >> 3) + '0'); -+ putchar (((unsigned char) *s & 07) + '0'); -+ col += 2; ++ perror(file); ++ close(fd); ++ ++ return FALSE; + } -+ } + ++ charCount += lp->len; ++ lineCount++; ++ lp = lp->next; + } -+ else -+ putchar (*s); -+ } -+ if (!traditional && (gflag & GLS)) -+ putchar ('$'); -+ putchar ('\n'); -+ return 0; -+} -+ + ++ if (close(fd) < 0) ++ { ++ perror(file); + ++ return FALSE; ++ } + -+char *rhbuf; /* rhs substitution buffer */ -+int rhbufsz; /* rhs substitution buffer size */ -+int rhbufi; /* rhs substitution buffer index */ ++ printf("%d lines, %d chars\n", lineCount, charCount); + -+/* 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; ++ return TRUE; +} + + -+/* extract_subst_template: return pointer to copy of substitution template -+ in the command buffer */ -+char * -+extract_subst_template () ++/* ++ * Print lines in a specified range. ++ * The last line printed becomes the current line. ++ * If expandFlag is TRUE, then the line is printed specially to ++ * show magic characters. ++ */ ++static BOOL ++printLines(NUM num1, NUM num2, BOOL expandFlag) +{ -+ 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) ++ const LINE * lp; ++ const unsigned char * cp; ++ int ch; ++ LEN count; ++ ++ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) + { -+ while ((n = get_tty_line ()) == 0 || -+ (n > 0 && ibuf[n - 1] != '\n')) -+ clearerr (stdin); -+ if (n < 0) -+ return NULL; ++ fprintf(stderr, "Bad line range for print\n"); ++ ++ return FALSE; + } -+ } -+ REALLOC (rhbuf, rhbufsz, i + 1, NULL); -+ rhbuf[rhbufi = i] = '\0'; -+ return rhbuf; -+} + ++ lp = findLine(num1); + -+char *rbuf; /* substitute_matching_text buffer */ -+int rbufsz; /* substitute_matching_text buffer size */ ++ if (lp == NULL) ++ return FALSE; + -+/* 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) ++ while (num1 <= num2) + { -+ 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) ++ if (!expandFlag) + { -+ SPL0 (); -+ return ERR; ++ write(STDOUT, lp->data, lp->len); ++ setCurNum(num1++); ++ lp = lp->next; ++ ++ continue; + } -+ else if (up) -+ up->t = get_addressed_line_node (current_addr); -+ else if ((up = push_undo_stack (UADD, -+ current_addr, current_addr)) == NULL) ++ ++ /* ++ * Show control characters and characters with the ++ * high bit set specially. ++ */ ++ cp = lp->data; ++ count = lp->len; ++ ++ if ((count > 0) && (cp[count - 1] == '\n')) ++ count--; ++ ++ while (count-- > 0) + { -+ SPL0 (); -+ return ERR; ++ ch = *cp++; ++ ++ if (ch & 0x80) ++ { ++ fputs("M-", stdout); ++ ch &= 0x7f; ++ } ++ ++ if (ch < ' ') ++ { ++ fputc('^', stdout); ++ ch += '@'; ++ } ++ ++ if (ch == 0x7f) ++ { ++ fputc('^', stdout); ++ ch = '?'; ++ } ++ ++ fputc(ch, stdout); + } -+ } -+ while (txt != eot); -+ SPL0 (); -+ nsubs++; ++ ++ fputs("$\n", stdout); ++ ++ setCurNum(num1++); ++ lp = lp->next; + } -+ } -+ 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; ++ ++ return TRUE; +} + + -+/* 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; ++/* ++ * Insert a new line with the specified text. ++ * The line is inserted so as to become the specified line, ++ * thus pushing any existing and further lines down one. ++ * The inserted line is also set to become the current line. ++ * Returns TRUE if successful. ++ */ ++static BOOL ++insertLine(NUM num, const char * data, LEN len) +{ -+ 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 ++ LINE * newLp; ++ LINE * lp; ++ ++ if ((num < 1) || (num > lastNum + 1)) + { -+ 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; ++ fprintf(stderr, "Inserting at bad line number\n"); ++ ++ return FALSE; + } -+ 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)) ++ ++ newLp = (LINE *) malloc(sizeof(LINE) + len - 1); ++ ++ if (newLp == NULL) + { -+ sprintf (errmsg, "Infinite substitution loop"); -+ return ERR; ++ fprintf(stderr, "Failed to allocate memory for line\n"); ++ ++ return FALSE; + } -+ if (isbinary) -+ NEWLINE_TO_NUL (txt, i); -+ memcpy (rbuf + off, txt, i); -+ memcpy (rbuf + off + i, "\n", 2); -+ } -+ return changed ? off + i + 1 : 0; -+} + ++ memcpy(newLp->data, data, len); ++ newLp->len = len; + -+/* 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; -+} ++ if (num > lastNum) ++ lp = &lines; ++ else ++ { ++ lp = findLine(num); + ++ if (lp == NULL) ++ { ++ free((char *) newLp); + ++ return FALSE; ++ } ++ } + -+#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 */ ++ newLp->next = lp; ++ newLp->prev = lp->prev; ++ lp->prev->next = newLp; ++ lp->prev = newLp; + -+/* 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++; ++ lastNum++; ++ dirty = TRUE; ++ ++ return setCurNum(num); +} + + -+/* USWAP: swap undo nodes */ -+#define USWAP(x, y) \ -+ do \ -+ { \ -+ undo_t utmp; \ -+ utmp = (x), (x) = (y), (y) = utmp; \ -+ } \ -+ while (0) ++/* ++ * Delete lines from the given range. ++ */ ++static BOOL ++deleteLines(NUM num1, NUM num2) ++{ ++ LINE * lp; ++ LINE * nlp; ++ LINE * plp; ++ NUM count; + ++ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) ++ { ++ fprintf(stderr, "Bad line numbers for delete\n"); + -+long u_current_addr = -1; /* if >= 0, undo enabled */ -+long u_addr_last = -1; /* if >= 0, undo enabled */ ++ return FALSE; ++ } + -+/* 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) ++ lp = findLine(num1); ++ ++ if (lp == NULL) ++ return FALSE; ++ ++ if ((curNum >= num1) && (curNum <= num2)) + { -+ 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 */ -+ ; ++ if (num2 < lastNum) ++ setCurNum(num2 + 1); ++ else if (num1 > 1) ++ setCurNum(num1 - 1); ++ else ++ curNum = 0; + } -+ 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; -+} + ++ count = num2 - num1 + 1; + -+/* 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; -+} ++ if (curNum > num2) ++ curNum -= count; + ++ lastNum -= count; + ++ while (count-- > 0) ++ { ++ nlp = lp->next; ++ plp = lp->prev; ++ plp->next = nlp; ++ nlp->prev = plp; ++ lp->next = NULL; ++ lp->prev = NULL; ++ lp->len = 0; ++ free(lp); ++ lp = nlp; ++ } + ++ dirty = TRUE; + -+/* 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; ++ return TRUE; +} + + -+/* 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; ++/* ++ * Search for a line which contains the specified string. ++ * If the string is NULL, then the previously searched for string ++ * is used. The currently searched for string is saved for future use. ++ * Returns the line number which matches, or 0 if there was no match ++ * with an error printed. ++ */ ++static NUM ++searchLines(const char * str, NUM num1, NUM num2) +{ -+ 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) ++ const LINE * lp; ++ int len; ++ ++ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) ++ { ++ fprintf(stderr, "Bad line numbers for search\n"); ++ ++ return 0; ++ } ++ ++ if (*str == '\0') + { -+ /* 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) ++ if (searchString[0] == '\0') + { -+ sprintf (errmsg, "No previous command"); -+ return ERR; ++ fprintf(stderr, "No previous search string\n"); ++ ++ return 0; + } -+ 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; -+ } + ++ str = searchString; + } -+ 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; -+} + ++ if (str != searchString) ++ strcpy(searchString, str); + -+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) */ ++ len = strlen(str); + -+/* 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 ++ lp = findLine(num1); ++ ++ if (lp == NULL) ++ return 0; ++ ++ while (num1 <= num2) + { -+ 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; -+ } ++ if (findString(lp, str, len, 0) >= 0) ++ return num1; ++ ++ num1++; ++ lp = lp->next; + } -+ active_size = ti; -+ active_list = ts; -+ SPL0 (); -+ } -+ active_list[active_last++] = lp; -+ return 0; ++ ++ fprintf(stderr, "Cannot find string \"%s\"\n", str); ++ ++ 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; ++/* ++ * Return a pointer to the specified line number. ++ */ ++static LINE * ++findLine(NUM num) +{ -+ line_t *lp; -+ long i; ++ LINE * lp; ++ NUM lnum; + -+ for (lp = np; lp != mp; lp = lp->q_forw) -+ for (i = 0; i < active_last; i++) -+ if (active_list[active_ndx] == lp) ++ if ((num < 1) || (num > lastNum)) + { -+ active_list[active_ndx] = NULL; -+ active_ndx = INC_MOD (active_ndx, active_last - 1); -+ break; ++ fprintf(stderr, "Line number %d does not exist\n", num); ++ ++ return NULL; + } -+ else -+ active_ndx = INC_MOD (active_ndx, active_last - 1); -+} + ++ if (curNum <= 0) ++ { ++ curNum = 1; ++ curLine = lines.next; ++ } + -+/* 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; -+} ++ if (num == curNum) ++ return curLine; + ++ lp = curLine; ++ lnum = curNum; + -+/* 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 (); -+} ++ if (num < (curNum / 2)) ++ { ++ lp = lines.next; ++ lnum = 1; ++ } ++ else if (num > ((curNum + lastNum) / 2)) ++ { ++ lp = lines.prev; ++ lnum = lastNum; ++ } + ++ while (lnum < num) ++ { ++ lp = lp->next; ++ lnum++; ++ } + ++ while (lnum > num) ++ { ++ lp = lp->prev; ++ lnum--; ++ } + -+/* 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; ++ return lp; +} + + -+/* extract_pattern: copy a pattern string from the command buffer; return -+ pointer to the copy */ -+char * -+extract_pattern (delimiter) -+ int delimiter; ++/* ++ * Set the current line number. ++ * Returns TRUE if successful. ++ */ ++static BOOL ++setCurNum(NUM num) +{ -+ 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; -+} ++ LINE * lp; + ++ lp = findLine(num); + -+/* 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 $ -+*/ ++ if (lp == NULL) ++ return FALSE; + -+#include ++ curNum = num; ++ curLine = lp; ++ ++ return TRUE; ++} + -+#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; ++/* END CODE */ -- cgit v1.2.3