hush/patches/ed.patch

3444 lines
78 KiB
Diff
Raw Normal View History

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