mirror of
https://github.com/sheumann/hush.git
synced 2024-11-05 06:07:00 +00:00
3444 lines
78 KiB
Diff
3444 lines
78 KiB
Diff
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;
|