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