aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2005-04-23 01:50:55 +0000
committerMike Frysinger <vapier@gentoo.org>2005-04-23 01:50:55 +0000
commit84ab267e226407ee402eff10aaf8239ed684de77 (patch)
tree24d9d109c14b18b36e4f007973f7ea28b52a4c91
parent6b05b0ce9345895bbd8ae3e553699613d6d2bc98 (diff)
downloadbusybox-w32-84ab267e226407ee402eff10aaf8239ed684de77.tar.gz
busybox-w32-84ab267e226407ee402eff10aaf8239ed684de77.tar.bz2
busybox-w32-84ab267e226407ee402eff10aaf8239ed684de77.zip
patch for a very alpha busybox ed
-rw-r--r--patches/ed.patch3443
1 files changed, 3443 insertions, 0 deletions
diff --git a/patches/ed.patch b/patches/ed.patch
new file mode 100644
index 000000000..fc261b88d
--- /dev/null
+++ b/patches/ed.patch
@@ -0,0 +1,3443 @@
1Index: editors/Makefile.in
2===================================================================
3--- editors/Makefile.in (revision 10144)
4+++ editors/Makefile.in (working copy)
5@@ -24,8 +24,9 @@
6 srcdir=$(top_srcdir)/editors
7
8 EDITOR-y:=
9-EDITOR-$(CONFIG_AWK) += awk.o
10-EDITOR-$(CONFIG_PATCH) += patch.o
11+EDITOR-$(CONFIG_AWK) += awk.o
12+EDITOR-$(CONFIG_ED) += ed.o
13+EDITOR-$(CONFIG_PATCH) += patch.o
14 EDITOR-$(CONFIG_SED) += sed.o
15 EDITOR-$(CONFIG_VI) += vi.o
16 EDITOR_SRC:= $(EDITOR-y)
17Index: editors/Config.in
18===================================================================
19--- editors/Config.in (revision 10144)
20+++ editors/Config.in (working copy)
21@@ -20,6 +20,12 @@
22 Enable math functions of the Awk programming language.
23 NOTE: This will require libm to be present for linking.
24
25+config CONFIG_ED
26+ bool "ed"
27+ default n
28+ help
29+ ed
30+
31 config CONFIG_PATCH
32 bool "patch"
33 default n
34Index: include/usage.h
35===================================================================
36--- include/usage.h (revision 10151)
37+++ include/usage.h (working copy)
38@@ -556,6 +561,9 @@
39 "$ echo \"Erik\\nis\\ncool\"\n" \
40 "Erik\\nis\\ncool\n")
41
42+#define ed_trivial_usage ""
43+#define ed_full_usage ""
44+
45 #define env_trivial_usage \
46 "[-iu] [-] [name=value]... [command]"
47 #define env_full_usage \
48Index: include/applets.h
49===================================================================
50--- include/applets.h (revision 10151)
51+++ include/applets.h (working copy)
52@@ -179,6 +179,9 @@
53 #ifdef CONFIG_ECHO
54 APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER)
55 #endif
56+#ifdef CONFIG_ED
57+ APPLET(ed, ed_main, _BB_DIR_BIN, _BB_SUID_NEVER)
58+#endif
59 #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS)
60 APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
61 #endif
62--- /dev/null 2005-04-22 11:15:01.120978184 -0400
63+++ editors/ed.c 2005-04-22 11:16:00.000000000 -0400
64@@ -0,0 +1,3120 @@
65+/* main.c: This file contains the main control and user-interface routines
66+ for the ed line editor. */
67+/* ed line editor.
68+ Copyright (C) 1993, 1994 Andrew Moore, Talke Studio
69+ All Rights Reserved
70+
71+ This program is free software; you can redistribute it and/or modify
72+ it under the terms of the GNU General Public License as published by
73+ the Free Software Foundation; either version 2, or (at your option)
74+ any later version.
75+
76+ This program is distributed in the hope that it will be useful, but
77+ WITHOUT ANY WARRANTY; without even the implied warranty of
78+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
79+ General Public License for more details.
80+
81+ You should have received a copy of the GNU General Public License
82+ along with this program; if not, write to the Free Software
83+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
84+*/
85+
86+/*
87+ * CREDITS
88+ *
89+ * This program is based on the editor algorithm described in
90+ * Brian W. Kernighan and P. J. Plauger's book "Software Tools
91+ * in Pascal," Addison-Wesley, 1981.
92+ *
93+ * The buffering algorithm is attributed to Rodney Ruddock of
94+ * the University of Guelph, Guelph, Ontario.
95+ *
96+ */
97+
98+#include <errno.h>
99+#include <limits.h>
100+#include <signal.h>
101+#include <stdlib.h>
102+#include <stdio.h>
103+#include <string.h>
104+#include <ctype.h>
105+#include <setjmp.h>
106+#include <pwd.h>
107+#include <unistd.h>
108+#include <sys/types.h>
109+#include <sys/stat.h>
110+#include <sys/file.h>
111+#include <sys/ioctl.h>
112+
113+#include "busybox.h"
114+#include "ed.h"
115+#include "getopt.h"
116+
117+jmp_buf env;
118+
119+/* static buffers */
120+char *errmsg; /* error message buffer */
121+char stdinbuf[1]; /* stdin buffer */
122+char *shcmd; /* shell command buffer */
123+int shcmdsz; /* shell command buffer size */
124+int shcmdi; /* shell command buffer index */
125+char *ibuf; /* ed command-line buffer */
126+int ibufsz; /* ed command-line buffer size */
127+char *ibufp; /* pointer to ed command-line buffer */
128+
129+/* global flags */
130+int traditional = 0; /* if set, be backwards compatible */
131+int garrulous = 0; /* if set, print all error messages */
132+int isbinary; /* if set, buffer contains ASCII NULs */
133+int isglobal; /* if set, doing a global command */
134+int modified; /* if set, buffer modified since last write */
135+int mutex = 0; /* if set, signals set "sigflags" */
136+int red = 0; /* if set, restrict shell/directory access */
137+int scripted = 0; /* if set, suppress diagnostics */
138+int sigactive = 0; /* if set, signal handlers are enabled */
139+int sigflags = 0; /* if set, signals received while mutex set */
140+
141+char *old_filename; /* default filename */
142+long current_addr; /* current address in editor buffer */
143+long addr_last; /* last address in editor buffer */
144+int lineno; /* script line number */
145+char *prompt; /* command-line prompt */
146+char *dps = "*"; /* default command-line prompt */
147+long err_status = 0; /* program exit status */
148+
149+/* The name this program was run with. */
150+char *program_name;
151+
152+/* If non-zero, display usage information and exit. */
153+int show_help = 0;
154+
155+/* If non-zero, print the version on standard output and exit. */
156+int show_version = 0;
157+
158+/* Long options equivalences. */
159+struct option long_options[] =
160+{
161+ {"help", no_argument, &show_help, 1},
162+ {"prompt", required_argument, NULL, 'p'},
163+ {"quiet", no_argument, NULL, 's'},
164+ {"silent", no_argument, NULL, 's'},
165+ {"traditional", no_argument, NULL, 'G'},
166+ {"version", no_argument, &show_version, 1},
167+ {0, 0, 0, 0},
168+};
169+
170+extern int optind;
171+extern char *optarg;
172+
173+/* usage: explain usage */
174+
175+#if 0
176+void
177+usage (status)
178+ int status;
179+{
180+ if (status != 0)
181+ fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
182+ else
183+ {
184+ printf ("Usage: %s [OPTION]... [FILE]\n", program_name);
185+ printf ("\
186+\n\
187+ -G, --traditional use a few backward compatible features\n\
188+ -p, --prompt=STRING use STRING as an interactive prompt\n\
189+ -s, -, --quiet, --silent suppress diagnostics\n");
190+ printf ("\
191+ --help display this help\n\
192+ --version output version information\n\
193+\n\
194+Start edit by reading in FILE if given. Read output of shell command\n\
195+if FILE begins with a `!'.\n");
196+ }
197+ exit (status);
198+}
199+#endif
200+#define usage(x) bb_show_usage()
201+
202+
203+/* ed: line editor */
204+int
205+ed_main (int argc, char **argv)
206+{
207+ int c, n;
208+ long status = 0;
209+
210+ program_name = argv[0];
211+ red = (n = strlen (argv[0])) > 2 && argv[0][n - 3] == 'r';
212+top:
213+ while ((c = getopt_long (argc, argv, "Gp:s", long_options, NULL)) != EOF)
214+ switch (c)
215+ {
216+ default:
217+ usage (1);
218+ case 0:
219+ break;
220+ case 'G': /* backward compatibility */
221+ traditional = 1;
222+ break;
223+ case 'p': /* set prompt */
224+ prompt = optarg;
225+ break;
226+ case 's': /* run script */
227+ scripted = 1;
228+ break;
229+ }
230+ if (show_help)
231+ usage (0);
232+ argv += optind;
233+ argc -= optind;
234+ if (argc && **argv == '-')
235+ {
236+ scripted = 1;
237+ if (argc > 1)
238+ {
239+ optind = 0;
240+ goto top;
241+ }
242+ argv++;
243+ argc--;
244+ }
245+ init_buffers ();
246+
247+ /* assert: reliable signals! */
248+#ifdef SIGWINCH
249+ handle_winch (SIGWINCH);
250+ if (isatty (0))
251+ reliable_signal (SIGWINCH, handle_winch);
252+#endif
253+ reliable_signal (SIGHUP, signal_hup);
254+ reliable_signal (SIGQUIT, SIG_IGN);
255+ reliable_signal (SIGINT, signal_int);
256+ if ((status = setjmp (env)))
257+ {
258+ fputs ("\n?\n", stderr);
259+ sprintf (errmsg, "Interrupt");
260+ }
261+ else
262+ {
263+ sigactive = 1; /* enable signal handlers */
264+ if (argc && **argv && is_legal_filename (*argv))
265+ {
266+ if (read_file (*argv, 0) < 0 && is_regular_file (0))
267+ quit (2);
268+ else if (**argv != '!')
269+ strcpy (old_filename, *argv);
270+ }
271+ else if (argc)
272+ {
273+ fputs ("?\n", stderr);
274+ if (**argv == '\0')
275+ sprintf (errmsg, "Invalid filename");
276+ if (is_regular_file (0))
277+ quit (2);
278+ }
279+ }
280+ for (;;)
281+ {
282+ if (status < 0 && garrulous)
283+ fprintf (stderr, "%s\n", errmsg);
284+ if (prompt)
285+ {
286+ printf ("%s", prompt);
287+ fflush (stdout);
288+ }
289+ if ((n = get_tty_line ()) < 0)
290+ {
291+ status = ERR;
292+ continue;
293+ }
294+ else if (n == 0)
295+ {
296+ if (modified && !scripted)
297+ {
298+ fputs ("?\n", stderr);
299+ sprintf (errmsg, "Warning: file modified");
300+ if (is_regular_file (0))
301+ {
302+ fprintf (stderr, garrulous ?
303+ "script, line %d: %s\n" :
304+ "", lineno, errmsg);
305+ quit (2);
306+ }
307+ clearerr (stdin);
308+ modified = 0;
309+ status = EMOD;
310+ continue;
311+ }
312+ else
313+ quit (err_status);
314+ }
315+ else if (ibuf[n - 1] != '\n')
316+ {
317+ /* discard line */
318+ sprintf (errmsg, "Unexpected end-of-file");
319+ clearerr (stdin);
320+ status = ERR;
321+ continue;
322+ }
323+ isglobal = 0;
324+ if ((status = extract_addr_range ()) >= 0 &&
325+ (status = exec_command ()) >= 0)
326+ if (!status || (status = display_lines (current_addr, current_addr,
327+ status)) >= 0)
328+ continue;
329+ switch (status)
330+ {
331+ case EOF:
332+ quit (err_status);
333+ case EMOD:
334+ modified = 0;
335+ fputs ("?\n", stderr); /* give warning */
336+ sprintf (errmsg, "Warning: file modified");
337+ if (is_regular_file (0))
338+ {
339+ fprintf (stderr, garrulous ?
340+ "script, line %d: %s\n" :
341+ "", lineno, errmsg);
342+ quit (2);
343+ }
344+ break;
345+ case FATAL:
346+ if (is_regular_file (0))
347+ fprintf (stderr, garrulous ?
348+ "script, line %d: %s\n" : "",
349+ lineno, errmsg);
350+ else
351+ fprintf (stderr, garrulous ? "%s\n" : "",
352+ errmsg);
353+ quit (3);
354+ default:
355+ fputs ("?\n", stderr);
356+ if (is_regular_file (0))
357+ {
358+ fprintf (stderr, garrulous ?
359+ "script, line %d: %s\n" : "",
360+ lineno, errmsg);
361+ quit (4);
362+ }
363+ break;
364+ }
365+ err_status = -status;
366+ }
367+ /*NOTREACHED */
368+}
369+
370+long first_addr, second_addr, addr_cnt;
371+
372+/* extract_addr_range: get line addresses from the command buffer until an
373+ illegal address is seen; return status */
374+int
375+extract_addr_range ()
376+{
377+ long addr;
378+
379+ addr_cnt = 0;
380+ first_addr = second_addr = current_addr;
381+ while ((addr = next_addr ()) >= 0)
382+ {
383+ addr_cnt++;
384+ first_addr = second_addr;
385+ second_addr = addr;
386+ if (*ibufp != ',' && *ibufp != ';')
387+ break;
388+ else if (*ibufp++ == ';')
389+ current_addr = addr;
390+ }
391+ if ((addr_cnt = min (addr_cnt, 2)) == 1 || second_addr != addr)
392+ first_addr = second_addr;
393+ return (addr == ERR) ? ERR : 0;
394+}
395+
396+
397+#define SKIP_BLANKS() \
398+ while (isspace (*ibufp) && *ibufp != '\n') \
399+ ibufp++
400+
401+#define MUST_BE_FIRST() \
402+ do \
403+ { \
404+ if (!first) \
405+ { \
406+ sprintf (errmsg, "Invalid address"); \
407+ return ERR; \
408+ } \
409+ } \
410+ while (0)
411+
412+/* next_addr: return the next line address in the command buffer */
413+long
414+next_addr ()
415+{
416+ char *hd;
417+ long addr = current_addr;
418+ long n;
419+ int first = 1;
420+ int c;
421+
422+ SKIP_BLANKS ();
423+ for (hd = ibufp;; first = 0)
424+ switch (c = *ibufp)
425+ {
426+ case '+':
427+ case '\t':
428+ case ' ':
429+ case '-':
430+ case '^':
431+ ibufp++;
432+ SKIP_BLANKS ();
433+ if (isdigit (*ibufp))
434+ {
435+ STRTOL (n, ibufp);
436+ addr += (c == '-' || c == '^') ? -n : n;
437+ }
438+ else if (!isspace (c))
439+ addr += (c == '-' || c == '^') ? -1 : 1;
440+ break;
441+ case '0':
442+ case '1':
443+ case '2':
444+ case '3':
445+ case '4':
446+ case '5':
447+ case '6':
448+ case '7':
449+ case '8':
450+ case '9':
451+ MUST_BE_FIRST ();
452+ STRTOL (addr, ibufp);
453+ break;
454+ case '.':
455+ case '$':
456+ MUST_BE_FIRST ();
457+ ibufp++;
458+ addr = (c == '.') ? current_addr : addr_last;
459+ break;
460+ case '/':
461+ case '?':
462+ MUST_BE_FIRST ();
463+ if ((addr = get_matching_node_addr (
464+ get_compiled_pattern (), c == '/')) < 0)
465+ return ERR;
466+ else if (c == *ibufp)
467+ ibufp++;
468+ break;
469+ case '\'':
470+ MUST_BE_FIRST ();
471+ ibufp++;
472+ if ((addr = get_marked_node_addr (*ibufp++)) < 0)
473+ return ERR;
474+ break;
475+ case '%':
476+ case ',':
477+ case ';':
478+ if (first)
479+ {
480+ ibufp++;
481+ addr_cnt++;
482+ second_addr = (c == ';') ? current_addr : 1;
483+ addr = addr_last;
484+ break;
485+ }
486+ /* FALL THROUGH */
487+ default:
488+ if (ibufp == hd)
489+ return EOF;
490+ else if (addr < 0 || addr_last < addr)
491+ {
492+ sprintf (errmsg, "Invalid address");
493+ return ERR;
494+ }
495+ else
496+ return addr;
497+ }
498+ /* NOTREACHED */
499+}
500+
501+
502+/* GET_THIRD_ADDR: get a legal address from the command buffer */
503+#define GET_THIRD_ADDR(addr) \
504+ do \
505+ { \
506+ long ol1, ol2; \
507+ ol1 = first_addr, ol2 = second_addr; \
508+ if (extract_addr_range () < 0) \
509+ return ERR; \
510+ else if (traditional && addr_cnt == 0) \
511+ { \
512+ sprintf (errmsg, "Destination expected"); \
513+ return ERR; \
514+ } \
515+ else if (second_addr < 0 || addr_last < second_addr) \
516+ { \
517+ sprintf (errmsg, "Invalid address"); \
518+ return ERR; \
519+ } \
520+ (addr) = second_addr; \
521+ first_addr = ol1, second_addr = ol2; \
522+ } \
523+ while (0)
524+
525+/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */
526+#define GET_COMMAND_SUFFIX() \
527+ do \
528+ { \
529+ int done = 0; \
530+ do \
531+ { \
532+ switch (*ibufp) \
533+ { \
534+ case 'p': \
535+ gflag |= GPR, ibufp++; \
536+ break; \
537+ case 'l': \
538+ gflag |= GLS, ibufp++; \
539+ break; \
540+ case 'n': \
541+ gflag |= GNP, ibufp++; \
542+ break; \
543+ default: \
544+ done++; \
545+ } \
546+ } \
547+ while (!done); \
548+ if (*ibufp++ != '\n') \
549+ { \
550+ sprintf (errmsg, "Invalid command suffix"); \
551+ return ERR; \
552+ } \
553+ } \
554+ while (0)
555+
556+/* sflags */
557+#define SGG 001 /* complement previous global substitute suffix */
558+#define SGP 002 /* complement previous print suffix */
559+#define SGR 004 /* use last regex instead of last pat */
560+#define SGF 010 /* repeat last substitution */
561+
562+int patlock = 0; /* if set, pattern not freed by get_compiled_pattern() */
563+
564+long rows = 22; /* scroll length: ws_row - 2 */
565+
566+/* exec_command: execute the next command in command buffer; return print
567+ request, if any */
568+int
569+exec_command ()
570+{
571+ extern long u_current_addr;
572+ extern long u_addr_last;
573+
574+ static pattern_t *pat = NULL;
575+ static int sgflag = 0;
576+ static int sgnum = 0;
577+
578+ pattern_t *tpat;
579+ char *fnp;
580+ int gflag = 0;
581+ int sflags = 0;
582+ long addr = 0;
583+ int n = 0;
584+ int c;
585+
586+ SKIP_BLANKS ();
587+ switch (c = *ibufp++)
588+ {
589+ case 'a':
590+ GET_COMMAND_SUFFIX ();
591+ if (!isglobal)
592+ clear_undo_stack ();
593+ if (append_lines (second_addr) < 0)
594+ return ERR;
595+ break;
596+ case 'c':
597+ if (check_addr_range (current_addr, current_addr) < 0)
598+ return ERR;
599+ GET_COMMAND_SUFFIX ();
600+ if (!isglobal)
601+ clear_undo_stack ();
602+ if (delete_lines (first_addr, second_addr) < 0 ||
603+ append_lines (current_addr) < 0)
604+ return ERR;
605+ break;
606+ case 'd':
607+ if (check_addr_range (current_addr, current_addr) < 0)
608+ return ERR;
609+ GET_COMMAND_SUFFIX ();
610+ if (!isglobal)
611+ clear_undo_stack ();
612+ if (delete_lines (first_addr, second_addr) < 0)
613+ return ERR;
614+ else if ((addr = INC_MOD (current_addr, addr_last)) != 0)
615+ current_addr = addr;
616+ break;
617+ case 'e':
618+ if (modified && !scripted)
619+ return EMOD;
620+ /* fall through */
621+ case 'E':
622+ if (addr_cnt > 0)
623+ {
624+ sprintf (errmsg, "Unexpected address");
625+ return ERR;
626+ }
627+ else if (!isspace (*ibufp))
628+ {
629+ sprintf (errmsg, "Unexpected command suffix");
630+ return ERR;
631+ }
632+ else if ((fnp = get_filename ()) == NULL)
633+ return ERR;
634+ GET_COMMAND_SUFFIX ();
635+ if (delete_lines (1, addr_last) < 0)
636+ return ERR;
637+ delete_yank_lines ();
638+ clear_undo_stack ();
639+ if (close_sbuf () < 0)
640+ return ERR;
641+ else if (open_sbuf () < 0)
642+ return FATAL;
643+ if (*fnp && *fnp != '!')
644+ strcpy (old_filename, fnp);
645+ if (traditional && *fnp == '\0' && *old_filename == '\0')
646+ {
647+ sprintf (errmsg, "No current filename");
648+ return ERR;
649+ }
650+ else if (read_file (*fnp ? fnp : old_filename, 0) < 0)
651+ return ERR;
652+ clear_undo_stack ();
653+ modified = 0;
654+ u_current_addr = u_addr_last = -1;
655+ break;
656+ case 'f':
657+ if (addr_cnt > 0)
658+ {
659+ sprintf (errmsg, "Unexpected address");
660+ return ERR;
661+ }
662+ else if (!isspace (*ibufp))
663+ {
664+ sprintf (errmsg, "Unexpected command suffix");
665+ return ERR;
666+ }
667+ else if ((fnp = get_filename ()) == NULL)
668+ return ERR;
669+ else if (*fnp == '!')
670+ {
671+ sprintf (errmsg, "Invalid redirection");
672+ return ERR;
673+ }
674+ GET_COMMAND_SUFFIX ();
675+ if (*fnp)
676+ strcpy (old_filename, fnp);
677+ printf ("%s\n", strip_escapes (old_filename));
678+ break;
679+ case 'g':
680+ case 'v':
681+ case 'G':
682+ case 'V':
683+ if (isglobal)
684+ {
685+ sprintf (errmsg, "Cannot nest global commands");
686+ return ERR;
687+ }
688+ else if (check_addr_range (1, addr_last) < 0)
689+ return ERR;
690+ else if (build_active_list (c == 'g' || c == 'G') < 0)
691+ return ERR;
692+ else if ((n = (c == 'G' || c == 'V')))
693+ GET_COMMAND_SUFFIX ();
694+ isglobal++;
695+ if (exec_global (n, gflag) < 0)
696+ return ERR;
697+ break;
698+ case 'h':
699+ if (addr_cnt > 0)
700+ {
701+ sprintf (errmsg, "Unexpected address");
702+ return ERR;
703+ }
704+ GET_COMMAND_SUFFIX ();
705+ if (*errmsg)
706+ fprintf (stderr, "%s\n", errmsg);
707+ break;
708+ case 'H':
709+ if (addr_cnt > 0)
710+ {
711+ sprintf (errmsg, "Unexpected address");
712+ return ERR;
713+ }
714+ GET_COMMAND_SUFFIX ();
715+ if ((garrulous = 1 - garrulous) && *errmsg)
716+ fprintf (stderr, "%s\n", errmsg);
717+ break;
718+ case 'i':
719+ if (second_addr == 0)
720+ {
721+ sprintf (errmsg, "Invalid address");
722+ return ERR;
723+ }
724+ GET_COMMAND_SUFFIX ();
725+ if (!isglobal)
726+ clear_undo_stack ();
727+ if (append_lines (second_addr - 1) < 0)
728+ return ERR;
729+ break;
730+ case 'j':
731+ if (check_addr_range (current_addr, current_addr + 1) < 0)
732+ return ERR;
733+ GET_COMMAND_SUFFIX ();
734+ if (!isglobal)
735+ clear_undo_stack ();
736+ if (first_addr != second_addr &&
737+ join_lines (first_addr, second_addr) < 0)
738+ return ERR;
739+ break;
740+ case 'k':
741+ c = *ibufp++;
742+ if (second_addr == 0)
743+ {
744+ sprintf (errmsg, "Invalid address");
745+ return ERR;
746+ }
747+ GET_COMMAND_SUFFIX ();
748+ if (mark_line_node (get_addressed_line_node (second_addr), c) < 0)
749+ return ERR;
750+ break;
751+ case 'l':
752+ if (check_addr_range (current_addr, current_addr) < 0)
753+ return ERR;
754+ GET_COMMAND_SUFFIX ();
755+ if (display_lines (first_addr, second_addr, gflag | GLS) < 0)
756+ return ERR;
757+ gflag = 0;
758+ break;
759+ case 'm':
760+ if (check_addr_range (current_addr, current_addr) < 0)
761+ return ERR;
762+ GET_THIRD_ADDR (addr);
763+ if (first_addr <= addr && addr < second_addr)
764+ {
765+ sprintf (errmsg, "Invalid destination");
766+ return ERR;
767+ }
768+ GET_COMMAND_SUFFIX ();
769+ if (!isglobal)
770+ clear_undo_stack ();
771+ if (move_lines (addr) < 0)
772+ return ERR;
773+ break;
774+ case 'n':
775+ if (check_addr_range (current_addr, current_addr) < 0)
776+ return ERR;
777+ GET_COMMAND_SUFFIX ();
778+ if (display_lines (first_addr, second_addr, gflag | GNP) < 0)
779+ return ERR;
780+ gflag = 0;
781+ break;
782+ case 'p':
783+ if (check_addr_range (current_addr, current_addr) < 0)
784+ return ERR;
785+ GET_COMMAND_SUFFIX ();
786+ if (display_lines (first_addr, second_addr, gflag | GPR) < 0)
787+ return ERR;
788+ gflag = 0;
789+ break;
790+ case 'P':
791+ if (addr_cnt > 0)
792+ {
793+ sprintf (errmsg, "Unexpected address");
794+ return ERR;
795+ }
796+ GET_COMMAND_SUFFIX ();
797+ prompt = prompt ? NULL : optarg ? optarg : dps;
798+ break;
799+ case 'q':
800+ case 'Q':
801+ if (addr_cnt > 0)
802+ {
803+ sprintf (errmsg, "Unexpected address");
804+ return ERR;
805+ }
806+ GET_COMMAND_SUFFIX ();
807+ gflag = (modified && !scripted && c == 'q') ? EMOD : EOF;
808+ break;
809+ case 'r':
810+ if (!isspace (*ibufp))
811+ {
812+ sprintf (errmsg, "Unexpected command suffix");
813+ return ERR;
814+ }
815+ else if (addr_cnt == 0)
816+ second_addr = addr_last;
817+ if ((fnp = get_filename ()) == NULL)
818+ return ERR;
819+ GET_COMMAND_SUFFIX ();
820+ if (!isglobal)
821+ clear_undo_stack ();
822+ if (*old_filename == '\0' && *fnp != '!')
823+ strcpy (old_filename, fnp);
824+ if (traditional && *fnp == '\0' && *old_filename == '\0')
825+ {
826+ sprintf (errmsg, "No current filename");
827+ return ERR;
828+ }
829+ if ((addr = read_file (*fnp ? fnp : old_filename, second_addr)) < 0)
830+ return ERR;
831+ else if (addr && addr != addr_last)
832+ modified = 1;
833+ break;
834+ case 's':
835+ do
836+ {
837+ switch (*ibufp)
838+ {
839+ case '\n':
840+ sflags |= SGF;
841+ break;
842+ case 'g':
843+ sflags |= SGG;
844+ ibufp++;
845+ break;
846+ case 'p':
847+ sflags |= SGP;
848+ ibufp++;
849+ break;
850+ case 'r':
851+ sflags |= SGR;
852+ ibufp++;
853+ break;
854+ case '0':
855+ case '1':
856+ case '2':
857+ case '3':
858+ case '4':
859+ case '5':
860+ case '6':
861+ case '7':
862+ case '8':
863+ case '9': {
864+ long sgnum_long;
865+ STRTOL (sgnum_long, ibufp);
866+ sgnum = sgnum_long;
867+ sflags |= SGF;
868+ sgflag &= ~GSG; /* override GSG */
869+ break;
870+ }
871+ default:
872+ if (sflags)
873+ {
874+ sprintf (errmsg, "Invalid command suffix");
875+ return ERR;
876+ }
877+ }
878+ }
879+ while (sflags && *ibufp != '\n');
880+ if (sflags && !pat)
881+ {
882+ sprintf (errmsg, "No previous substitution");
883+ return ERR;
884+ }
885+ else if (sflags & SGG)
886+ sgnum = 0; /* override numeric arg */
887+ if (*ibufp != '\n' && *(ibufp + 1) == '\n')
888+ {
889+ sprintf (errmsg, "Invalid pattern delimiter");
890+ return ERR;
891+ }
892+ tpat = pat;
893+ SPL1 ();
894+ if ((!sflags || (sflags & SGR)) &&
895+ (tpat = get_compiled_pattern ()) == NULL)
896+ {
897+ SPL0 ();
898+ return ERR;
899+ }
900+ else if (tpat != pat)
901+ {
902+ if (pat)
903+ {
904+ regfree (pat);
905+ free (pat);
906+ }
907+ pat = tpat;
908+ patlock = 1; /* reserve pattern */
909+ }
910+ SPL0 ();
911+ if (!sflags && extract_subst_tail (&sgflag, &sgnum) < 0)
912+ return ERR;
913+ else if (isglobal)
914+ sgflag |= GLB;
915+ else
916+ sgflag &= ~GLB;
917+ if (sflags & SGG)
918+ sgflag ^= GSG;
919+ if (sflags & SGP)
920+ sgflag ^= GPR, sgflag &= ~(GLS | GNP);
921+ do
922+ {
923+ switch (*ibufp)
924+ {
925+ case 'p':
926+ sgflag |= GPR, ibufp++;
927+ break;
928+ case 'l':
929+ sgflag |= GLS, ibufp++;
930+ break;
931+ case 'n':
932+ sgflag |= GNP, ibufp++;
933+ break;
934+ default:
935+ n++;
936+ }
937+ }
938+ while (!n);
939+ if (check_addr_range (current_addr, current_addr) < 0)
940+ return ERR;
941+ GET_COMMAND_SUFFIX ();
942+ if (!isglobal)
943+ clear_undo_stack ();
944+ if (search_and_replace (pat, sgflag, sgnum) < 0)
945+ return ERR;
946+ break;
947+ case 't':
948+ if (check_addr_range (current_addr, current_addr) < 0)
949+ return ERR;
950+ GET_THIRD_ADDR (addr);
951+ GET_COMMAND_SUFFIX ();
952+ if (!isglobal)
953+ clear_undo_stack ();
954+ if (copy_lines (addr) < 0)
955+ return ERR;
956+ break;
957+ case 'u':
958+ if (addr_cnt > 0)
959+ {
960+ sprintf (errmsg, "Unexpected address");
961+ return ERR;
962+ }
963+ GET_COMMAND_SUFFIX ();
964+ if (pop_undo_stack () < 0)
965+ return ERR;
966+ break;
967+ case 'w':
968+ case 'W':
969+ if ((n = *ibufp) == 'q' || n == 'Q')
970+ {
971+ gflag = EOF;
972+ ibufp++;
973+ }
974+ if (!isspace (*ibufp))
975+ {
976+ sprintf (errmsg, "Unexpected command suffix");
977+ return ERR;
978+ }
979+ else if ((fnp = get_filename ()) == NULL)
980+ return ERR;
981+ if (addr_cnt == 0 && !addr_last)
982+ first_addr = second_addr = 0;
983+ else if (check_addr_range (1, addr_last) < 0)
984+ return ERR;
985+ GET_COMMAND_SUFFIX ();
986+ if (*old_filename == '\0' && *fnp != '!')
987+ strcpy (old_filename, fnp);
988+ if (traditional && *fnp == '\0' && *old_filename == '\0')
989+ {
990+ sprintf (errmsg, "No current filename");
991+ return ERR;
992+ }
993+ if ((addr = write_file (*fnp ? fnp : old_filename,
994+ (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0)
995+ return ERR;
996+ else if (addr == addr_last)
997+ modified = 0;
998+ else if (modified && !scripted && n == 'q')
999+ gflag = EMOD;
1000+ break;
1001+ case 'x':
1002+ if (second_addr < 0 || addr_last < second_addr)
1003+ {
1004+ sprintf (errmsg, "Invalid address");
1005+ return ERR;
1006+ }
1007+ GET_COMMAND_SUFFIX ();
1008+ if (!isglobal)
1009+ clear_undo_stack ();
1010+ if (put_lines (second_addr) < 0)
1011+ return ERR;
1012+ break;
1013+ case 'y':
1014+ if (check_addr_range (current_addr, current_addr) < 0)
1015+ return ERR;
1016+ GET_COMMAND_SUFFIX ();
1017+ if (yank_lines (first_addr, second_addr) < 0)
1018+ return ERR;
1019+ break;
1020+ case 'z':
1021+ if (check_addr_range
1022+ (first_addr = 1, current_addr + (traditional ? 1 : !isglobal)) < 0)
1023+ return ERR;
1024+ else if ('0' < *ibufp && *ibufp <= '9')
1025+ STRTOL (rows, ibufp);
1026+ GET_COMMAND_SUFFIX ();
1027+ if (display_lines (second_addr, min (addr_last,
1028+ second_addr + rows), gflag) < 0)
1029+ return ERR;
1030+ gflag = 0;
1031+ break;
1032+ case '=':
1033+ GET_COMMAND_SUFFIX ();
1034+ printf ("%ld\n", addr_cnt ? second_addr : addr_last);
1035+ break;
1036+ case '!':
1037+ if (addr_cnt > 0)
1038+ {
1039+ sprintf (errmsg, "Unexpected address");
1040+ return ERR;
1041+ }
1042+ else if ((sflags = get_shell_command ()) < 0)
1043+ return ERR;
1044+ GET_COMMAND_SUFFIX ();
1045+ if (sflags)
1046+ printf ("%s\n", shcmd + 1);
1047+ system (shcmd + 1);
1048+ if (!scripted)
1049+ printf ("!\n");
1050+ break;
1051+ case '\n':
1052+ if (check_addr_range
1053+ (first_addr = 1, current_addr + (traditional ? 1 : !isglobal)) < 0 ||
1054+ display_lines (second_addr, second_addr, 0) < 0)
1055+ return ERR;
1056+ break;
1057+ case '#':
1058+ while (*ibufp++ != '\n')
1059+ ;
1060+ break;
1061+ default:
1062+ sprintf (errmsg, "Unknown command");
1063+ return ERR;
1064+ }
1065+ return gflag;
1066+}
1067+
1068+
1069+/* check_addr_range: return status of address range check */
1070+int
1071+check_addr_range (n, m)
1072+ long n, m;
1073+{
1074+ if (addr_cnt == 0)
1075+ {
1076+ first_addr = n;
1077+ second_addr = m;
1078+ }
1079+ if (first_addr > second_addr || 1 > first_addr ||
1080+ second_addr > addr_last)
1081+ {
1082+ sprintf (errmsg, "Invalid address");
1083+ return ERR;
1084+ }
1085+ return 0;
1086+}
1087+
1088+
1089+/* get_matching_node_addr: return the address of the next line matching a
1090+ pattern in a given direction. wrap around begin/end of editor buffer if
1091+ necessary */
1092+long
1093+get_matching_node_addr (pat, dir)
1094+ pattern_t *pat;
1095+ int dir;
1096+{
1097+ char *s;
1098+ long n = current_addr;
1099+ line_t *lp;
1100+
1101+ if (!pat)
1102+ return ERR;
1103+ do
1104+ {
1105+ if ((n = dir ? INC_MOD (n, addr_last) : DEC_MOD (n, addr_last)))
1106+ {
1107+ lp = get_addressed_line_node (n);
1108+ if ((s = get_sbuf_line (lp)) == NULL)
1109+ return ERR;
1110+ if (isbinary)
1111+ NUL_TO_NEWLINE (s, lp->len);
1112+ if (!regexec (pat, s, 0, NULL, 0))
1113+ return n;
1114+ }
1115+ }
1116+ while (n != current_addr);
1117+ sprintf (errmsg, "No match");
1118+ return ERR;
1119+}
1120+
1121+
1122+/* get_filename: return pointer to copy of filename in the command buffer */
1123+char *
1124+get_filename ()
1125+{
1126+ static char *file = NULL;
1127+ static int filesz = 0;
1128+
1129+ int n;
1130+
1131+ if (*ibufp != '\n')
1132+ {
1133+ SKIP_BLANKS ();
1134+ if (*ibufp == '\n')
1135+ {
1136+ sprintf (errmsg, "Invalid filename");
1137+ return NULL;
1138+ }
1139+ else if ((ibufp = get_extended_line (&n, 1)) == NULL)
1140+ return NULL;
1141+ else if (*ibufp == '!')
1142+ {
1143+ ibufp++;
1144+ if ((n = get_shell_command ()) < 0)
1145+ return NULL;
1146+ if (n)
1147+ printf ("%s\n", shcmd + 1);
1148+ return shcmd;
1149+ }
1150+ else if (n > PATH_MAX)
1151+ {
1152+ sprintf (errmsg, "Filename too long");
1153+ return NULL;
1154+ }
1155+ }
1156+ else if (!traditional && *old_filename == '\0')
1157+ {
1158+ sprintf (errmsg, "No current filename");
1159+ return NULL;
1160+ }
1161+ REALLOC (file, filesz, PATH_MAX + 1, NULL);
1162+ for (n = 0; *ibufp != '\n';)
1163+ file[n++] = *ibufp++;
1164+ file[n] = '\0';
1165+ return is_legal_filename (file) ? file : NULL;
1166+}
1167+
1168+
1169+/* get_shell_command: read a shell command from stdin; return substitution
1170+ status */
1171+int
1172+get_shell_command ()
1173+{
1174+ static char *buf = NULL;
1175+ static int n = 0;
1176+
1177+ char *s; /* substitution char pointer */
1178+ int i = 0;
1179+ int j = 0;
1180+
1181+ if (red)
1182+ {
1183+ sprintf (errmsg, "Shell access restricted");
1184+ return ERR;
1185+ }
1186+ else if ((s = ibufp = get_extended_line (&j, 1)) == NULL)
1187+ return ERR;
1188+ REALLOC (buf, n, j + 1, ERR);
1189+ buf[i++] = '!'; /* prefix command w/ bang */
1190+ while (*ibufp != '\n')
1191+ switch (*ibufp)
1192+ {
1193+ default:
1194+ REALLOC (buf, n, i + 2, ERR);
1195+ buf[i++] = *ibufp;
1196+ if (*ibufp++ == '\\')
1197+ buf[i++] = *ibufp++;
1198+ break;
1199+ case '!':
1200+ if (s != ibufp)
1201+ {
1202+ REALLOC (buf, n, i + 1, ERR);
1203+ buf[i++] = *ibufp++;
1204+ }
1205+ else if (shcmd == NULL || (traditional && *(shcmd + 1) == '\0'))
1206+ {
1207+ sprintf (errmsg, "No previous command");
1208+ return ERR;
1209+ }
1210+ else
1211+ {
1212+ REALLOC (buf, n, i + shcmdi, ERR);
1213+ for (s = shcmd + 1; s < shcmd + shcmdi;)
1214+ buf[i++] = *s++;
1215+ s = ibufp++;
1216+ }
1217+ break;
1218+ case '%':
1219+ if (*old_filename == '\0')
1220+ {
1221+ sprintf (errmsg, "No current filename");
1222+ return ERR;
1223+ }
1224+ j = strlen (s = strip_escapes (old_filename));
1225+ REALLOC (buf, n, i + j, ERR);
1226+ while (j--)
1227+ buf[i++] = *s++;
1228+ s = ibufp++;
1229+ break;
1230+ }
1231+ REALLOC (shcmd, shcmdsz, i + 1, ERR);
1232+ memcpy (shcmd, buf, i);
1233+ shcmd[shcmdi = i] = '\0';
1234+ return *s == '!' || *s == '%';
1235+}
1236+
1237+
1238+/* append_lines: insert text from stdin to after line n; stop when either a
1239+ single period is read or EOF; return status */
1240+int
1241+append_lines (n)
1242+ long n;
1243+{
1244+ int l;
1245+ char *lp = ibuf;
1246+ char *eot;
1247+ undo_t *up = NULL;
1248+
1249+ for (current_addr = n;;)
1250+ {
1251+ if (!isglobal)
1252+ {
1253+ if ((l = get_tty_line ()) < 0)
1254+ return ERR;
1255+ else if (l == 0 || ibuf[l - 1] != '\n')
1256+ {
1257+ clearerr (stdin);
1258+ return l ? EOF : 0;
1259+ }
1260+ lp = ibuf;
1261+ }
1262+ else if (*(lp = ibufp) == '\0')
1263+ return 0;
1264+ else
1265+ {
1266+ while (*ibufp++ != '\n')
1267+ ;
1268+ l = ibufp - lp;
1269+ }
1270+ if (l == 2 && lp[0] == '.' && lp[1] == '\n')
1271+ {
1272+ return 0;
1273+ }
1274+ eot = lp + l;
1275+ SPL1 ();
1276+ do
1277+ {
1278+ if ((lp = put_sbuf_line (lp)) == NULL)
1279+ {
1280+ SPL0 ();
1281+ return ERR;
1282+ }
1283+ else if (up)
1284+ up->t = get_addressed_line_node (current_addr);
1285+ else if ((up = push_undo_stack (UADD, current_addr,
1286+ current_addr)) == NULL)
1287+ {
1288+ SPL0 ();
1289+ return ERR;
1290+ }
1291+ }
1292+ while (lp != eot);
1293+ modified = 1;
1294+ SPL0 ();
1295+ }
1296+ /* NOTREACHED */
1297+}
1298+
1299+
1300+/* join_lines: replace a range of lines with the joined text of those lines */
1301+int
1302+join_lines (from, to)
1303+ long from;
1304+ long to;
1305+{
1306+ static char *buf = NULL;
1307+ static int n;
1308+
1309+ char *s;
1310+ int size = 0;
1311+ line_t *bp, *ep;
1312+
1313+ ep = get_addressed_line_node (INC_MOD (to, addr_last));
1314+ bp = get_addressed_line_node (from);
1315+ for (; bp != ep; bp = bp->q_forw)
1316+ {
1317+ if ((s = get_sbuf_line (bp)) == NULL)
1318+ return ERR;
1319+ REALLOC (buf, n, size + bp->len, ERR);
1320+ memcpy (buf + size, s, bp->len);
1321+ size += bp->len;
1322+ }
1323+ REALLOC (buf, n, size + 2, ERR);
1324+ memcpy (buf + size, "\n", 2);
1325+ if (delete_lines (from, to) < 0)
1326+ return ERR;
1327+ current_addr = from - 1;
1328+ SPL1 ();
1329+ if (put_sbuf_line (buf) == NULL ||
1330+ push_undo_stack (UADD, current_addr, current_addr) == NULL)
1331+ {
1332+ SPL0 ();
1333+ return ERR;
1334+ }
1335+ modified = 1;
1336+ SPL0 ();
1337+ return 0;
1338+}
1339+
1340+
1341+/* move_lines: move a range of lines */
1342+int
1343+move_lines (addr)
1344+ long addr;
1345+{
1346+ line_t *b1, *a1, *b2, *a2;
1347+ long n = INC_MOD (second_addr, addr_last);
1348+ long p = first_addr - 1;
1349+ int done = (addr == first_addr - 1 || addr == second_addr);
1350+
1351+ SPL1 ();
1352+ if (done)
1353+ {
1354+ a2 = get_addressed_line_node (n);
1355+ b2 = get_addressed_line_node (p);
1356+ current_addr = second_addr;
1357+ }
1358+ else if (push_undo_stack (UMOV, p, n) == NULL ||
1359+ push_undo_stack (UMOV, addr, INC_MOD (addr, addr_last)) == NULL)
1360+ {
1361+ SPL0 ();
1362+ return ERR;
1363+ }
1364+ else
1365+ {
1366+ a1 = get_addressed_line_node (n);
1367+ if (addr < first_addr)
1368+ {
1369+ b1 = get_addressed_line_node (p);
1370+ b2 = get_addressed_line_node (addr);
1371+ /* this get_addressed_line_node last! */
1372+ }
1373+ else
1374+ {
1375+ b2 = get_addressed_line_node (addr);
1376+ b1 = get_addressed_line_node (p);
1377+ /* this get_addressed_line_node last! */
1378+ }
1379+ a2 = b2->q_forw;
1380+ REQUE (b2, b1->q_forw);
1381+ REQUE (a1->q_back, a2);
1382+ REQUE (b1, a1);
1383+ current_addr = addr + ((addr < first_addr) ?
1384+ second_addr - first_addr + 1 : 0);
1385+ }
1386+ if (isglobal)
1387+ unset_active_nodes (b2->q_forw, a2);
1388+ modified = 1;
1389+ SPL0 ();
1390+ return 0;
1391+}
1392+
1393+
1394+/* copy_lines: copy a range of lines; return status */
1395+int
1396+copy_lines (addr)
1397+ long addr;
1398+{
1399+ line_t *lp, *np = get_addressed_line_node (first_addr);
1400+ undo_t *up = NULL;
1401+ long n = second_addr - first_addr + 1;
1402+ long m = 0;
1403+
1404+ current_addr = addr;
1405+ if (first_addr <= addr && addr < second_addr)
1406+ {
1407+ n = addr - first_addr + 1;
1408+ m = second_addr - addr;
1409+ }
1410+ for (; n > 0; n = m, m = 0, np = get_addressed_line_node (current_addr + 1))
1411+ for (; n-- > 0; np = np->q_forw)
1412+ {
1413+ SPL1 ();
1414+ if ((lp = dup_line_node (np)) == NULL)
1415+ {
1416+ SPL0 ();
1417+ return ERR;
1418+ }
1419+ add_line_node (lp);
1420+ if (up)
1421+ up->t = lp;
1422+ else if ((up = push_undo_stack (UADD, current_addr,
1423+ current_addr)) == NULL)
1424+ {
1425+ SPL0 ();
1426+ return ERR;
1427+ }
1428+ modified = 1;
1429+ SPL0 ();
1430+ }
1431+ return 0;
1432+}
1433+
1434+
1435+/* delete_lines: delete a range of lines */
1436+int
1437+delete_lines (from, to)
1438+ long from, to;
1439+{
1440+ line_t *n, *p;
1441+
1442+ if (yank_lines (from, to) < 0)
1443+ return ERR;
1444+ SPL1 ();
1445+ if (push_undo_stack (UDEL, from, to) == NULL)
1446+ {
1447+ SPL0 ();
1448+ return ERR;
1449+ }
1450+ n = get_addressed_line_node (INC_MOD (to, addr_last));
1451+ p = get_addressed_line_node (from - 1);
1452+ /* this get_addressed_line_node last! */
1453+ if (isglobal)
1454+ unset_active_nodes (p->q_forw, n);
1455+ REQUE (p, n);
1456+ addr_last -= to - from + 1;
1457+ current_addr = from - 1;
1458+ modified = 1;
1459+ SPL0 ();
1460+ return 0;
1461+}
1462+
1463+
1464+int dlcnt = 0; /* # of lines displayed */
1465+
1466+/* display_lines: print a range of lines to stdout */
1467+int
1468+display_lines (from, to, gflag)
1469+ long from;
1470+ long to;
1471+ int gflag;
1472+{
1473+ line_t *bp;
1474+ line_t *ep;
1475+ char *s;
1476+
1477+ if (!from)
1478+ {
1479+ sprintf (errmsg, "Invalid address");
1480+ return ERR;
1481+ }
1482+ ep = get_addressed_line_node (INC_MOD (to, addr_last));
1483+ bp = get_addressed_line_node (from);
1484+ for (dlcnt = 0; bp != ep; bp = bp->q_forw)
1485+ {
1486+ if ((s = get_sbuf_line (bp)) == NULL)
1487+ return ERR;
1488+ else if (put_tty_line (s, bp->len, current_addr = from++, gflag) < 0)
1489+ return ERR;
1490+ else if (!traditional && !scripted && !isglobal &&
1491+ bp->q_forw != ep && ++dlcnt > rows)
1492+ {
1493+ dlcnt = 0;
1494+ fputs ("Press <RETURN> to continue... ", stdout);
1495+ fflush (stdout);
1496+ if (get_tty_line () < 0)
1497+ return ERR;
1498+ }
1499+ }
1500+ return 0;
1501+}
1502+
1503+
1504+line_t yank_buffer_head; /* head of yank buffer */
1505+
1506+/* yank_lines: copy a range of lines to the cut buffer */
1507+int
1508+yank_lines (from, to)
1509+ long from;
1510+ long to;
1511+{
1512+ line_t *bp, *cp, *ep, *lp;
1513+
1514+
1515+ delete_yank_lines ();
1516+ ep = get_addressed_line_node (INC_MOD (to, addr_last));
1517+ bp = get_addressed_line_node (from);
1518+ for (lp = &yank_buffer_head; bp != ep; bp = bp->q_forw, lp = cp)
1519+ {
1520+ SPL1 ();
1521+ if ((cp = dup_line_node (bp)) == NULL)
1522+ {
1523+ SPL0 ();
1524+ return ERR;
1525+ }
1526+ INSQUE (cp, lp);
1527+ SPL0 ();
1528+ }
1529+ return 0;
1530+}
1531+
1532+
1533+/* delete_yank_lines: delete lines from the yank buffer */
1534+void
1535+delete_yank_lines ()
1536+{
1537+ line_t *cp, *lp;
1538+
1539+
1540+ for (lp = yank_buffer_head.q_forw; lp != &yank_buffer_head; lp = cp)
1541+ {
1542+ SPL1 ();
1543+ cp = lp->q_forw;
1544+ REQUE (lp->q_back, lp->q_forw);
1545+ free (lp);
1546+ SPL0 ();
1547+ }
1548+}
1549+
1550+
1551+/* put_lines: append lines from the yank buffer */
1552+int
1553+put_lines (addr)
1554+ long addr;
1555+{
1556+ undo_t *up = NULL;
1557+ line_t *lp, *cp;
1558+
1559+ if ((lp = yank_buffer_head.q_forw) == &yank_buffer_head)
1560+ {
1561+ sprintf (errmsg, "Nothing to put");
1562+ return ERR;
1563+ }
1564+ current_addr = addr;
1565+ for (; lp != &yank_buffer_head; lp = lp->q_forw)
1566+ {
1567+ SPL1 ();
1568+ if ((cp = dup_line_node (lp)) == NULL)
1569+ {
1570+ SPL0 ();
1571+ return ERR;
1572+ }
1573+ add_line_node (cp);
1574+ if (up)
1575+ up->t = cp;
1576+ else if ((up = push_undo_stack (UADD, current_addr,
1577+ current_addr)) == NULL)
1578+ {
1579+ SPL0 ();
1580+ return ERR;
1581+ }
1582+ modified = 1;
1583+ SPL0 ();
1584+ }
1585+ return 0;
1586+}
1587+
1588+
1589+#define MAXMARK 26 /* max number of marks */
1590+
1591+line_t *mark[MAXMARK]; /* line markers */
1592+int markno; /* line marker count */
1593+
1594+/* mark_line_node: set a line node mark */
1595+int
1596+mark_line_node (lp, n)
1597+ line_t *lp;
1598+ int n;
1599+{
1600+ if (!islower (n))
1601+ {
1602+ sprintf (errmsg, "Invalid mark character");
1603+ return ERR;
1604+ }
1605+ else if (mark[n - 'a'] == NULL)
1606+ markno++;
1607+ mark[n - 'a'] = lp;
1608+ return 0;
1609+}
1610+
1611+
1612+/* get_marked_node_addr: return address of a marked line */
1613+long
1614+get_marked_node_addr (n)
1615+ int n;
1616+{
1617+ if (!islower (n))
1618+ {
1619+ sprintf (errmsg, "Invalid mark character");
1620+ return ERR;
1621+ }
1622+ return get_line_node_addr (mark[n - 'a']);
1623+}
1624+
1625+
1626+/* unmark_line_node: clear line node mark */
1627+void
1628+unmark_line_node (lp)
1629+ line_t *lp;
1630+{
1631+ int i;
1632+
1633+ for (i = 0; markno && i < MAXMARK; i++)
1634+ if (mark[i] == lp)
1635+ {
1636+ mark[i] = NULL;
1637+ markno--;
1638+ }
1639+}
1640+
1641+
1642+/* dup_line_node: return a pointer to a copy of a line node */
1643+line_t *
1644+dup_line_node (lp)
1645+ line_t *lp;
1646+{
1647+ line_t *np;
1648+
1649+ if ((np = (line_t *) malloc (sizeof (line_t))) == NULL)
1650+ {
1651+ fprintf (stderr, "%s\n", strerror (errno));
1652+ sprintf (errmsg, "Out of memory");
1653+ return NULL;
1654+ }
1655+ np->seek = lp->seek;
1656+ np->len = lp->len;
1657+ return np;
1658+}
1659+
1660+
1661+/* has_trailing_escape: return the parity of escapes preceding a character
1662+ in a string */
1663+int
1664+has_trailing_escape (s, t)
1665+ char *s;
1666+ char *t;
1667+{
1668+ return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape (s, t - 1);
1669+}
1670+
1671+
1672+/* strip_escapes: return copy of escaped string of at most length PATH_MAX */
1673+char *
1674+strip_escapes (s)
1675+ char *s;
1676+{
1677+ static char *file = NULL;
1678+ static int filesz = 0;
1679+
1680+ int i = 0;
1681+
1682+ REALLOC (file, filesz, PATH_MAX + 1, NULL);
1683+ /* assert: no trailing escape */
1684+ while ((file[i++] = (*s == '\\') ? *++s : *s))
1685+ s++;
1686+ return file;
1687+}
1688+
1689+
1690+#ifndef S_ISREG
1691+#define S_ISREG(m) ((m & S_IFMT) == S_IFREG)
1692+#endif
1693+
1694+/* is_regular_file: if file descriptor of a regular file, then return true;
1695+ otherwise return false */
1696+int
1697+is_regular_file (fd)
1698+ int fd;
1699+{
1700+ struct stat sb;
1701+
1702+ return fstat (fd, &sb) < 0 || S_ISREG (sb.st_mode);
1703+}
1704+
1705+
1706+/* is_legal_filename: return a legal filename */
1707+int
1708+is_legal_filename (s)
1709+ char *s;
1710+{
1711+ if (red && (*s == '!' || !strcmp (s, "..") || strchr (s, '/')))
1712+ {
1713+ sprintf (errmsg, "Shell access restricted");
1714+ return 0;
1715+ }
1716+ return 1;
1717+}
1718+
1719+FILE *sfp; /* scratch file pointer */
1720+off_t sfseek; /* scratch file position */
1721+int seek_write; /* seek before writing */
1722+line_t buffer_head; /* incore buffer */
1723+
1724+/* get_sbuf_line: get a line of text from the scratch file; return pointer
1725+ to the text */
1726+char *
1727+get_sbuf_line (lp)
1728+ line_t *lp;
1729+{
1730+ static char *sfbuf = NULL; /* buffer */
1731+ static int sfbufsz = 0; /* buffer size */
1732+
1733+ int len, ct;
1734+
1735+ if (lp == &buffer_head)
1736+ return NULL;
1737+ seek_write = 1; /* force seek on write */
1738+ /* out of position */
1739+ if (sfseek != lp->seek)
1740+ {
1741+ sfseek = lp->seek;
1742+ if (fseek (sfp, sfseek, SEEK_SET) < 0)
1743+ {
1744+ fprintf (stderr, "%s\n", strerror (errno));
1745+ sprintf (errmsg, "Cannot seek temp file");
1746+ return NULL;
1747+ }
1748+ }
1749+ len = lp->len;
1750+ REALLOC (sfbuf, sfbufsz, len + 1, NULL);
1751+ if ((ct = fread (sfbuf, sizeof (char), len, sfp)) < 0 || ct != len)
1752+ {
1753+ fprintf (stderr, "%s\n", strerror (errno));
1754+ sprintf (errmsg, "Cannot read temp file");
1755+ return NULL;
1756+ }
1757+ sfseek += len; /* update file position */
1758+ sfbuf[len] = '\0';
1759+ return sfbuf;
1760+}
1761+
1762+
1763+/* put_sbuf_line: write a line of text to the scratch file and add a line node
1764+ to the editor buffer; return a pointer to the end of the text */
1765+char *
1766+put_sbuf_line (cs)
1767+ char *cs;
1768+{
1769+ line_t *lp;
1770+ int len, ct;
1771+ char *s;
1772+
1773+ if ((lp = (line_t *) malloc (sizeof (line_t))) == NULL)
1774+ {
1775+ fprintf (stderr, "%s\n", strerror (errno));
1776+ sprintf (errmsg, "Out of memory");
1777+ return NULL;
1778+ }
1779+ /* assert: cs is '\n' terminated */
1780+ for (s = cs; *s != '\n'; s++)
1781+ ;
1782+ if (s - cs >= LINECHARS)
1783+ {
1784+ sprintf (errmsg, "Line too long");
1785+ return NULL;
1786+ }
1787+ len = s - cs;
1788+ /* out of position */
1789+ if (seek_write)
1790+ {
1791+ if (fseek (sfp, 0L, SEEK_END) < 0)
1792+ {
1793+ fprintf (stderr, "%s\n", strerror (errno));
1794+ sprintf (errmsg, "Cannot seek temp file");
1795+ return NULL;
1796+ }
1797+ sfseek = ftell (sfp);
1798+ seek_write = 0;
1799+ }
1800+ /* assert: SPL1() */
1801+ if ((ct = fwrite (cs, sizeof (char), len, sfp)) < 0 || ct != len)
1802+ {
1803+ sfseek = -1;
1804+ fprintf (stderr, "%s\n", strerror (errno));
1805+ sprintf (errmsg, "Cannot write temp file");
1806+ return NULL;
1807+ }
1808+ lp->len = len;
1809+ lp->seek = sfseek;
1810+ add_line_node (lp);
1811+ sfseek += len; /* update file position */
1812+ return ++s;
1813+}
1814+
1815+
1816+/* add_line_node: add a line node in the editor buffer after the current line */
1817+void
1818+add_line_node (lp)
1819+ line_t *lp;
1820+{
1821+ line_t *cp;
1822+
1823+ cp = get_addressed_line_node (current_addr); /* this get_addressed_line_node last! */
1824+ INSQUE (lp, cp);
1825+ addr_last++;
1826+ current_addr++;
1827+}
1828+
1829+
1830+/* get_line_node_addr: return line number of pointer */
1831+long
1832+get_line_node_addr (lp)
1833+ line_t *lp;
1834+{
1835+ line_t *cp = &buffer_head;
1836+ long n = 0;
1837+
1838+ while (cp != lp && (cp = cp->q_forw) != &buffer_head)
1839+ n++;
1840+ if (n && cp == &buffer_head)
1841+ {
1842+ sprintf (errmsg, "Invalid address");
1843+ return ERR;
1844+ }
1845+ return n;
1846+}
1847+
1848+
1849+/* get_addressed_line_node: return pointer to a line node in the editor buffer */
1850+line_t *
1851+get_addressed_line_node (n)
1852+ long n;
1853+{
1854+ static line_t *lp = &buffer_head;
1855+ static long on = 0;
1856+
1857+ SPL1 ();
1858+ if (n > on)
1859+ if (n <= (on + addr_last) >> 1)
1860+ for (; on < n; on++)
1861+ lp = lp->q_forw;
1862+ else
1863+ {
1864+ lp = buffer_head.q_back;
1865+ for (on = addr_last; on > n; on--)
1866+ lp = lp->q_back;
1867+ }
1868+ else if (n >= on >> 1)
1869+ for (; on > n; on--)
1870+ lp = lp->q_back;
1871+ else
1872+ {
1873+ lp = &buffer_head;
1874+ for (on = 0; on < n; on++)
1875+ lp = lp->q_forw;
1876+ }
1877+ SPL0 ();
1878+ return lp;
1879+}
1880+
1881+
1882+extern int newline_added;
1883+
1884+/* open_sbuf: open scratch file */
1885+int
1886+open_sbuf ()
1887+{
1888+ char *mktemp ();
1889+ int u;
1890+
1891+ isbinary = newline_added = 0;
1892+ u = umask(077);
1893+ if ((sfp = tmpfile()) == NULL)
1894+ {
1895+ fprintf (stderr, "open_sbuf: tmpfile() failed with '%s'\n", strerror (errno));
1896+ sprintf (errmsg, "Cannot open temp file");
1897+ umask(u);
1898+ return ERR;
1899+ }
1900+ umask(u);
1901+ return 0;
1902+}
1903+
1904+
1905+/* close_sbuf: close scratch file */
1906+int
1907+close_sbuf ()
1908+{
1909+ if (sfp)
1910+ {
1911+ if (fclose (sfp) < 0)
1912+ {
1913+ fprintf (stderr, "close_sbuf: fclose on temporary file failed with '%s'.\n", strerror (errno));
1914+ sprintf (errmsg, "Cannot close temp file");
1915+ return ERR;
1916+ }
1917+ sfp = NULL;
1918+ }
1919+ sfseek = seek_write = 0;
1920+
1921+ return 0;
1922+}
1923+
1924+
1925+/* quit: remove_lines scratch file and exit */
1926+void
1927+quit (n)
1928+ int n;
1929+{
1930+ if (sfp)
1931+ {
1932+ fclose (sfp);
1933+ }
1934+ exit (n);
1935+}
1936+
1937+
1938+extern line_t yank_buffer_head;
1939+extern char *old_filename;
1940+unsigned char ctab[256]; /* character translation table */
1941+
1942+/* init_buffers: open scratch buffer; initialize line queue */
1943+void
1944+init_buffers ()
1945+{
1946+ int i = 0;
1947+
1948+ /* Read stdin one character at a time to avoid i/o contention
1949+ with shell escapes invoked by nonterminal input, e.g.,
1950+ ed - <<EOF
1951+ !cat
1952+ hello, world
1953+ EOF */
1954+#ifdef HAVE_SETBUFFER
1955+ setbuffer (stdin, stdinbuf, 1);
1956+#else
1957+ setvbuf (stdin, stdinbuf, _IONBF, 0);
1958+#endif
1959+ if ((errmsg = (char *) malloc (ERRSZ)) == NULL ||
1960+ (old_filename = (char *) malloc (PATH_MAX + 1)) == NULL)
1961+ {
1962+ fprintf (stderr, "%s\n", strerror (errno));
1963+ quit (2);
1964+ }
1965+ old_filename[0] = '\0';
1966+ if (open_sbuf () < 0)
1967+ quit (2);
1968+ REQUE (&buffer_head, &buffer_head);
1969+ REQUE (&yank_buffer_head, &yank_buffer_head);
1970+ for (i = 0; i < 256; i++)
1971+ ctab[i] = i;
1972+
1973+}
1974+
1975+
1976+/* translit_text: translate characters in a string */
1977+char *
1978+translit_text (s, len, from, to)
1979+ char *s;
1980+ int len;
1981+ int from;
1982+ int to;
1983+{
1984+ static int i = 0;
1985+
1986+ unsigned char *us;
1987+
1988+ ctab[i] = i; /* restore table to initial state */
1989+ ctab[i = from] = to;
1990+ for (us = (unsigned char *) s; len-- > 0; us++)
1991+ *us = ctab[*us];
1992+ return s;
1993+}
1994+
1995+
1996+#ifndef SIG_ERR
1997+# define SIG_ERR ((void (*)()) -1)
1998+#endif /* !SIG_ERR */
1999+
2000+void
2001+(*reliable_signal (sno, hndlr)) ()
2002+ int sno;
2003+ void (*hndlr) ();
2004+{
2005+#ifndef HAVE_SIGACTION
2006+ signal (sno, hndlr);
2007+#else
2008+ struct sigaction sa, osa;
2009+
2010+ sa.sa_handler = hndlr;
2011+ sigemptyset (&sa.sa_mask);
2012+ sa.sa_flags = 0;
2013+#ifdef SA_RESTART
2014+ sa.sa_flags |= SA_RESTART;
2015+#endif
2016+ return (sigaction (sno, &sa, &osa) < 0) ? SIG_ERR : osa.sa_handler;
2017+#endif /* HAVE_SIGACTION */
2018+}
2019+
2020+
2021+void
2022+signal_hup (signo)
2023+ int signo;
2024+{
2025+ if (mutex)
2026+ sigflags |= (1 << (signo - 1));
2027+ else
2028+ handle_hup (signo);
2029+}
2030+
2031+
2032+void
2033+signal_int (signo)
2034+ int signo;
2035+{
2036+ if (mutex)
2037+ sigflags |= (1 << (signo - 1));
2038+ else
2039+ handle_int (signo);
2040+}
2041+
2042+
2043+#ifdef HAVE_SIGSETJMP
2044+extern sigjmp_buf env;
2045+#else
2046+extern jmp_buf env;
2047+#endif
2048+extern int sigactive;
2049+
2050+void
2051+handle_hup (signo)
2052+ int signo;
2053+{
2054+ char *hup = NULL; /* hup filename */
2055+ char *s;
2056+ int n;
2057+
2058+ if (!sigactive)
2059+ quit (1);
2060+ sigflags &= ~(1 << (signo - 1));
2061+ if (addr_last && write_file ("ed.hup", "w", 1, addr_last) < 0 &&
2062+ (s = getenv ("HOME")) != NULL &&
2063+ (n = strlen (s)) + 7 <= PATH_MAX && /* "ed.hup" + '/' */
2064+ (hup = (char *) malloc (n + 8)) != NULL)
2065+ {
2066+ strcpy (hup, s);
2067+ if (n && hup[n - 1] != '/')
2068+ hup[n++] = '/';
2069+ strcpy (hup + n, "ed.hup");
2070+ write_file (hup, "w", 1, addr_last);
2071+ }
2072+ quit (2);
2073+}
2074+
2075+
2076+void
2077+handle_int (signo)
2078+ int signo;
2079+{
2080+ if (!sigactive)
2081+ quit (1);
2082+ sigflags &= ~(1 << (signo - 1));
2083+#ifdef HAVE_SIGSETJMP
2084+ siglongjmp (env, -1);
2085+#else
2086+ longjmp (env, -1);
2087+#endif
2088+}
2089+
2090+
2091+extern long rows;
2092+int cols = 72; /* wrap column */
2093+
2094+void
2095+handle_winch (signo)
2096+ int signo;
2097+{
2098+#ifdef TIOCGWINSZ
2099+ struct winsize ws; /* window size structure */
2100+#endif
2101+
2102+ sigflags &= ~(1 << (signo - 1));
2103+#ifdef TIOCGWINSZ
2104+ if (ioctl (0, TIOCGWINSZ, (char *) &ws) >= 0)
2105+ {
2106+ if (ws.ws_row > 2)
2107+ rows = ws.ws_row - 2;
2108+ if (ws.ws_col > 8)
2109+ cols = ws.ws_col - 8;
2110+ }
2111+#endif
2112+}
2113+
2114+/* read_file: read a named file/pipe into the buffer; return line count */
2115+long
2116+read_file (fn, n)
2117+ char *fn;
2118+ long n;
2119+{
2120+ FILE *fp;
2121+ long size;
2122+
2123+
2124+ fp = (*fn == '!') ? popen (fn + 1, "r") : fopen (strip_escapes (fn), "r");
2125+ if (fp == NULL)
2126+ {
2127+ fprintf (stderr, "%s: %s\n", fn, strerror (errno));
2128+ sprintf (errmsg, "Cannot open input file");
2129+ return ERR;
2130+ }
2131+ else if ((size = read_stream (fp, n)) < 0)
2132+ return ERR;
2133+ else if (((*fn == '!') ? pclose (fp) : fclose (fp)) < 0)
2134+ {
2135+ fprintf (stderr, "%s: %s\n", fn, strerror (errno));
2136+ sprintf (errmsg, "Cannot close input file");
2137+ return ERR;
2138+ }
2139+ fprintf (stderr, !scripted ? "%lu\n" : "", size);
2140+ return current_addr - n;
2141+}
2142+
2143+
2144+char *sbuf; /* file i/o buffer */
2145+int sbufsz; /* file i/o buffer size */
2146+int newline_added; /* if set, newline appended to input file */
2147+
2148+/* read_stream: read a stream into the editor buffer; return status */
2149+long
2150+read_stream (fp, n)
2151+ FILE *fp;
2152+ long n;
2153+{
2154+ line_t *lp = get_addressed_line_node (n);
2155+ undo_t *up = NULL;
2156+ unsigned long size = 0;
2157+ int o_newline_added = newline_added;
2158+ int o_isbinary = isbinary;
2159+ int o_n = n;
2160+ int appended = n == addr_last;
2161+ int len;
2162+
2163+ isbinary = newline_added = 0;
2164+ for (current_addr = n; (len = get_stream_line (fp)) > 0; size += len)
2165+ {
2166+ SPL1 ();
2167+ if (put_sbuf_line (sbuf) == NULL)
2168+ {
2169+ SPL0 ();
2170+ return ERR;
2171+ }
2172+ lp = lp->q_forw;
2173+ if (up)
2174+ up->t = lp;
2175+ else if ((up = push_undo_stack (UADD, current_addr,
2176+ current_addr)) == NULL)
2177+ {
2178+ SPL0 ();
2179+ return ERR;
2180+ }
2181+ SPL0 ();
2182+ }
2183+ if (len < 0)
2184+ return ERR;
2185+ if (o_n && appended && size && o_isbinary && o_newline_added)
2186+ fputs ("Newline inserted\n", stderr);
2187+ else if (newline_added && (!appended || (!isbinary && !o_isbinary)))
2188+ fputs ("Newline appended\n", stderr);
2189+ if (isbinary && newline_added && !appended)
2190+ size += 1;
2191+ if (!size)
2192+ newline_added = 1;
2193+ newline_added = appended ? newline_added : o_newline_added;
2194+ isbinary = isbinary | o_isbinary;
2195+ return size;
2196+}
2197+
2198+
2199+/* get_stream_line: read a line of text from a stream; return line length */
2200+int
2201+get_stream_line (fp)
2202+ FILE *fp;
2203+{
2204+ register int c;
2205+ register int i = 0;
2206+
2207+ while (((c = getc (fp)) != EOF || (!feof (fp) &&
2208+ !ferror (fp))) && c != '\n')
2209+ {
2210+ REALLOC (sbuf, sbufsz, i + 1, ERR);
2211+ if (!(sbuf[i++] = c))
2212+ isbinary = 1;
2213+ }
2214+ REALLOC (sbuf, sbufsz, i + 2, ERR);
2215+ if (c == '\n')
2216+ sbuf[i++] = c;
2217+ else if (ferror (fp))
2218+ {
2219+ fprintf (stderr, "%s\n", strerror (errno));
2220+ sprintf (errmsg, "Cannot read input file");
2221+ return ERR;
2222+ }
2223+ else if (i)
2224+ {
2225+ sbuf[i++] = '\n';
2226+ newline_added = 1;
2227+ }
2228+ sbuf[i] = '\0';
2229+ return (isbinary && newline_added && i) ? --i : i;
2230+}
2231+
2232+
2233+/* write_file: write a range of lines to a named file/pipe; return line count */
2234+long
2235+write_file (fn, mode, n, m)
2236+ char *fn;
2237+ char *mode;
2238+ long n;
2239+ long m;
2240+{
2241+ FILE *fp;
2242+ long size;
2243+
2244+ fp = (*fn == '!') ? popen (fn + 1, "w") : fopen (strip_escapes (fn), mode);
2245+ if (fp == NULL)
2246+ {
2247+ fprintf (stderr, "%s: %s\n", fn, strerror (errno));
2248+ sprintf (errmsg, "Cannot open output file");
2249+ return ERR;
2250+ }
2251+ else if ((size = write_stream (fp, n, m)) < 0)
2252+ return ERR;
2253+ else if (((*fn == '!') ? pclose (fp) : fclose (fp)) < 0)
2254+ {
2255+ fprintf (stderr, "%s: %s\n", fn, strerror (errno));
2256+ sprintf (errmsg, "Cannot close output file");
2257+ return ERR;
2258+ }
2259+ fprintf (stderr, !scripted ? "%lu\n" : "", size);
2260+ return n ? m - n + 1 : 0;
2261+}
2262+
2263+
2264+/* write_stream: write a range of lines to a stream; return status */
2265+long
2266+write_stream (fp, n, m)
2267+ FILE *fp;
2268+ long n;
2269+ long m;
2270+{
2271+ line_t *lp = get_addressed_line_node (n);
2272+ unsigned long size = 0;
2273+ char *s;
2274+ int len;
2275+
2276+ for (; n && n <= m; n++, lp = lp->q_forw)
2277+ {
2278+ if ((s = get_sbuf_line (lp)) == NULL)
2279+ return ERR;
2280+ len = lp->len;
2281+ if (n != addr_last || !isbinary || !newline_added)
2282+ s[len++] = '\n';
2283+ if (put_stream_line (fp, s, len) < 0)
2284+ return ERR;
2285+ size += len;
2286+ }
2287+ return size;
2288+}
2289+
2290+
2291+/* put_stream_line: write a line of text to a stream; return status */
2292+int
2293+put_stream_line (fp, s, len)
2294+ FILE *fp;
2295+ char *s;
2296+ int len;
2297+{
2298+ while (len--)
2299+ if (fputc (*s++, fp) < 0)
2300+ {
2301+ fprintf (stderr, "%s\n", strerror (errno));
2302+ sprintf (errmsg, "Cannot write file");
2303+ return ERR;
2304+ }
2305+ return 0;
2306+}
2307+
2308+/* get_extended_line: get an extended line from stdin */
2309+char *
2310+get_extended_line (sizep, nonl)
2311+ int *sizep;
2312+ int nonl;
2313+{
2314+ static char *cvbuf = NULL; /* buffer */
2315+ static int cvbufsz = 0; /* buffer size */
2316+
2317+ int l, n;
2318+ char *t = ibufp;
2319+
2320+ while (*t++ != '\n')
2321+ ;
2322+ if ((l = t - ibufp) < 2 || !has_trailing_escape (ibufp, ibufp + l - 1))
2323+ {
2324+ *sizep = l;
2325+ return ibufp;
2326+ }
2327+ *sizep = -1;
2328+ REALLOC (cvbuf, cvbufsz, l, NULL);
2329+ memcpy (cvbuf, ibufp, l);
2330+ *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */
2331+ if (nonl)
2332+ l--; /* strip newline */
2333+ for (;;)
2334+ {
2335+ if ((n = get_tty_line ()) < 0)
2336+ return NULL;
2337+ else if (n == 0 || ibuf[n - 1] != '\n')
2338+ {
2339+ sprintf (errmsg, "Unexpected end-of-file");
2340+ return NULL;
2341+ }
2342+ REALLOC (cvbuf, cvbufsz, l + n, NULL);
2343+ memcpy (cvbuf + l, ibuf, n);
2344+ l += n;
2345+ if (n < 2 || !has_trailing_escape (cvbuf, cvbuf + l - 1))
2346+ break;
2347+ *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */
2348+ if (nonl)
2349+ l--; /* strip newline */
2350+ }
2351+ REALLOC (cvbuf, cvbufsz, l + 1, NULL);
2352+ cvbuf[l] = '\0';
2353+ *sizep = l;
2354+ return cvbuf;
2355+}
2356+
2357+
2358+/* get_tty_line: read a line of text from stdin; return line length */
2359+int
2360+get_tty_line ()
2361+{
2362+ register int oi = 0;
2363+ register int i = 0;
2364+ int c;
2365+
2366+ /* Read stdin one character at a time to avoid i/o contention
2367+ with shell escapes invoked by nonterminal input, e.g.,
2368+ ed - <<EOF
2369+ !cat
2370+ hello, world
2371+ EOF */
2372+ for (;;)
2373+ switch (c = getchar ())
2374+ {
2375+ default:
2376+ oi = 0;
2377+ REALLOC (ibuf, ibufsz, i + 2, ERR);
2378+ if (!(ibuf[i++] = c))
2379+ isbinary = 1;
2380+ if (c != '\n')
2381+ continue;
2382+ lineno++;
2383+ ibuf[i] = '\0';
2384+ ibufp = ibuf;
2385+ return i;
2386+ case EOF:
2387+ if (ferror (stdin))
2388+ {
2389+ fprintf (stderr, "stdin: %s\n", strerror (errno));
2390+ sprintf (errmsg, "Cannot read stdin");
2391+ clearerr (stdin);
2392+ ibufp = NULL;
2393+ return ERR;
2394+ }
2395+ else
2396+ {
2397+ clearerr (stdin);
2398+ if (i != oi)
2399+ {
2400+ oi = i;
2401+ continue;
2402+ }
2403+ else if (i)
2404+ ibuf[i] = '\0';
2405+ ibufp = ibuf;
2406+ return i;
2407+ }
2408+ }
2409+}
2410+
2411+
2412+
2413+#define ESCAPES "\a\b\f\n\r\t\v\\"
2414+#define ESCCHARS "abfnrtv\\"
2415+
2416+extern long rows;
2417+extern int cols;
2418+extern int dlcnt;
2419+
2420+/* put_tty_line: print text to stdout */
2421+int
2422+put_tty_line (s, l, n, gflag)
2423+ char *s;
2424+ int l;
2425+ long n;
2426+ int gflag;
2427+{
2428+ int col = 0;
2429+ char *cp;
2430+
2431+ if (gflag & GNP)
2432+ {
2433+ printf ("%ld\t", n);
2434+ col = 8;
2435+ }
2436+ for (; l--; s++)
2437+ {
2438+ if ((gflag & GLS) && ++col > cols)
2439+ {
2440+ fputs ("\\\n", stdout);
2441+ col = 1;
2442+ if (!traditional && !scripted && !isglobal && ++dlcnt > rows)
2443+ {
2444+ dlcnt = 0;
2445+ fputs ("Press <RETURN> to continue... ", stdout);
2446+ fflush (stdout);
2447+ if (get_tty_line () < 0)
2448+ return ERR;
2449+ }
2450+ }
2451+ if (gflag & GLS)
2452+ {
2453+ if (31 < *s && *s < 127 && *s != '\\')
2454+ putchar (*s);
2455+ else
2456+ {
2457+ putchar ('\\');
2458+ col++;
2459+ if (*s && (cp = strchr (ESCAPES, *s)) != NULL)
2460+ putchar (ESCCHARS[cp - ESCAPES]);
2461+ else
2462+ {
2463+ putchar ((((unsigned char) *s & 0300) >> 6) + '0');
2464+ putchar ((((unsigned char) *s & 070) >> 3) + '0');
2465+ putchar (((unsigned char) *s & 07) + '0');
2466+ col += 2;
2467+ }
2468+ }
2469+
2470+ }
2471+ else
2472+ putchar (*s);
2473+ }
2474+ if (!traditional && (gflag & GLS))
2475+ putchar ('$');
2476+ putchar ('\n');
2477+ return 0;
2478+}
2479+
2480+
2481+
2482+
2483+char *rhbuf; /* rhs substitution buffer */
2484+int rhbufsz; /* rhs substitution buffer size */
2485+int rhbufi; /* rhs substitution buffer index */
2486+
2487+/* extract_subst_tail: extract substitution tail from the command buffer */
2488+int
2489+extract_subst_tail (flagp, np)
2490+ int *flagp;
2491+ int *np;
2492+{
2493+ char delimiter;
2494+
2495+ *flagp = *np = 0;
2496+ if ((delimiter = *ibufp) == '\n')
2497+ {
2498+ rhbufi = 0;
2499+ *flagp = GPR;
2500+ return 0;
2501+ }
2502+ else if (extract_subst_template () == NULL)
2503+ return ERR;
2504+ else if (*ibufp == '\n')
2505+ {
2506+ *flagp = GPR;
2507+ return 0;
2508+ }
2509+ else if (*ibufp == delimiter)
2510+ ibufp++;
2511+ if ('1' <= *ibufp && *ibufp <= '9')
2512+ {
2513+ STRTOL (*np, ibufp);
2514+ return 0;
2515+ }
2516+ else if (*ibufp == 'g')
2517+ {
2518+ ibufp++;
2519+ *flagp = GSG;
2520+ return 0;
2521+ }
2522+ return 0;
2523+}
2524+
2525+
2526+/* extract_subst_template: return pointer to copy of substitution template
2527+ in the command buffer */
2528+char *
2529+extract_subst_template ()
2530+{
2531+ int n = 0;
2532+ int i = 0;
2533+ char c;
2534+ char delimiter = *ibufp++;
2535+
2536+ if (*ibufp == '%' && *(ibufp + 1) == delimiter)
2537+ {
2538+ ibufp++;
2539+ if (!rhbuf)
2540+ sprintf (errmsg, "No previous substitution");
2541+ return rhbuf;
2542+ }
2543+ while (*ibufp != delimiter)
2544+ {
2545+ REALLOC (rhbuf, rhbufsz, i + 2, NULL);
2546+ if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0')
2547+ {
2548+ i--, ibufp--;
2549+ break;
2550+ }
2551+ else if (c != '\\')
2552+ ;
2553+ else if ((rhbuf[i++] = *ibufp++) != '\n')
2554+ ;
2555+ else if (!isglobal)
2556+ {
2557+ while ((n = get_tty_line ()) == 0 ||
2558+ (n > 0 && ibuf[n - 1] != '\n'))
2559+ clearerr (stdin);
2560+ if (n < 0)
2561+ return NULL;
2562+ }
2563+ }
2564+ REALLOC (rhbuf, rhbufsz, i + 1, NULL);
2565+ rhbuf[rhbufi = i] = '\0';
2566+ return rhbuf;
2567+}
2568+
2569+
2570+char *rbuf; /* substitute_matching_text buffer */
2571+int rbufsz; /* substitute_matching_text buffer size */
2572+
2573+/* search_and_replace: for each line in a range, change text matching a pattern
2574+ according to a substitution template; return status */
2575+int
2576+search_and_replace (pat, gflag, kth)
2577+ pattern_t *pat;
2578+ int gflag;
2579+ int kth;
2580+{
2581+ undo_t *up;
2582+ char *txt;
2583+ char *eot;
2584+ long lc;
2585+ int nsubs = 0;
2586+ line_t *lp;
2587+ int len;
2588+
2589+ current_addr = first_addr - 1;
2590+ for (lc = 0; lc <= second_addr - first_addr; lc++)
2591+ {
2592+ lp = get_addressed_line_node (++current_addr);
2593+ if ((len = substitute_matching_text (pat, lp, gflag, kth)) < 0)
2594+ return ERR;
2595+ else if (len)
2596+ {
2597+ up = NULL;
2598+ if (delete_lines (current_addr, current_addr) < 0)
2599+ return ERR;
2600+ txt = rbuf;
2601+ eot = rbuf + len;
2602+ SPL1 ();
2603+ do
2604+ {
2605+ if ((txt = put_sbuf_line (txt)) == NULL)
2606+ {
2607+ SPL0 ();
2608+ return ERR;
2609+ }
2610+ else if (up)
2611+ up->t = get_addressed_line_node (current_addr);
2612+ else if ((up = push_undo_stack (UADD,
2613+ current_addr, current_addr)) == NULL)
2614+ {
2615+ SPL0 ();
2616+ return ERR;
2617+ }
2618+ }
2619+ while (txt != eot);
2620+ SPL0 ();
2621+ nsubs++;
2622+ }
2623+ }
2624+ if (nsubs == 0 && !(gflag & GLB))
2625+ {
2626+ sprintf (errmsg, "No match");
2627+ return ERR;
2628+ }
2629+ else if ((gflag & (GPR | GLS | GNP)) &&
2630+ display_lines (current_addr, current_addr, gflag) < 0)
2631+ return ERR;
2632+ return 0;
2633+}
2634+
2635+
2636+/* substitute_matching_text: replace text matched by a pattern according to
2637+ a substitution template; return pointer to the modified text */
2638+int
2639+substitute_matching_text (pat, lp, gflag, kth)
2640+ pattern_t *pat;
2641+ line_t *lp;
2642+ int gflag;
2643+ int kth;
2644+{
2645+ int off = 0;
2646+ int changed = 0;
2647+ int matchno = 0;
2648+ int i = 0;
2649+ regmatch_t rm[SE_MAX];
2650+ char *txt;
2651+ char *eot;
2652+
2653+ if ((txt = get_sbuf_line (lp)) == NULL)
2654+ return ERR;
2655+ if (isbinary)
2656+ NUL_TO_NEWLINE (txt, lp->len);
2657+ eot = txt + lp->len;
2658+ if (!regexec (pat, txt, SE_MAX, rm, 0))
2659+ {
2660+ do
2661+ {
2662+ if (!kth || kth == ++matchno)
2663+ {
2664+ changed++;
2665+ i = rm[0].rm_so;
2666+ REALLOC (rbuf, rbufsz, off + i, ERR);
2667+ if (isbinary)
2668+ NEWLINE_TO_NUL (txt, rm[0].rm_eo);
2669+ memcpy (rbuf + off, txt, i);
2670+ off += i;
2671+ if ((off = apply_subst_template (txt, rm, off,
2672+ pat->re_nsub)) < 0)
2673+ return ERR;
2674+ }
2675+ else
2676+ {
2677+ i = rm[0].rm_eo;
2678+ REALLOC (rbuf, rbufsz, off + i, ERR);
2679+ if (isbinary)
2680+ NEWLINE_TO_NUL (txt, i);
2681+ memcpy (rbuf + off, txt, i);
2682+ off += i;
2683+ }
2684+ txt += rm[0].rm_eo;
2685+ }
2686+ while (*txt && (!changed || ((gflag & GSG) && rm[0].rm_eo)) &&
2687+ !regexec (pat, txt, SE_MAX, rm, REG_NOTBOL));
2688+ i = eot - txt;
2689+ REALLOC (rbuf, rbufsz, off + i + 2, ERR);
2690+ if (i > 0 && !rm[0].rm_eo && (gflag & GSG))
2691+ {
2692+ sprintf (errmsg, "Infinite substitution loop");
2693+ return ERR;
2694+ }
2695+ if (isbinary)
2696+ NEWLINE_TO_NUL (txt, i);
2697+ memcpy (rbuf + off, txt, i);
2698+ memcpy (rbuf + off + i, "\n", 2);
2699+ }
2700+ return changed ? off + i + 1 : 0;
2701+}
2702+
2703+
2704+/* apply_subst_template: modify text according to a substitution template;
2705+ return offset to end of modified text */
2706+int
2707+apply_subst_template (boln, rm, off, re_nsub)
2708+ char *boln;
2709+ regmatch_t *rm;
2710+ int off;
2711+ int re_nsub;
2712+{
2713+ int j = 0;
2714+ int k = 0;
2715+ int n;
2716+ char *sub = rhbuf;
2717+
2718+ for (; sub - rhbuf < rhbufi; sub++)
2719+ if (*sub == '&')
2720+ {
2721+ j = rm[0].rm_so;
2722+ k = rm[0].rm_eo;
2723+ REALLOC (rbuf, rbufsz, off + k - j, ERR);
2724+ while (j < k)
2725+ rbuf[off++] = boln[j++];
2726+ }
2727+ else if (*sub == '\\' && '1' <= *++sub && *sub <= '9' &&
2728+ (n = *sub - '0') <= re_nsub)
2729+ {
2730+ j = rm[n].rm_so;
2731+ k = rm[n].rm_eo;
2732+ REALLOC (rbuf, rbufsz, off + k - j, ERR);
2733+ while (j < k)
2734+ rbuf[off++] = boln[j++];
2735+ }
2736+ else
2737+ {
2738+ REALLOC (rbuf, rbufsz, off + 1, ERR);
2739+ rbuf[off++] = *sub;
2740+ }
2741+ REALLOC (rbuf, rbufsz, off + 1, ERR);
2742+ rbuf[off] = '\0';
2743+ return off;
2744+}
2745+
2746+
2747+
2748+#define USIZE 100 /* undo stack size */
2749+undo_t *ustack = NULL; /* undo stack */
2750+long usize = 0; /* stack size variable */
2751+long u_p = 0; /* undo stack pointer */
2752+
2753+/* push_undo_stack: return pointer to intialized undo node */
2754+undo_t *
2755+push_undo_stack (type, from, to)
2756+ int type;
2757+ long from;
2758+ long to;
2759+{
2760+ undo_t *t;
2761+
2762+ if ((t = ustack) == NULL &&
2763+ (t = ustack = (undo_t *) malloc ((usize = USIZE) * sizeof (undo_t))) == NULL)
2764+ {
2765+ fprintf (stderr, "%s\n", strerror (errno));
2766+ sprintf (errmsg, "Out of memory");
2767+ return NULL;
2768+ }
2769+ else if (u_p >= usize &&
2770+ (t = (undo_t *) realloc (ustack, (usize += USIZE) * sizeof (undo_t))) == NULL)
2771+ {
2772+ /* out of memory - release undo stack */
2773+ fprintf (stderr, "%s\n", strerror (errno));
2774+ sprintf (errmsg, "Out of memory");
2775+ clear_undo_stack ();
2776+ free (ustack);
2777+ ustack = NULL;
2778+ usize = 0;
2779+ return NULL;
2780+ }
2781+ ustack = t;
2782+ ustack[u_p].type = type;
2783+ ustack[u_p].t = get_addressed_line_node (to);
2784+ ustack[u_p].h = get_addressed_line_node (from);
2785+ return ustack + u_p++;
2786+}
2787+
2788+
2789+/* USWAP: swap undo nodes */
2790+#define USWAP(x, y) \
2791+ do \
2792+ { \
2793+ undo_t utmp; \
2794+ utmp = (x), (x) = (y), (y) = utmp; \
2795+ } \
2796+ while (0)
2797+
2798+
2799+long u_current_addr = -1; /* if >= 0, undo enabled */
2800+long u_addr_last = -1; /* if >= 0, undo enabled */
2801+
2802+/* pop_undo_stack: undo last change to the editor buffer */
2803+int
2804+pop_undo_stack ()
2805+{
2806+ long n;
2807+ long o_current_addr = current_addr;
2808+ long o_addr_last = addr_last;
2809+
2810+ if (u_current_addr == -1 || u_addr_last == -1)
2811+ {
2812+ sprintf (errmsg, "Nothing to undo");
2813+ return ERR;
2814+ }
2815+ else if (u_p)
2816+ modified = 1;
2817+ get_addressed_line_node (0); /* this get_addressed_line_node last! */
2818+ SPL1 ();
2819+ for (n = u_p; n-- > 0;)
2820+ {
2821+ switch (ustack[n].type)
2822+ {
2823+ case UADD:
2824+ REQUE (ustack[n].h->q_back, ustack[n].t->q_forw);
2825+ break;
2826+ case UDEL:
2827+ REQUE (ustack[n].h->q_back, ustack[n].h);
2828+ REQUE (ustack[n].t, ustack[n].t->q_forw);
2829+ break;
2830+ case UMOV:
2831+ case VMOV:
2832+ REQUE (ustack[n - 1].h, ustack[n].h->q_forw);
2833+ REQUE (ustack[n].t->q_back, ustack[n - 1].t);
2834+ REQUE (ustack[n].h, ustack[n].t);
2835+ n--;
2836+ break;
2837+ default:
2838+ /*NOTREACHED */
2839+ ;
2840+ }
2841+ ustack[n].type ^= 1;
2842+ }
2843+ /* reverse undo stack order */
2844+ for (n = u_p; n-- > (u_p + 1) / 2;)
2845+ USWAP (ustack[n], ustack[u_p - 1 - n]);
2846+ if (isglobal)
2847+ clear_active_list ();
2848+ current_addr = u_current_addr, u_current_addr = o_current_addr;
2849+ addr_last = u_addr_last, u_addr_last = o_addr_last;
2850+ SPL0 ();
2851+ return 0;
2852+}
2853+
2854+
2855+/* clear_undo_stack: clear the undo stack */
2856+void
2857+clear_undo_stack ()
2858+{
2859+ line_t *lp, *ep, *tl;
2860+
2861+ while (u_p--)
2862+ if (ustack[u_p].type == UDEL)
2863+ {
2864+ ep = ustack[u_p].t->q_forw;
2865+ for (lp = ustack[u_p].h; lp != ep; lp = tl)
2866+ {
2867+ unmark_line_node (lp);
2868+ tl = lp->q_forw;
2869+ free (lp);
2870+ }
2871+ }
2872+ u_p = 0;
2873+ u_current_addr = current_addr;
2874+ u_addr_last = addr_last;
2875+}
2876+
2877+
2878+
2879+
2880+/* build_active_list: add line matching a pattern to the global-active list */
2881+int
2882+build_active_list (isgcmd)
2883+ int isgcmd;
2884+{
2885+ pattern_t *pat;
2886+ line_t *lp;
2887+ long n;
2888+ char *s;
2889+ char delimiter;
2890+
2891+ if ((delimiter = *ibufp) == ' ' || delimiter == '\n')
2892+ {
2893+ sprintf (errmsg, "Invalid pattern delimiter");
2894+ return ERR;
2895+ }
2896+ else if ((pat = get_compiled_pattern ()) == NULL)
2897+ return ERR;
2898+ else if (*ibufp == delimiter)
2899+ ibufp++;
2900+ clear_active_list ();
2901+ lp = get_addressed_line_node (first_addr);
2902+ for (n = first_addr; n <= second_addr; n++, lp = lp->q_forw)
2903+ {
2904+ if ((s = get_sbuf_line (lp)) == NULL)
2905+ return ERR;
2906+ if (isbinary)
2907+ NUL_TO_NEWLINE (s, lp->len);
2908+ if (!regexec (pat, s, 0, NULL, 0) == isgcmd &&
2909+ set_active_node (lp) < 0)
2910+ return ERR;
2911+ }
2912+ return 0;
2913+}
2914+
2915+
2916+/* exec_global: apply command list in the command buffer to the active
2917+ lines in a range; return command status */
2918+long
2919+exec_global (interact, gflag)
2920+ int interact;
2921+ int gflag;
2922+{
2923+ static char *ocmd = NULL;
2924+ static int ocmdsz = 0;
2925+
2926+ line_t *lp = NULL;
2927+ int status;
2928+ int n;
2929+ char *cmd = NULL;
2930+
2931+ if (!interact)
2932+ if (traditional && !strcmp (ibufp, "\n"))
2933+ cmd = "p\n"; /* null cmd-list == `p' */
2934+ else if ((cmd = get_extended_line (&n, 0)) == NULL)
2935+ return ERR;
2936+ clear_undo_stack ();
2937+ while ((lp = next_active_node ()) != NULL)
2938+ {
2939+ if ((current_addr = get_line_node_addr (lp)) < 0)
2940+ return ERR;
2941+ if (interact)
2942+ {
2943+ /* print current_addr; get a command in global syntax */
2944+ if (display_lines (current_addr, current_addr, gflag) < 0)
2945+ return ERR;
2946+ while ((n = get_tty_line ()) > 0 &&
2947+ ibuf[n - 1] != '\n')
2948+ clearerr (stdin);
2949+ if (n < 0)
2950+ return ERR;
2951+ else if (n == 0)
2952+ {
2953+ sprintf (errmsg, "Unexpected end-of-file");
2954+ return ERR;
2955+ }
2956+ else if (n == 1 && !strcmp (ibuf, "\n"))
2957+ continue;
2958+ else if (n == 2 && !strcmp (ibuf, "&\n"))
2959+ {
2960+ if (cmd == NULL)
2961+ {
2962+ sprintf (errmsg, "No previous command");
2963+ return ERR;
2964+ }
2965+ else
2966+ cmd = ocmd;
2967+ }
2968+ else if ((cmd = get_extended_line (&n, 0)) == NULL)
2969+ return ERR;
2970+ else
2971+ {
2972+ REALLOC (ocmd, ocmdsz, n + 1, ERR);
2973+ memcpy (ocmd, cmd, n + 1);
2974+ cmd = ocmd;
2975+ }
2976+
2977+ }
2978+ ibufp = cmd;
2979+ for (; *ibufp;)
2980+ if ((status = extract_addr_range ()) < 0 ||
2981+ (status = exec_command ()) < 0 ||
2982+ (status > 0 && (status = display_lines (
2983+ current_addr, current_addr, status)) < 0))
2984+ return status;
2985+ }
2986+ return 0;
2987+}
2988+
2989+
2990+line_t **active_list; /* list of lines active in a global command */
2991+long active_last; /* index of last active line in active_list */
2992+long active_size; /* size of active_list */
2993+long active_ptr; /* active_list index (non-decreasing) */
2994+long active_ndx; /* active_list index (modulo active_last) */
2995+
2996+/* set_active_node: add a line node to the global-active list */
2997+int
2998+set_active_node (lp)
2999+ line_t *lp;
3000+{
3001+ if (active_last + 1 > active_size)
3002+ {
3003+ int ti = active_size;
3004+ line_t **ts;
3005+ SPL1 ();
3006+ if (active_list != NULL)
3007+ {
3008+ if ((ts = (line_t **) realloc (active_list,
3009+ (ti += MINBUFSZ) * sizeof (line_t **))) == NULL)
3010+ {
3011+ fprintf (stderr, "%s\n", strerror (errno));
3012+ sprintf (errmsg, "Out of memory");
3013+ SPL0 ();
3014+ return ERR;
3015+ }
3016+ }
3017+ else
3018+ {
3019+ if ((ts = (line_t **) malloc ((ti += MINBUFSZ) *
3020+ sizeof (line_t **))) == NULL)
3021+ {
3022+ fprintf (stderr, "%s\n", strerror (errno));
3023+ sprintf (errmsg, "Out of memory");
3024+ SPL0 ();
3025+ return ERR;
3026+ }
3027+ }
3028+ active_size = ti;
3029+ active_list = ts;
3030+ SPL0 ();
3031+ }
3032+ active_list[active_last++] = lp;
3033+ return 0;
3034+}
3035+
3036+
3037+/* unset_active_nodes: remove a range of lines from the global-active list */
3038+void
3039+unset_active_nodes (np, mp)
3040+ line_t *np, *mp;
3041+{
3042+ line_t *lp;
3043+ long i;
3044+
3045+ for (lp = np; lp != mp; lp = lp->q_forw)
3046+ for (i = 0; i < active_last; i++)
3047+ if (active_list[active_ndx] == lp)
3048+ {
3049+ active_list[active_ndx] = NULL;
3050+ active_ndx = INC_MOD (active_ndx, active_last - 1);
3051+ break;
3052+ }
3053+ else
3054+ active_ndx = INC_MOD (active_ndx, active_last - 1);
3055+}
3056+
3057+
3058+/* next_active_node: return the next global-active line node */
3059+line_t *
3060+next_active_node ()
3061+{
3062+ while (active_ptr < active_last && active_list[active_ptr] == NULL)
3063+ active_ptr++;
3064+ return (active_ptr < active_last) ? active_list[active_ptr++] : NULL;
3065+}
3066+
3067+
3068+/* clear_active_list: clear the global-active list */
3069+void
3070+clear_active_list ()
3071+{
3072+ SPL1 ();
3073+ active_size = active_last = active_ptr = active_ndx = 0;
3074+ if (active_list != NULL)
3075+ free (active_list);
3076+ active_list = NULL;
3077+ SPL0 ();
3078+}
3079+
3080+
3081+
3082+/* get_compiled_pattern: return pointer to compiled pattern from command
3083+ buffer */
3084+pattern_t *
3085+get_compiled_pattern ()
3086+{
3087+ static pattern_t *exp = NULL;
3088+
3089+ char *exps;
3090+ char delimiter;
3091+ int n;
3092+
3093+ if ((delimiter = *ibufp) == ' ')
3094+ {
3095+ sprintf (errmsg, "Invalid pattern delimiter");
3096+ return NULL;
3097+ }
3098+ else if (delimiter == '\n' || *++ibufp == '\n' || *ibufp == delimiter)
3099+ {
3100+ if (!exp)
3101+ sprintf (errmsg, "No previous pattern");
3102+ return exp;
3103+ }
3104+ else if ((exps = extract_pattern (delimiter)) == NULL)
3105+ return NULL;
3106+ /* buffer alloc'd && not reserved */
3107+ if (exp && !patlock)
3108+ regfree (exp);
3109+ else if ((exp = (pattern_t *) malloc (sizeof (pattern_t))) == NULL)
3110+ {
3111+ fprintf (stderr, "%s\n", strerror (errno));
3112+ sprintf (errmsg, "Out of memory");
3113+ return NULL;
3114+ }
3115+ patlock = 0;
3116+ if ((n = regcomp (exp, exps, 0)))
3117+ {
3118+ regerror (n, exp, errmsg, ERRSZ);
3119+ free (exp);
3120+ return exp = NULL;
3121+ }
3122+ return exp;
3123+}
3124+
3125+
3126+/* extract_pattern: copy a pattern string from the command buffer; return
3127+ pointer to the copy */
3128+char *
3129+extract_pattern (delimiter)
3130+ int delimiter;
3131+{
3132+ static char *lhbuf = NULL; /* buffer */
3133+ static int lhbufsz = 0; /* buffer size */
3134+
3135+ char *nd;
3136+ int len;
3137+
3138+ for (nd = ibufp; *nd != delimiter && *nd != '\n'; nd++)
3139+ switch (*nd)
3140+ {
3141+ default:
3142+ break;
3143+ case '[':
3144+ if ((nd = parse_char_class (++nd)) == NULL)
3145+ {
3146+ sprintf (errmsg, "Unbalanced brackets ([])");
3147+ return NULL;
3148+ }
3149+ break;
3150+ case '\\':
3151+ if (*++nd == '\n')
3152+ {
3153+ sprintf (errmsg, "Trailing backslash (\\)");
3154+ return NULL;
3155+ }
3156+ break;
3157+ }
3158+ len = nd - ibufp;
3159+ REALLOC (lhbuf, lhbufsz, len + 1, NULL);
3160+ memcpy (lhbuf, ibufp, len);
3161+ lhbuf[len] = '\0';
3162+ ibufp = nd;
3163+ return (isbinary) ? NUL_TO_NEWLINE (lhbuf, len) : lhbuf;
3164+}
3165+
3166+
3167+/* parse_char_class: expand a POSIX character class */
3168+char *
3169+parse_char_class (s)
3170+ char *s;
3171+{
3172+ int c, d;
3173+
3174+ if (*s == '^')
3175+ s++;
3176+ if (*s == ']')
3177+ s++;
3178+ for (; *s != ']' && *s != '\n'; s++)
3179+ if (*s == '[' && ((d = *(s + 1)) == '.' || d == ':' || d == '='))
3180+ for (s++, c = *++s; *s != ']' || c != d; s++)
3181+ if ((c = *s) == '\n')
3182+ return NULL;
3183+ return (*s == ']') ? s : NULL;
3184+}
3185--- /dev/null 2005-04-22 11:15:01.120978184 -0400
3186+++ editors/ed.h 2005-04-22 11:15:09.000000000 -0400
3187@@ -0,0 +1,256 @@
3188+/* ed.h: type and constant definitions for the ed editor. */
3189+/* ed line editor.
3190+ Copyright (C) 1993, 1994 Andrew Moore, Talke Studio
3191+ All Rights Reserved
3192+
3193+ This program is free software; you can redistribute it and/or modify
3194+ it under the terms of the GNU General Public License as published by
3195+ the Free Software Foundation; either version 2, or (at your option)
3196+ any later version.
3197+
3198+ This program is distributed in the hope that it will be useful, but
3199+ WITHOUT ANY WARRANTY; without even the implied warranty of
3200+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3201+ General Public License for more details.
3202+
3203+ You should have received a copy of the GNU General Public License
3204+ along with this program; if not, write to the Free Software
3205+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
3206+
3207+ @(#)$Id: ed.h,v 1.14 1994/11/13 04:25:44 alm Exp $
3208+*/
3209+
3210+#include <stdio.h>
3211+
3212+#define ERR (-2)
3213+#define EMOD (-3)
3214+#define FATAL (-4)
3215+
3216+#define ERRSZ (PATH_MAX + 40) /* size of error message buffer */
3217+#define MINBUFSZ 512 /* minimum buffer size: must be > 0 */
3218+#define SE_MAX 30 /* max subexpressions in a regular expression */
3219+
3220+#define LINECHARS INT_MAX /* max chars per line */
3221+
3222+/* gflags */
3223+#define GLB 001 /* global command */
3224+#define GPR 002 /* print after command */
3225+#define GLS 004 /* list after command */
3226+#define GNP 010 /* enumerate after command */
3227+#define GSG 020 /* global substitute */
3228+
3229+typedef regex_t pattern_t;
3230+
3231+/* Line node */
3232+typedef struct line
3233+ {
3234+ struct line *q_forw;
3235+ struct line *q_back;
3236+ off_t seek; /* address of line in scratch buffer */
3237+ int len; /* length of line */
3238+ }
3239+line_t;
3240+
3241+
3242+typedef struct undo
3243+ {
3244+
3245+/* type of undo nodes */
3246+#define UADD 0
3247+#define UDEL 1
3248+#define UMOV 2
3249+#define VMOV 3
3250+
3251+ int type; /* command type */
3252+ line_t *h; /* head of list */
3253+ line_t *t; /* tail of list */
3254+ }
3255+undo_t;
3256+
3257+#define INC_MOD(l, k) ((l) + 1 > (k) ? 0 : (l) + 1)
3258+#define DEC_MOD(l, k) ((l) - 1 < 0 ? (k) : (l) - 1)
3259+
3260+/* SPL1: disable some interrupts (requires reliable signals) */
3261+#define SPL1() mutex++
3262+
3263+/* SPL0: enable all interrupts; check sigflags (requires reliable signals) */
3264+#define SPL0() \
3265+ do \
3266+ { \
3267+ if (--mutex == 0) \
3268+ { \
3269+ if (sigflags & (1 << (SIGHUP - 1))) handle_hup (SIGHUP); \
3270+ if (sigflags & (1 << (SIGINT - 1))) handle_int (SIGINT); \
3271+ } \
3272+ } \
3273+ while (0)
3274+
3275+/* STRTOL: convert a string to long */
3276+#define STRTOL(i, p) \
3277+ do \
3278+ { \
3279+ if ((((i) = strtol ((p), &(p), 10)) == LONG_MIN \
3280+ || (i) == LONG_MAX) && errno == ERANGE) \
3281+ { \
3282+ sprintf (errmsg, "Number out of range"); \
3283+ (i) = 0; \
3284+ return ERR; \
3285+ } \
3286+ } \
3287+ while (0)
3288+
3289+/* REALLOC: assure at least a minimum size for buffer b */
3290+#define REALLOC(b, n, i, err) \
3291+ do \
3292+ { \
3293+ if ((i) > (n)) \
3294+ { \
3295+ int ti = (n); \
3296+ char *ts; \
3297+ SPL1 (); \
3298+ if ((b) != NULL) \
3299+ { \
3300+ if ((ts = (char *) realloc ((b), ti += max ((i), MINBUFSZ))) \
3301+ == NULL) \
3302+ { \
3303+ fprintf (stderr, "%s\n", strerror (errno)); \
3304+ sprintf (errmsg, "Out of memory"); \
3305+ SPL0 (); \
3306+ return err; \
3307+ } \
3308+ } \
3309+ else \
3310+ { \
3311+ if ((ts = (char *) malloc (ti += max ((i), MINBUFSZ))) \
3312+ == NULL) \
3313+ { \
3314+ fprintf (stderr, "%s\n", strerror (errno)); \
3315+ sprintf (errmsg, "Out of memory"); \
3316+ SPL0 (); \
3317+ return err; \
3318+ } \
3319+ } \
3320+ (n) = ti; \
3321+ (b) = ts; \
3322+ SPL0 (); \
3323+ } \
3324+ } \
3325+ while (0)
3326+
3327+/* REQUE: link pred before succ */
3328+#define REQUE(pred, succ) \
3329+ ((pred)->q_forw = (succ), (succ)->q_back = (pred))
3330+
3331+/* INSQUE: insert elem in circular queue after pred */
3332+#define INSQUE(elem, pred) \
3333+ do \
3334+ { \
3335+ REQUE ((elem), (pred)->q_forw); \
3336+ REQUE ((pred), (elem)); \
3337+ } \
3338+ while (0)
3339+
3340+/* REMQUE: remove elem from circular queue */
3341+#define REMQUE(elem) \
3342+ REQUE ((elem)->q_back, (elem)->q_forw)
3343+
3344+/* NUL_TO_NEWLINE: overwrite ASCII NULs with newlines */
3345+#define NUL_TO_NEWLINE(s, l) translit_text(s, l, '\0', '\n')
3346+
3347+/* NEWLINE_TO_NUL: overwrite newlines with ASCII NULs */
3348+#define NEWLINE_TO_NUL(s, l) translit_text(s, l, '\n', '\0')
3349+
3350+/* Local Function Declarations */
3351+void add_line_node __P ((line_t *));
3352+int append_lines __P ((long));
3353+int apply_subst_template __P ((char *, regmatch_t *, int, int));
3354+int build_active_list __P ((int));
3355+int cbc_decode __P ((char *, FILE *));
3356+int cbc_encode __P ((char *, int, FILE *));
3357+int check_addr_range __P ((long, long));
3358+void clear_active_list __P ((void));
3359+void clear_undo_stack __P ((void));
3360+int close_sbuf __P ((void));
3361+int copy_lines __P ((long));
3362+int delete_lines __P ((long, long));
3363+void delete_yank_lines __P ((void));
3364+int display_lines __P ((long, long, int));
3365+line_t *dup_line_node __P ((line_t *));
3366+int exec_command __P ((void));
3367+long exec_global __P ((int, int));
3368+int extract_addr_range __P ((void));
3369+char *extract_pattern __P ((int));
3370+int extract_subst_tail __P ((int *, int *));
3371+char *extract_subst_template __P ((void));
3372+int filter_lines __P ((long, long, char *));
3373+line_t *get_addressed_line_node __P ((long));
3374+pattern_t *get_compiled_pattern __P ((void));
3375+char *get_extended_line __P ((int *, int));
3376+char *get_filename __P ((void));
3377+int get_keyword __P ((void));
3378+long get_line_node_addr __P ((line_t *));
3379+long get_matching_node_addr __P ((pattern_t *, int));
3380+long get_marked_node_addr __P ((int));
3381+char *get_sbuf_line __P ((line_t *));
3382+int get_shell_command __P ((void));
3383+int get_stream_line __P ((FILE *));
3384+int get_tty_line __P ((void));
3385+void handle_hup __P ((int));
3386+void handle_int __P ((int));
3387+void handle_winch __P ((int));
3388+int has_trailing_escape __P ((char *, char *));
3389+int hex_to_binary __P ((int, int));
3390+void init_buffers __P ((void));
3391+int is_legal_filename __P ((char *));
3392+int is_regular_file __P ((int));
3393+int join_lines __P ((long, long));
3394+int mark_line_node __P ((line_t *, int));
3395+int move_lines __P ((long));
3396+line_t *next_active_node ();
3397+long next_addr __P ((void));
3398+int open_sbuf __P ((void));
3399+char *parse_char_class __P ((char *));
3400+int pop_undo_stack __P ((void));
3401+undo_t *push_undo_stack __P ((int, long, long));
3402+int put_lines __P ((long));
3403+char *put_sbuf_line __P ((char *));
3404+int put_stream_line __P ((FILE *, char *, int));
3405+int put_tty_line __P ((char *, int, long, int));
3406+void quit __P ((int));
3407+long read_file __P ((char *, long));
3408+long read_stream __P ((FILE *, long));
3409+void (*reliable_signal __P ((int, void (*) ()))) ();
3410+int search_and_replace __P ((pattern_t *, int, int));
3411+int set_active_node __P ((line_t *));
3412+void signal_hup __P ((int));
3413+void signal_int __P ((int));
3414+char *strip_escapes __P ((char *));
3415+int substitute_matching_text __P ((pattern_t *, line_t *, int, int));
3416+char *translit_text __P ((char *, int, int, int));
3417+void unmark_line_node __P ((line_t *));
3418+void unset_active_nodes __P ((line_t *, line_t *));
3419+long write_file __P ((char *, char *, long, long));
3420+long write_stream __P ((FILE *, long, long));
3421+int yank_lines __P ((long, long));
3422+
3423+/* global buffers */
3424+extern char stdinbuf[];
3425+extern char *errmsg;
3426+extern char *ibuf;
3427+extern char *ibufp;
3428+extern int ibufsz;
3429+
3430+/* global flags */
3431+extern int isbinary;
3432+extern int isglobal;
3433+extern int modified;
3434+extern int mutex;
3435+extern int sigflags;
3436+extern int traditional;
3437+
3438+/* global vars */
3439+extern long addr_last;
3440+extern long current_addr;
3441+extern long first_addr;
3442+extern int lineno;
3443+extern long second_addr;