patch for a very alpha busybox ed
No binary (build failed)
—
(log yok)
| @@ -0,0 +1,3443 @@ | |
| + | Index: editors/Makefile.in |
| + | =================================================================== |
| + | --- editors/Makefile.in (revision 10144) |
| + | +++ editors/Makefile.in (working copy) |
| + | @@ -24,8 +24,9 @@ |
| + | srcdir=$(top_srcdir)/editors |
| + | |
| + | EDITOR-y:= |
| + | -EDITOR-$(CONFIG_AWK) += awk.o |
| + | -EDITOR-$(CONFIG_PATCH) += patch.o |
| + | +EDITOR-$(CONFIG_AWK) += awk.o |
| + | +EDITOR-$(CONFIG_ED) += ed.o |
| + | +EDITOR-$(CONFIG_PATCH) += patch.o |
| + | EDITOR-$(CONFIG_SED) += sed.o |
| + | EDITOR-$(CONFIG_VI) += vi.o |
| + | EDITOR_SRC:= $(EDITOR-y) |
| + | Index: editors/Config.in |
| + | =================================================================== |
| + | --- editors/Config.in (revision 10144) |
| + | +++ editors/Config.in (working copy) |
| + | @@ -20,6 +20,12 @@ |
| + | Enable math functions of the Awk programming language. |
| + | NOTE: This will require libm to be present for linking. |
| + | |
| + | +config CONFIG_ED |
| + | + bool "ed" |
| + | + default n |
| + | + help |
| + | + ed |
| + | + |
| + | config CONFIG_PATCH |
| + | bool "patch" |
| + | default n |
| + | Index: include/usage.h |
| + | =================================================================== |
| + | --- include/usage.h (revision 10151) |
| + | +++ include/usage.h (working copy) |
| + | @@ -556,6 +561,9 @@ |
| + | "$ echo \"Erik\\nis\\ncool\"\n" \ |
| + | "Erik\\nis\\ncool\n") |
| + | |
| + | +#define ed_trivial_usage "" |
| + | +#define ed_full_usage "" |
| + | + |
| + | #define env_trivial_usage \ |
| + | "[-iu] [-] [name=value]... [command]" |
| + | #define env_full_usage \ |
| + | Index: include/applets.h |
| + | =================================================================== |
| + | --- include/applets.h (revision 10151) |
| + | +++ include/applets.h (working copy) |
| + | @@ -179,6 +179,9 @@ |
| + | #ifdef CONFIG_ECHO |
| + | APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER) |
| + | #endif |
| + | +#ifdef CONFIG_ED |
| + | + APPLET(ed, ed_main, _BB_DIR_BIN, _BB_SUID_NEVER) |
| + | +#endif |
| + | #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS) |
| + | APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER) |
| + | #endif |
| + | --- /dev/null 2005-04-22 11:15:01.120978184 -0400 |
| + | +++ editors/ed.c 2005-04-22 11:16:00.000000000 -0400 |
| + | @@ -0,0 +1,3120 @@ |
| + | +/* main.c: This file contains the main control and user-interface routines |
| + | + for the ed line editor. */ |
| + | +/* ed line editor. |
| + | + Copyright (C) 1993, 1994 Andrew Moore, Talke Studio |
| + | + All Rights Reserved |
| + | + |
| + | + This program is free software; you can redistribute it and/or modify |
| + | + it under the terms of the GNU General Public License as published by |
| + | + the Free Software Foundation; either version 2, or (at your option) |
| + | + any later version. |
| + | + |
| + | + This program is distributed in the hope that it will be useful, but |
| + | + WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| + | + General Public License for more details. |
| + | + |
| + | + You should have received a copy of the GNU General Public License |
| + | + along with this program; if not, write to the Free Software |
| + | + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| + | +*/ |
| + | + |
| + | +/* |
| + | + * CREDITS |
| + | + * |
| + | + * This program is based on the editor algorithm described in |
| + | + * Brian W. Kernighan and P. J. Plauger's book "Software Tools |
| + | + * in Pascal," Addison-Wesley, 1981. |
| + | + * |
| + | + * The buffering algorithm is attributed to Rodney Ruddock of |
| + | + * the University of Guelph, Guelph, Ontario. |
| + | + * |
| + | + */ |
| + | + |
| + | +#include <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; |