1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1995-2013 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *               Glenn Fowler <glenn.s.fowler@gmail.com>                *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  * Editor (snarfed from v10, now posix compliant, no hard limits)
23  */
24 
25 static const char usage[] =
26 "[-?\n@(#)$Id: ed (AT&T Research) 2004-06-08 $\n]"
27 USAGE_LICENSE
28 "[+NAME?ed - edit text]"
29 "[+DESCRIPTION?\bed\b is a line-oriented text editor that has two modes:"
30 "	command mode and input mode. In command mode characters on the"
31 "	standard input are interpreted as commands, and in input mode they"
32 "	are interpreted as text.]"
33 
34 "[h:explain?Explain the details of error conditions rather than the default"
35 "	``\b?\b'' on the standard error.]"
36 "[o:output?Write output to the standard output and error messages to the"
37 "	standard error. By default output is written to the file being edited"
38 "	and error messages are printed on the standard output.]"
39 "[p:prompt?Sets the command line prompt to \astring\a. The default is"
40 "	no prompt.]:[string]"
41 "[q:test?For testing; enable verbose messages and reset the \bQUIT\b signal"
42 "	handler to the default action.]"
43 "[s:silent?Disable verbose messages.]"
44 "[O:lenient?Enable lenient regular expression interpretation."
45 "	This is the default if \bgetconf CONFORMANCE\b is not \bstandard\b.]"
46 "[S:strict?Enable strict regular expression interpretation. This is the"
47 "	default if \bgetconf CONFORMANCE\b is \bstandard\b. You'd be"
48 "	suprised what the lenient mode lets by.]"
49 
50 "\n"
51 "\n[ file ]\n"
52 "\n"
53 
54 "[+SEE ALSO?\bsed\b(1), \bregex\b(3)]"
55 ;
56 
57 #include <ast.h>
58 #include <error.h>
59 #include <ls.h>
60 #include <sfdisc.h>
61 #include <sig.h>
62 
63 #include <ctype.h>
64 #include <regex.h>
65 #include <setjmp.h>
66 
67 #define BLOCK_LINE	1024
68 #define BLOCK_TMP	(8*SF_BUFSIZE)
69 
70 #define BREAK_PAGE	23
71 #define BREAK_LINE	72
72 
73 #define LINE_GLOBAL	((off_t)((off_t)1)<<(CHAR_BIT*sizeof(off_t)-1))
74 #define LINE_MARKED	((off_t)((off_t)1)<<(CHAR_BIT*sizeof(off_t)-2))
75 #define LINE_NONE	((off_t)-1)
76 
77 #define MARK_MIN	'a'
78 #define MARK_MAX	'z'
79 
80 #define MATCH_MIN	'0'
81 #define MATCH_MAX	'9'
82 
83 #define REC_IGNORE	001
84 #define REC_LINE	002
85 #define REC_SPLICE	004
86 #define REC_TERMINATE	010
87 #define REC_TEXT	020
88 
89 #define BEG(n)		(ed.line+ed.match[n].rm_so)
90 #define BAS()		(ed.base)
91 #define CUR()		(ed.line)
92 #define END(n)		(ed.line+ed.match[n].rm_eo)
93 #define HIT(n)		(ed.match[n].rm_eo!=-1)
94 #define NUL(n)		(ed.match[n].rm_so==ed.match[n].rm_eo)
95 #define NXT()		(ed.line+=ed.match[0].rm_eo)
96 #define SET(p,n)	(ed.line=(ed.base=(p))+(n))
97 #define SWP(a,b)	(ed.swp=(a),(a)=(b),(b)=ed.swp)
98 
99 #define error		fatal
100 #define errorf		fatalf
101 #define splice		splicef
102 #define trap()		do{if(ed.caught)handle();}while(0)
103 
104 typedef struct
105 {
106 	off_t		offset;
107 	off_t		undo;
108 	unsigned long	event;
109 } Line_t;
110 
111 static int		signals[] = { SIGQUIT, SIGHUP, SIGINT, SIGTERM };
112 
113 static struct		/* program state -- no other dynamic globals */
114 {
115 	struct
116 	{
117 	Sfio_t*		file;
118 	Sfio_t*		global;
119 	Sfio_t*		help;
120 	Sfio_t*		line;
121 	Sfio_t*		prompt;
122 	Sfio_t*		query;
123 	Sfio_t*		shell;
124 	Sfio_t*		work;
125 	}		buffer;
126 	struct
127 	{
128 	int		print;
129 	int		size;
130 	}		page;
131 	struct
132 	{
133 	off_t		marks[MARK_MAX - MARK_MIN + 1];
134 	unsigned long	dol;
135 	unsigned long	dot;
136 	}		undo;
137 	Line_t*		addr1;
138 	Line_t*		addr2;
139 	Line_t*		dol;
140 	Line_t*		dot;
141 	Line_t*		zero;
142 	Sfio_t*		iop;
143 	Sfio_t*		msg;
144 	Sfio_t*		tmp;
145 	Sfio_t*		spl;
146 	char*		base;
147 	char*		spbeg;
148 	char*		spend;
149 	char*		global;
150 	char*		input;
151 	char*		line;
152 	char*		linebreak;
153 	char*		tmpfile;
154 	int		caught;
155 	int		compiled;
156 	int		evented;
157 	int		given;
158 	int		help;
159 	int		initialized;
160 	int		interactive;
161 	int		lastc;
162 	int		marked;
163 	int		modified;
164 	int		peekc;
165 	int		pending;
166 	int		print;
167 	int		prompt;
168 	int		reflags;
169 	int		restricted;
170 	int		verbose;
171 	int		warn_newline;
172 	int		warn_null;
173 	jmp_buf		again;
174 	off_t		marks[MARK_MAX - MARK_MIN + 1];
175 	off_t		tmpoff;
176 	regex_t		re;
177 	regdisc_t	redisc;
178 	regmatch_t	match[MATCH_MAX - MATCH_MIN + 1];
179 	unsigned long	all;
180 	unsigned long	bytes;
181 	unsigned long	event;
182 	unsigned long	lines;
183 	Sfio_t*		swp;
184 }			ed;
185 
186 #define REG_SUB_LIST	REG_SUB_USER
187 
188 static const regflags_t	submap[] =
189 {
190 	'g',	REG_SUB_ALL,
191 	'l',	REG_SUB_LIST,
192 	'n',	REG_SUB_NUMBER,
193 	'p',	REG_SUB_PRINT,
194 	'L',	REG_SUB_LOWER,
195 	'U',	REG_SUB_UPPER,
196 	0,	0
197 };
198 
199 static void		commands(void);
200 static void		handle(void);
201 static void		quit(int);
202 
203 static void
eat(void)204 eat(void)
205 {
206 	if (ed.global)
207 	{
208 		if (!*ed.global++)
209 			ed.global = 0;
210 		ed.lastc = '\n';
211 	}
212 	else
213 		ed.input = 0;
214 }
215 
216 static void
reset(int level)217 reset(int level)
218 {
219 	if (level >= 2) {
220 		if (ed.iop) {
221 			sfclose(ed.iop);
222 			ed.iop = 0;
223 			error_info.file = 0;
224 		}
225 		if (ed.interactive <= 0 && (ed.interactive = isatty(0)) <= 0)
226 			quit(1);
227 		ed.print = 0;
228 		ed.bytes = 0;
229 		ed.lines = 0;
230 		eat();
231 		if (ed.initialized)
232 			longjmp(ed.again, 1);
233 	}
234 }
235 
236 static void
error(int level,...)237 error(int level, ...)
238 {
239 	va_list		ap;
240 
241 	trap();
242 	va_start(ap, level);
243 	errorv(NiL, level, ap);
244 	va_end(ap);
245 	reset(level);
246 }
247 
248 static int
errorf(const regex_t * re,regdisc_t * disc,int level,...)249 errorf(const regex_t* re, regdisc_t* disc, int level, ...)
250 {
251 	va_list		ap;
252 
253 	trap();
254 	va_start(ap, level);
255 	errorv(NiL, level, ap);
256 	va_end(ap);
257 	reset(level);
258 	return 0;
259 }
260 
261 static void
interrupt(int sig)262 interrupt(int sig)
263 {
264 	signal(sig, interrupt);
265 	if (ed.initialized) {
266 		if (!ed.caught)
267 			ed.caught = sig;
268 	}
269 	else if (!ed.pending)
270 		ed.pending = sig;
271 }
272 
273 static int
getchr(void)274 getchr(void)
275 {
276 	int	n;
277 
278 	if (ed.lastc = ed.peekc) {
279 		ed.peekc = 0;
280 		return ed.lastc;
281 	}
282 	if (ed.global) {
283 		if (ed.lastc = *ed.global++)
284 			return ed.lastc;
285 		ed.global = 0;
286 		return EOF;
287 	}
288 	if (!ed.input) {
289 		if (!(ed.input = sfgetr(sfstdin, '\n', 1))) {
290 			trap();
291 			return EOF;
292 		}
293 		if ((n = sfvalue(sfstdin) - 2) >= 0 && ed.input[n] == '\r')
294 			ed.input[n--] = 0;
295 		ed.spbeg = ed.input;
296 		ed.spend = (n >= 0 && ed.input[n] == '\\') ? (ed.input + n) : (char*)0;
297 	}
298 	if (!(ed.lastc = *ed.input++)) {
299 		ed.input = 0;
300 		ed.lastc = '\n';
301 	}
302 	return ed.lastc;
303 }
304 
305 static void
splice(void)306 splice(void)
307 {
308 	char*	s;
309 	int	n;
310 
311 	if (ed.spend) {
312 		if (!ed.spl && !(ed.spl = sfstropen()))
313 			error(ERROR_SYSTEM|3, "cannot initialize splice buffer");
314 		sfwrite(ed.spl, ed.spbeg, ed.spend - ed.spbeg);
315 		ed.spend = 0;
316 		sfputc(ed.spl, '\n');
317 		while (s = sfgetr(sfstdin, '\n', 1)) {
318 			if ((n = sfvalue(sfstdin) - 1) > 0 && s[n - 1] == '\r')
319 				n--;
320 			if (n > 0 && s[n - 1] == '\\') {
321 				sfwrite(ed.spl, s, n - 1);
322 				sfputc(ed.spl, '\n');
323 			}
324 			else {
325 				sfwrite(ed.spl, s, n);
326 				break;
327 			}
328 		}
329 		if (!(s = sfstruse(ed.spl)))
330 			error(ERROR_SYSTEM|3, "out of space");
331 		ed.input = s + (ed.input - ed.spbeg);
332 	}
333 }
334 
335 static char*
input(int n)336 input(int n)
337 {
338 	if (ed.peekc) {
339 		ed.peekc = 0;
340 		n--;
341 	}
342 	if (ed.global)
343 		return ed.global += n;
344 	else if (ed.input)
345 		return ed.input += n;
346 	else
347 		return 0;
348 }
349 
350 static ssize_t
helpwrite(int fd,const void * buf,size_t len)351 helpwrite(int fd, const void* buf, size_t len)
352 {
353 	ssize_t	n;
354 
355 	NoP(fd);
356 	n = ed.help ? sfwrite(sfstderr, buf, len) : ed.verbose ? sfputr(ed.msg, "?", '\n') : 0;
357 	sfstrseek(ed.buffer.help, 0, SEEK_SET);
358 	sfwrite(ed.buffer.help, buf, len - 1);
359 	sfputc(ed.buffer.help, 0);
360 	return n;
361 }
362 
363 static void
init(void)364 init(void)
365 {
366 	register Sfio_t**	ss;
367 	register int		c;
368 
369 	ed.interactive = -1;
370 	ed.msg = sfstdout;
371 	ed.all = BLOCK_LINE;
372 	ed.page.size = BREAK_PAGE;
373 	ed.redisc.re_version = REG_VERSION;
374 	ed.redisc.re_errorf = errorf;
375 	ed.re.re_disc = &ed.redisc;
376 	ed.reflags = REG_DISCIPLINE|REG_DELIMITED;
377 	if (!conformance(0, 0))
378 		ed.reflags |= REG_LENIENT;
379 	ed.verbose = 1;
380 	for (c = 0; c < elementsof(signals); c++)
381 		if (signal(signals[c], interrupt) == SIG_IGN)
382 			signal(signals[c], SIG_IGN);
383 	for (ss = (Sfio_t**)&ed.buffer; ss < (Sfio_t**)(((char*)&ed.buffer) + sizeof(ed.buffer)); ss++) {
384 		if (!(*ss = sfstropen()))
385 			error(ERROR_SYSTEM|3, "cannot initialize internal buffer");
386 		sfputc(*ss, 0);
387 		sfstrseek(*ss, 0, SEEK_SET);
388 	}
389 	sfputr(ed.buffer.help, "?", 0);
390 	if (!(ed.zero = newof(NiL, Line_t, ed.all, 0)))
391 		error(ERROR_SYSTEM|3, "out of space [zero]");
392 }
393 
394 static char*
getrec(register Sfio_t * sp,register int delimiter,register int flags)395 getrec(register Sfio_t* sp, register int delimiter, register int flags)
396 {
397 	register int	c;
398 	register char*	glob;
399 
400 	sfstrseek(sp, 0, SEEK_SET);
401 	glob = ed.global;
402 	while ((c = getchr()) != delimiter) {
403 		if (c == '\n') {
404 			ed.peekc = c;
405 			break;
406 		}
407 		if (c == EOF) {
408 			if (glob)
409 				ed.peekc = (flags & REC_LINE) ? 0 : c;
410 			else if (delimiter != '\n' || (flags & (REC_LINE|REC_SPLICE)))
411 				error(2, "unexpected EOF");
412 			else if (flags & REC_TEXT)
413 				return 0;
414 			break;
415 		}
416 		if (c == '\\' && ((c = getchr()) != delimiter || (flags & REC_SPLICE) && c != '\n') && c && !(flags & REC_IGNORE))
417 			sfputc(sp, '\\');
418 		if (!c)
419 			error(1, "null character ignored");
420 		else if (!(flags & REC_IGNORE))
421 			sfputc(sp, c);
422 	}
423 	if (flags & REC_TERMINATE)
424 		sfputc(sp, c);
425 	if (!(glob = sfstruse(sp)))
426 		error(ERROR_SYSTEM|3, "out of space");
427 	return glob;
428 }
429 
430 static void
putrec(register char * s)431 putrec(register char* s)
432 {
433 	register int	n;
434 	register char*	t;
435 
436 	if ((ed.print & REG_SUB_LIST) && (t = fmtesc(s))) {
437 		s = t;
438 		n = strlen(s);
439 		while (n > BREAK_LINE) {
440 			n -= BREAK_LINE;
441 			sfprintf(ed.msg, "%-*.*s\\\n", BREAK_LINE, BREAK_LINE, s);
442 			s += BREAK_LINE;
443 		}
444 		sfprintf(ed.msg, "%s$\n", s);
445 	}
446 	else
447 		sfputr(ed.msg, s, '\n');
448 }
449 
450 static void
modify(void)451 modify(void)
452 {
453 	if (!ed.evented) {
454 		ed.evented = ed.modified = 1;
455 		ed.event++;
456 		ed.undo.dot = ed.dot - ed.zero;
457 		ed.undo.dol = ed.dol - ed.zero;
458 		if (ed.marked) {
459 			register int	c;
460 
461 			for (c = 0; c < elementsof(ed.marks); c++)
462 				ed.undo.marks[c] = ed.marks[c];
463 		}
464 	}
465 }
466 
467 static void
undo(void)468 undo(void)
469 {
470 	register Line_t*	a1;
471 	register Line_t*	a3;
472 	register unsigned long	event;
473 	int			c;
474 	off_t			t;
475 	unsigned long		n;
476 
477 	c = 0;
478 	event = ed.event;
479 	a1 = ed.zero;
480 	a3 = ed.zero + ed.all;
481 	while (++a1 < a3)
482 		if (a1->event == event) {
483 			c = 1;
484 			t = a1->offset;
485 			a1->offset = a1->undo;
486 			a1->undo = t;
487 		}
488 	if (!c)
489 		error(2, "nothing to undo");
490 	if (ed.marked)
491 		for (c = 0; c < elementsof(ed.marks); c++) {
492 			t = ed.marks[c];
493 			ed.marks[c] = ed.undo.marks[c];
494 			ed.undo.marks[c] = t;
495 		}
496 	n = ed.dot - ed.zero;
497 	ed.dot = ed.zero + ed.undo.dot;
498 	ed.undo.dot = n;
499 	n = ed.dol - ed.zero;
500 	ed.dol = ed.zero + ed.undo.dol;
501 	ed.undo.dol = n;
502 }
503 
504 static char*
lineget(off_t off)505 lineget(off_t off)
506 {
507 	char*	s;
508 
509 	off &= ~(LINE_GLOBAL|LINE_MARKED);
510 	if (sfseek(ed.tmp, off, SEEK_SET) != off)
511 		error(ERROR_SYSTEM|2, "temp file read seek error");
512 	if (!(s = sfgetr(ed.tmp, 0, 0)))
513 		error(ERROR_SYSTEM|2, "temp file read error at offset %I*d", sizeof(off), off);
514 	return s;
515 }
516 
517 static off_t
lineput(char * s)518 lineput(char* s)
519 {
520 	off_t	off;
521 
522 	modify();
523 	off = ed.tmpoff;
524 	if (sfseek(ed.tmp, off, SEEK_SET) != off)
525 		error(ERROR_SYSTEM|2, "temp file write seek error");
526 	if (sfputr(ed.tmp, s, 0) < 0)
527 		error(ERROR_SYSTEM|2, "temp file write error at offset %I*d", sizeof(off), off);
528 	if ((ed.tmpoff = sfseek(ed.tmp, (off_t)0, SEEK_CUR)) == (off_t)-1)
529 		error(ERROR_SYSTEM|2, "temp file tell error");
530 	return off;
531 }
532 
533 static void
replace(register Line_t * a1,char * s)534 replace(register Line_t* a1, char* s)
535 {
536 	register off_t	off;
537 
538 	off = lineput(s);
539 	if (a1->offset & LINE_MARKED) {
540 		register off_t*	mp;
541 
542 		a1->offset &= ~LINE_GLOBAL;
543 		off |= LINE_MARKED;
544 		for (mp = ed.marks; mp < &ed.marks[elementsof(ed.marks)]; mp++)
545 			if (*mp == a1->offset)
546 				*mp = off;
547 	}
548 	a1->event = ed.event;
549 	a1->undo = a1->offset;
550 	a1->offset = off;
551 }
552 
553 static void
squeeze(int i)554 squeeze(int i)
555 {
556 	if (ed.addr1 < ed.zero + i)
557 		error(2, "at top of file");
558 	if (ed.addr2 > ed.dol)
559 		error(2, "at end of file");
560 	if (ed.addr1 > ed.addr2)
561 		error(2, "first address exceeds second");
562 }
563 
564 static void
nonzero(void)565 nonzero(void)
566 {
567 	squeeze(1);
568 }
569 
570 static char*
getfile(void)571 getfile(void)
572 {
573 	register char*	s;
574 	register int	n;
575 	register int	m;
576 
577 	if (!(s = sfgetr(ed.iop, '\n', 1))) {
578 		if (!(s = sfgetr(ed.iop, '\n', -1)))
579 			return 0;
580 		ed.warn_newline = 1;
581 	}
582 	if ((n = sfvalue(ed.iop)) > 0 && s[n - 1] == '\r')
583 		s[--n] = 0;
584 	if ((m = strlen(s)) < n) {
585 		register char*	t;
586 		register char*	u;
587 		register char*	x;
588 
589 		t = u = s + m;
590 		x = s + n;
591 		while (u < x)
592 			if (!(*t++ = *u++))
593 				t--;
594 		*t++ = 0;
595 		n = t - s;
596 		ed.warn_null += x - t;
597 	}
598 	ed.bytes += n;
599 	ed.lines++;
600 	return s;
601 }
602 
603 static char*
getline(void)604 getline(void)
605 {
606 	register char*	s;
607 
608 	if ((s = getrec(ed.buffer.line, '\n', REC_TEXT)) && s[0] == '.' && !s[1])
609 		s = 0;
610 	return s;
611 }
612 
613 static char*
getbreak(void)614 getbreak(void)
615 {
616 	char*	s;
617 
618 	if ((s = ed.linebreak) && (ed.linebreak = strchr(s, '\n')))
619 		*ed.linebreak++ = 0;
620 	return s;
621 }
622 
623 static char*
getcopy(void)624 getcopy(void)
625 {
626 	if (ed.addr1 > ed.addr2)
627 		return 0;
628 	return lineget((ed.addr1++)->offset);
629 }
630 
631 static void
print(void)632 print(void)
633 {
634 	register Line_t* a1;
635 
636 	nonzero();
637 	a1 = ed.addr1;
638 	do {
639 		if (ed.print & REG_SUB_NUMBER)
640 			sfprintf(ed.msg, "%d\t", a1 - ed.zero);
641 		putrec(lineget((a1++)->offset));
642 	} while (a1 <= ed.addr2);
643 	ed.dot = ed.addr2;
644 	ed.print = 0;
645 }
646 
647 static int
getnum(void)648 getnum(void)
649 {
650 	register int c;
651 	register int r;
652 
653 	r = 0;
654 	while ((c = getchr()) >= '0' && c <= '9')
655 		r = r * 10 + c - '0';
656 	ed.peekc = c;
657 	return r;
658 }
659 
660 static void
compile(void)661 compile(void)
662 {
663 	register char*	s;
664 	int		c;
665 
666 	s = input(0);
667 	if (*s) {
668 		if (*(s + 1)) {
669 			if (*s == *(s + 1))
670 				input(2);
671 			else {
672 				if (ed.compiled) {
673 					ed.compiled = 0;
674 					regfree(&ed.re);
675 				}
676 				if (c = regcomp(&ed.re, s, ed.reflags)) {
677 					regfatal(&ed.re, 2, c);
678 					eat();
679 				}
680 				else
681 					input(ed.re.re_npat);
682 				ed.compiled = 1;
683 				return;
684 			}
685 		}
686 		else
687 			input(1);
688 	}
689 	if (!ed.compiled)
690 		error(2, "no previous regular expression");
691 }
692 
693 static int
execute(Line_t * addr,regflags_t flags)694 execute(Line_t* addr, regflags_t flags)
695 {
696 	register char*	s;
697 	register int	c;
698 
699 	trap();
700 	if (!addr)
701 		s = CUR();
702 	else if (addr == ed.zero)
703 		return 0;
704 	else {
705 		s = lineget(addr->offset);
706 		SET(s, 0);
707 	}
708 	if (c = regexec(&ed.re, s, elementsof(ed.match), ed.match, ed.reflags|flags)) {
709 		if (c != REG_NOMATCH)
710 			regfatal(&ed.re, 2, c);
711 		return 0;
712 	}
713 	return 1;
714 }
715 
716 static Line_t*
address(void)717 address(void)
718 {
719 	register int		c;
720 	register int		sign;
721 	register Line_t*	a;
722 	register Line_t*	b;
723 	int			opcnt;
724 	int			nextopand;
725 
726 	nextopand = -1;
727 	sign = 1;
728 	opcnt = 0;
729 	a = ed.dot;
730 	do {
731 		do c = getchr(); while (isspace(c) && c != '\n');
732 		if (c >= '0' && c <= '9') {
733 			ed.peekc = c;
734 			if (!opcnt)
735 				a = ed.zero;
736 			a += sign * getnum();
737 		}
738 		else switch (c) {
739 
740 		case '$':
741 			a = ed.dol;
742 			/*FALLTHROUGH*/
743 		case '.':
744 			if (opcnt)
745 				error(2, "invalid address");
746 			break;
747 
748 		case '\'':
749 			if ((c = getchr()) == EOF || (c -= MARK_MIN) < 0 || c >= elementsof(ed.marks) || opcnt)
750 				error(2, "invalid mark");
751 			a = ed.marked && ed.marks[c] != LINE_NONE ? ed.zero : ed.dol;
752 			do {
753 				if (++a > ed.dol)
754 					error(2, "undefined mark referenced");
755 			} while (ed.marks[c] != (a->offset & ~LINE_GLOBAL));
756 			break;
757 
758 		case '?':
759 			sign = -sign;
760 			/*FALLTHROUGH*/
761 		case '/':
762 			input(-1);
763 			compile();
764 			b = a;
765 			for (;;) {
766 				a += sign;
767 				if (a <= ed.zero)
768 					a = ed.dol;
769 				if (a > ed.dol)
770 					a = ed.zero;
771 				if (execute(a, 0))
772 					break;
773 				if (a == b)
774 					error(2, "pattern not found");
775 			}
776 			break;
777 
778 		default:
779 			if (nextopand == opcnt) {
780 				a += sign;
781 				if (a < ed.zero || ed.dol < a)
782 					continue;       /* error? */
783 			}
784 			if (c != '+' && c != '-' && c != '^') {
785 				ed.peekc = c;
786 				if (!opcnt)
787 					a = 0;
788 				return a;
789 			}
790 			sign = 1;
791 			if (c != '+')
792 				sign = -sign;
793 			nextopand = ++opcnt;
794 			continue;
795 
796 		}
797 		sign = 1;
798 		opcnt++;
799 	} while (a >= ed.zero && a <= ed.dol);
800 	error(2, "address out of range");
801 	return 0;
802 }
803 
804 static void
setwide(void)805 setwide(void)
806 {
807 	if (!ed.given) {
808 		ed.addr1 = ed.zero + (ed.dol > ed.zero);
809 		ed.addr2 = ed.dol;
810 	}
811 }
812 
813 static void
setnoaddr(void)814 setnoaddr(void)
815 {
816 	if (ed.given)
817 		error(2, "invalid address count");
818 }
819 
820 static void
newline(void)821 newline(void)
822 {
823 	register int	warned = 0;
824 
825 	for (;;)
826 		switch (getchr()) {
827 
828 		case EOF:
829 		case '\n':
830 			return;
831 
832 		case 'l':
833 			ed.print = REG_SUB_LIST;
834 			continue;
835 
836 		case 'n':
837 			ed.print = REG_SUB_NUMBER;
838 			continue;
839 
840 		case 'p':
841 			ed.print = REG_SUB_PRINT;
842 			continue;
843 
844 		default:
845 			if (!warned) {
846 				warned = 1;
847 				error(2, "extra characters at end of command");
848 			}
849 			continue;
850 		}
851 }
852 
853 static char*
plural(unsigned long count)854 plural(unsigned long count)
855 {
856 	return count == 1 ? "" : "s";
857 }
858 
859 static void
exfile(void)860 exfile(void)
861 {
862 	if (sfclose(ed.iop))
863 		error(ERROR_SYSTEM|1, "io error");
864 	ed.iop = 0;
865 	if (ed.verbose) {
866 		if (ed.help) {
867 			sfprintf(ed.msg, "\"%s\" %lu line%s, %lu character%s", error_info.file, ed.lines, plural(ed.lines), ed.bytes, plural(ed.bytes));
868 			if (ed.warn_null) {
869 				sfprintf(ed.msg, ", %lu null%s", ed.warn_null, plural(ed.warn_null));
870 				ed.warn_null = 0;
871 			}
872 			if (ed.warn_newline) {
873 				sfprintf(ed.msg, ", newline appended");
874 				ed.warn_newline = 0;
875 			}
876 			sfputc(ed.msg, '\n');
877 		}
878 		else
879 			sfprintf(ed.msg, "%d\n", ed.bytes);
880 	}
881 	if (ed.warn_null || ed.warn_newline) {
882 		char*	sep = "";
883 
884 		sfstrseek(ed.buffer.line, 0, SEEK_SET);
885 		if (ed.warn_null) {
886 			sfprintf(ed.buffer.line, "%d null character%s ignored", ed.warn_null, plural(ed.warn_null));
887 			ed.warn_null = 0;
888 			sep = ", ";
889 		}
890 		if (ed.warn_newline) {
891 			sfprintf(ed.buffer.line, "%snewline appended to last line", sep);
892 			ed.warn_newline = 0;
893 		}
894 		if (!(sep = sfstruse(ed.buffer.line)))
895 			error(ERROR_SYSTEM|3, "out of space");
896 		error(1, "%s", sep);
897 	}
898 	error_info.file = 0;
899 }
900 
901 static void
putfile(void)902 putfile(void)
903 {
904 	register Line_t*	a1;
905 	register int		n;
906 
907 	ed.bytes = 0;
908 	ed.lines = 0;
909 	a1 = ed.addr1;
910 	do {
911 		if ((n = sfputr(ed.iop, lineget((a1++)->offset), '\n')) < 0)
912 			error(ERROR_SYSTEM|2, "write error");
913 		ed.bytes += n;
914 		ed.lines++;
915 	} while (a1 <= ed.addr2);
916 	if (sfsync(ed.iop))
917 		error(ERROR_SYSTEM|2, "write error");
918 }
919 
920 static void
quit(int code)921 quit(int code)
922 {
923 	if (ed.tmpfile) {
924 		remove(ed.tmpfile);
925 		ed.tmpfile = 0;
926 	}
927 	if (ed.verbose && ed.modified && ed.dol != ed.zero) {
928 		ed.modified = 0;
929 		error(2, "file changed but not written");
930 	}
931 	if (ed.caught == SIGQUIT) {
932 		signal(ed.caught, SIG_DFL);
933 		kill(0, ed.caught);
934 	}
935 	exit(code);
936 }
937 
938 static void
handle(void)939 handle(void)
940 {
941 	register int	c;
942 	char*		s;
943 	char*		b;
944 	mode_t		mask;
945 
946 	if (ed.caught == SIGINT) {
947 		ed.caught = 0;
948 		ed.lastc = '\n';
949 		sfputc(ed.msg, '\n');
950 		error(2, "interrupt");
951 	}
952 	for (c = 0; c < elementsof(signals); c++)
953 		signal(signals[c], SIG_IGN);
954 	if (ed.dol > ed.zero) {
955 		ed.addr1 = ed.zero + 1;
956 		ed.addr2 = ed.dol;
957 		mask = umask(S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
958 		b = "ed.hup";
959 		if (!(ed.iop = sfopen(NiL, b, "w")) && !ed.restricted && (s = getenv("HOME"))) {
960 			sfstrseek(ed.buffer.line, 0, SEEK_SET);
961 			sfprintf(ed.buffer.line, "%s/%s", s, b);
962 			if (!(b = sfstruse(ed.buffer.line)))
963 				error(ERROR_SYSTEM|3, "out of space");
964 			ed.iop = sfopen(NiL, b, "w");
965 		}
966 		umask(mask);
967 		if (!ed.iop)
968 			error(ERROR_SYSTEM|1, "%s: cannot save changes", b);
969 		else {
970 			error_info.file = b;
971 			putfile();
972 		}
973 	}
974 	ed.modified = 0;
975 	quit(0);
976 }
977 
978 static Line_t*
append(char * (* f)(void),Line_t * a,Line_t ** r)979 append(char* (*f)(void), Line_t* a, Line_t** r)
980 {
981 	register char*		s;
982 	register Line_t*	a1;
983 	register Line_t*	a2;
984 	register Line_t*	a3;
985 	off_t			t;
986 	long			added;
987 
988 	added = 0;
989 	ed.dot = a;
990 	while (s = (*f)()) {
991 		trap();
992 		if ((ed.dol - ed.zero) + 1 >= ed.all) {
993 			unsigned long	dot_off = ed.dot - ed.zero;
994 			unsigned long	dol_off = ed.dol - ed.zero;
995 			unsigned long	r_off = r ? *r - ed.zero : 0;
996 
997 			ed.all += BLOCK_LINE;
998 			if (!(ed.zero = newof(ed.zero, Line_t, ed.all, 0))) {
999 				error(ERROR_SYSTEM|1, "no space [zero]");
1000 				ed.caught = SIGHUP;
1001 				trap();
1002 			}
1003 			ed.dot = ed.zero + dot_off;
1004 			ed.dol = ed.zero + dol_off;
1005 			if (r)
1006 				*r = ed.zero + r_off;
1007 		}
1008 		t = lineput(s);
1009 		added++;
1010 		a1 = ++ed.dol;
1011 		a2 = a1 + 1;
1012 		a3 = ++ed.dot;
1013 		while (a1 > a3) {
1014 			(--a2)->event = ed.event;
1015 			a2->undo = a2->offset;
1016 			a2->offset = (--a1)->offset;
1017 		}
1018 		a3->event = ed.event;
1019 		a3->undo = a3->offset;
1020 		a3->offset = t;
1021 	}
1022 	if (r)
1023 		*r += added;
1024 	return ed.dot;
1025 }
1026 
1027 static void
add(int i)1028 add(int i)
1029 {
1030 	if (i && (ed.given || ed.dol > ed.zero)) {
1031 		ed.addr1--;
1032 		ed.addr2--;
1033 	}
1034 	squeeze(0);
1035 	newline();
1036 	append(getline, ed.addr2, NiL);
1037 }
1038 
1039 static void
page(void)1040 page(void)
1041 {
1042 	register int	direction;
1043 	register int	n;
1044 
1045 	switch (direction = getchr()) {
1046 
1047 	case '-':
1048 	case '.':
1049 	case '+':
1050 		break;
1051 
1052 	default:
1053 		ed.peekc = direction;
1054 		direction = '+';
1055 		break;
1056 
1057 	}
1058 	if ((n = getnum()) > 0)
1059 		ed.page.size = n;
1060 	newline();
1061 	if (ed.print)
1062 		ed.page.print = ed.print;
1063 	else
1064 		ed.print = ed.page.print;
1065 	switch (direction) {
1066 
1067 	case '-':
1068 		ed.addr1 = ed.addr2 - ed.page.size + 1;
1069 		break;
1070 
1071 	case '.':
1072 		ed.addr2 += ed.page.size / 2;
1073 		ed.addr1 = ed.addr2 - ed.page.size + 1;
1074 		break;
1075 
1076 	case '+':
1077 		ed.addr1 = ed.addr2;
1078 		ed.addr2 += ed.page.size - 1;
1079 		break;
1080 
1081 	}
1082 	if (ed.addr1 <= ed.zero)
1083 		ed.addr1 = ed.zero + 1;
1084 	if (ed.addr2 > ed.dol)
1085 		ed.addr2 = ed.dol;
1086 	print();
1087 }
1088 
1089 static void
rdelete(register Line_t * a1,register Line_t * a2)1090 rdelete(register Line_t* a1, register Line_t* a2)
1091 {
1092 	register Line_t*	a3;
1093 
1094 	modify();
1095 	a3 = ed.dol;
1096 	ed.dol -= ++a2 - a1;
1097 	ed.dot = a1 > ed.dol ? ed.dol : a1;
1098 	do {
1099 		a1->undo = a1->offset;
1100 		a1->event = ed.event;
1101 		(a1++)->offset = (a2++)->offset;
1102 	} while (a2 <= a3);
1103 	while (a1 <= a3) {
1104 		a1->undo = a1->offset;
1105 		(a1++)->event = ed.event;
1106 	}
1107 }
1108 
1109 static void
gdelete(void)1110 gdelete(void)
1111 {
1112 	register Line_t*	a1;
1113 	register Line_t*	a2;
1114 	register Line_t*	a3;
1115 
1116 	a3 = ed.dol;
1117 	for (a1 = ed.zero; !(a1->offset & LINE_GLOBAL); a1++)
1118 		if (a1 >= a3)
1119 			return;
1120 	modify();
1121 	for (a2 = a1 + 1; a2 <= a3;) {
1122 		a1->event = ed.event;
1123 		a1->undo = a1->offset;
1124 		if (a2->offset & LINE_GLOBAL) {
1125 			a2++;
1126 			ed.dot = a1;
1127 		}
1128 		else
1129 			(a1++)->offset = (a2++)->offset;
1130 	}
1131 	ed.dol = a1 - 1;
1132 	if (ed.dot > ed.dol)
1133 		ed.dot = ed.dol;
1134 	while (a1 <= a3) {
1135 		a1->undo = a1->offset;
1136 		(a1++)->event = ed.event;
1137 	}
1138 }
1139 
1140 static void
shell(void)1141 shell(void)
1142 {
1143 	register char*	s;
1144 	register char*	f = 0;
1145 	register int	c;
1146 
1147 	if (ed.given)
1148 		squeeze(ed.dol > ed.zero);
1149 	s = getrec(ed.buffer.line, '\n', 0);
1150 	if (s[0] == '!' && !s[1]) {
1151 		if (!*sfstrbase(ed.buffer.shell))
1152 			error(2, "no saved shell command");
1153 		f = sfstrbase(ed.buffer.file);
1154 	}
1155 	else if (!s[0])
1156 		error(2, "empty shell command");
1157 	else
1158 		SWP(ed.buffer.shell, ed.buffer.line);
1159 	s = sfstrbase(ed.buffer.shell);
1160 	sfstrseek(ed.buffer.line, 0, SEEK_SET);
1161 	sfputc(ed.buffer.line, '!');
1162 	while (c = *s++) {
1163 		if (c == '\\') {
1164 			if (*s != '%')
1165 				sfputc(ed.buffer.line, c);
1166 			sfputc(ed.buffer.line, *s++);
1167 		}
1168 		else if (c == '%')
1169 			sfputr(ed.buffer.line, f = sfstrbase(ed.buffer.file), -1);
1170 		else
1171 			sfputc(ed.buffer.line, c);
1172 	}
1173 	if (ed.given) {
1174 		if (!ed.tmpfile && !(ed.tmpfile = pathtemp(NiL, 0, NiL, error_info.id, NiL)))
1175 			error(ERROR_SYSTEM|2, "cannot generate temp file name");
1176 		if (!(ed.iop = sfopen(NiL, ed.tmpfile, "w")))
1177 			error(ERROR_SYSTEM|2, "%s: cannot create temp file", ed.tmpfile);
1178 		error_info.file = ed.tmpfile;
1179 		if (ed.dol > ed.zero)
1180 			putfile();
1181 		exfile();
1182 		ed.bytes = 0;
1183 		ed.lines = 0;
1184 		sfprintf(ed.buffer.line, " < %s", ed.tmpfile);
1185 		if (!(s = sfstruse(ed.buffer.line)))
1186 			error(ERROR_SYSTEM|3, "out of space");
1187 		if (!(ed.iop = sfpopen(NiL, s + 1, "r")))
1188 			error(ERROR_SYSTEM|2, "%s: cannot execute shell command", s);
1189 		error_info.file = s;
1190 		rdelete(ed.addr1, ed.addr2);
1191 		append(getfile, ed.dot, NiL);
1192 		exfile();
1193 		remove(ed.tmpfile);
1194 	}
1195 	else {
1196 		if (!(s = sfstruse(ed.buffer.line)))
1197 			error(ERROR_SYSTEM|3, "out of space");
1198 		s++;
1199 		if (f)
1200 			putrec(s);
1201 		if (!(ed.iop = sfpopen(NiL, s, "")))
1202 			error(ERROR_SYSTEM|2, "%s: cannot execute shell command", s);
1203 		if (sfclose(ed.iop)) {
1204 			ed.iop = 0;
1205 			error(ERROR_SYSTEM|2, "%s: shell command exit error", s);
1206 		}
1207 		if (ed.verbose)
1208 			putrec("!");
1209 	}
1210 }
1211 
1212 static void
edit(void)1213 edit(void)
1214 {
1215 	register off_t*	mp;
1216 
1217 	if (ed.tmp) {
1218 		sfclose(ed.tmp);
1219 		ed.tmp = 0;
1220 	}
1221 	ed.tmpoff = 0;
1222 	if (!(ed.tmp = sftmp(BLOCK_TMP)))
1223 		error(ERROR_SYSTEM|3, "cannot create temp file");
1224 	for (mp = ed.marks; mp < &ed.marks[elementsof(ed.marks)]; )
1225 		*mp++ = LINE_NONE;
1226 	ed.marked = 0;
1227 	ed.event++;
1228 	ed.dot = ed.dol = ed.zero;
1229 	if (!ed.initialized) {
1230 		ed.initialized = 1;
1231 		if (ed.pending)
1232 			ed.caught = ed.pending;
1233 	}
1234 }
1235 
1236 static void
filename(int c)1237 filename(int c)
1238 {
1239 	register char*	p;
1240 	register int	sh = 0;
1241 
1242 	ed.bytes = 0;
1243 	ed.lines = 0;
1244 	p = getrec(ed.buffer.line, '\n', REC_LINE);
1245 	if (*p) {
1246 		if (!isspace(*p))
1247 			error(2, "no space after command");
1248 		for (p++; isspace(*p); p++)
1249 			;
1250 		if (!*p)
1251 			error(2, "file name expected");
1252 		if (c != 'f') {
1253 			if (*p == '!') {
1254 				p++;
1255 				sh = 1;
1256 			}
1257 			else if (*p == '\\' && *(p + 1) == '!')
1258 				p++;
1259 		}
1260 		if (ed.restricted) {
1261 			register char*	s = p;
1262 
1263 			if (sh)
1264 				p--;
1265 			else
1266 				for (;;)
1267 				{
1268 					switch (*s++)
1269 					{
1270 					case 0:
1271 						break;
1272 					case '/':
1273 					case '\n':
1274 					case '\\':
1275 						sh = 1;
1276 						break;
1277 					default:
1278 						continue;
1279 					}
1280 					break;
1281 				}
1282 			if (sh)
1283 				error(2, "%s: restricted file name", p);
1284 		}
1285 		if (!sh && (!*sfstrbase(ed.buffer.file) || c == 'e' || c == 'f')) {
1286 			sfstrseek(ed.buffer.file, 0, SEEK_SET);
1287 			sfputr(ed.buffer.file, p, 0);
1288 		}
1289 		if (c == 'f')
1290 			return;
1291 	}
1292 	else if (c == 'f')
1293 		return;
1294 	else if (!*(p = sfstrbase(ed.buffer.file)))
1295 		error(2, "file name expected");
1296 	if (c == 'e') {
1297 		edit();
1298 		ed.addr2 = ed.zero;
1299 	}
1300 	if (sh) {
1301 		if (!(ed.iop = sfpopen(NiL, p, (c == 'e' || c == 'r') ? "r" : "w")))
1302 			error(ERROR_SYSTEM|2, "%s: cannot execute shell command", p);
1303 		p--;
1304 	}
1305 	else if (c == 'e' || c == 'r') {
1306 		if (!(ed.iop = sfopen(NiL, p, "r")))
1307 			error(ERROR_SYSTEM|2, "%s: cannot read", p);
1308 	}
1309 	else if ((c != 'W' || !(ed.iop = sfopen(NiL, p, "a"))) && !(ed.iop = sfopen(NiL, p, "w")))
1310 		error(ERROR_SYSTEM|2, "%s: cannot write", p);
1311 	error_info.file = p;
1312 }
1313 
1314 static void
global(int sense,int query)1315 global(int sense, int query)
1316 {
1317 	register char*		s;
1318 	register int		c;
1319 	register Line_t*	a1;
1320 
1321 	if (ed.global)
1322 		error(2, "recursive global not allowed");
1323 	setwide();
1324 	squeeze(ed.dol > ed.zero);
1325 	compile();
1326 	if (query)
1327 		newline();
1328 	else {
1329 		s = getrec(ed.buffer.global, '\n', REC_SPLICE|REC_TERMINATE);
1330 		if (s[0] == '\n' && !s[1])
1331 			sfputr(ed.buffer.global, "p\n", 0);
1332 	}
1333 	for (a1 = ed.zero; a1 <= ed.dol; a1++) {
1334 		a1->offset &= ~LINE_GLOBAL;
1335 		if (a1 >= ed.addr1 && a1 <= ed.addr2 && execute(a1, 0) == sense)
1336 			a1->offset |= LINE_GLOBAL;
1337 	}
1338 
1339 	/* special case: g/.../d (avoid n^2 algorithm) */
1340 
1341 	if (!query && s[0] == 'd' && s[1] == '\n' && !s[2])
1342 		gdelete();
1343 	else {
1344 		for (a1 = ed.zero; a1 <= ed.dol; a1++) {
1345 			if (a1->offset & LINE_GLOBAL) {
1346 				a1->offset &= ~LINE_GLOBAL;
1347 				ed.dot = a1;
1348 				if (query) {
1349 					putrec(lineget(a1->offset));
1350 					if ((c = getchr()) == EOF)
1351 						break;
1352 					else if (c == '\n')
1353 						continue;
1354 					else if (c == '&') {
1355 						newline();
1356 						if (!*(ed.global = sfstrbase(ed.buffer.query)))
1357 							error(2, "no saved command");
1358 					}
1359 					else {
1360 						ed.peekc = c;
1361 						ed.global = getrec(ed.buffer.query, '\n', REC_TERMINATE);
1362 					}
1363 				}
1364 				else
1365 					ed.global = s;
1366 				commands();
1367 				a1 = ed.zero;
1368 			}
1369 		}
1370 	}
1371 }
1372 
1373 static void
join(void)1374 join(void)
1375 {
1376 	register Line_t*	a1;
1377 	char*			s;
1378 
1379 	nonzero();
1380 	sfstrseek(ed.buffer.work, 0, SEEK_SET);
1381 	for (a1 = ed.addr1; a1 <= ed.addr2;)
1382 		sfputr(ed.buffer.work, lineget((a1++)->offset), -1);
1383 	a1 = ed.dot = ed.addr1;
1384 	if (!(s = sfstruse(ed.buffer.work)))
1385 		error(ERROR_SYSTEM|3, "out of space");
1386 	replace(a1, s);
1387 	if (a1 < ed.addr2)
1388 		rdelete(a1 + 1, ed.addr2);
1389 }
1390 
1391 static void
substitute(int inglob)1392 substitute(int inglob)
1393 {
1394 	register Line_t*	a1;
1395 	char*			s;
1396 	char*			e;
1397 	int			n;
1398 
1399 	n = getnum();
1400 	compile();
1401 	splice();
1402 	if (n = regsubcomp(&ed.re, input(0), submap, n, 0))
1403 		regfatal(&ed.re, 2, n);
1404 	else {
1405 		if (!(ed.re.re_sub->re_flags & REG_SUB_FULL))
1406 			ed.re.re_sub->re_flags |= REG_SUB_PRINT;
1407 		if ((n = *input(ed.re.re_npat)) && n != '\r' && n != '\n')
1408 			error(2, "extra characters at end of command");
1409 		ed.print = ed.re.re_sub->re_flags;
1410 	}
1411 	eat();
1412 	for (a1 = ed.addr1; a1 <= ed.addr2; a1++) {
1413 		if (execute(a1, 0)) {
1414 			if (!regsubexec(&ed.re, CUR(), elementsof(ed.match), ed.match)) {
1415 				inglob = 1;
1416 				s = ed.re.re_sub->re_buf;
1417 				SET(s, ed.re.re_sub->re_len);
1418 				if (e = strchr(s, '\n'))
1419 					*e++ = 0;
1420 				replace(a1, s);
1421 				if (e) {
1422 					ed.linebreak = e;
1423 					a1 = append(getbreak, a1, &ed.addr2);
1424 				}
1425 			}
1426 		}
1427 	}
1428 	if (!inglob)
1429 		error(2, "global pattern not found");
1430 	ed.dot = ed.addr2;
1431 }
1432 
1433 static void
reverse(register Line_t * a1,register Line_t * a2)1434 reverse(register Line_t* a1, register Line_t* a2)
1435 {
1436 	modify();
1437 	while (--a2 > a1) {
1438 		a1->event = a2->event = ed.event;
1439 		a2->undo = a2->offset;
1440 		a2->offset = a1->undo = a1->offset;
1441 		(a1++)->offset = a2->undo;
1442 	}
1443 }
1444 
1445 static void
move(int cflag)1446 move(int cflag)
1447 {
1448 	register Line_t*	ad1;
1449 	register Line_t*	ad2;
1450 	Line_t*			adt;
1451 	unsigned long		ad1_off;
1452 	unsigned long		adt_off;
1453 
1454 	nonzero();
1455 	if (!(adt = address()))
1456 		error(2, "invalid move destination");
1457 	newline();
1458 	if (cflag) {
1459 		ad1_off = ed.dol - ed.zero + 1;
1460 		adt_off = adt - ed.zero;
1461 		append(getcopy, ed.dol, NiL);
1462 		ad1 = ed.zero + ad1_off;
1463 		ad2 = ed.dol;
1464 		adt = ed.zero + adt_off;
1465 	}
1466 	else {
1467 		ad2 = ed.addr2;
1468 		for (ad1 = ed.addr1; ad1 <= ad2; ad1++)
1469 			ad1->offset &= ~LINE_GLOBAL;
1470 		ad1 = ed.addr1;
1471 	}
1472 	ad2++;
1473 	if (adt < ad1) {
1474 		ed.dot = adt + (ad2 - ad1);
1475 		if (++adt == ad1)
1476 			return;
1477 		reverse(adt, ad1);
1478 		reverse(ad1, ad2);
1479 		reverse(adt, ad2);
1480 	}
1481 	else if (adt >= ad2) {
1482 		ed.dot = adt++;
1483 		reverse(ad1, ad2);
1484 		reverse(ad2, adt);
1485 		reverse(ad1, adt);
1486 	}
1487 	else
1488 		error(2, "move would do nothing");
1489 }
1490 
1491 static void
commands(void)1492 commands(void)
1493 {
1494 	register Line_t*	a1;
1495 	register int		c;
1496 	register int		n;
1497 	char*			s;
1498 	int			lastsep;
1499 
1500 	for (;;) {
1501 		trap();
1502 		if (ed.print & (REG_SUB_LIST|REG_SUB_NUMBER|REG_SUB_PRINT)) {
1503 			ed.addr1 = ed.addr2 = ed.dot;
1504 			print();
1505 		}
1506 		if (!ed.global) {
1507 			ed.evented = 0;
1508 			if (ed.prompt > 0)
1509 				sfputr(ed.msg, sfstrbase(ed.buffer.prompt), -1);
1510 		}
1511 		if ((c = getchr()) == ',' || c == ';') {
1512 			ed.given = 1;
1513 			ed.addr1 = (lastsep = c) == ',' ? ed.zero + 1 : ed.dot;
1514 			a1 = ed.dol;
1515 			c = getchr();
1516 		}
1517 		else {
1518 			ed.addr1 = 0;
1519 			ed.peekc = c;
1520 			c = '\n';
1521 			for (;;) {
1522 				lastsep = c;
1523 				a1 = address();
1524 				c = getchr();
1525 				if (c != ',' && c != ';')
1526 					break;
1527 				if (lastsep == ',')
1528 					error(2, "invalid address");
1529 				if (!a1) {
1530 					a1 = ed.zero + 1;
1531 					if (a1 > ed.dol)
1532 						a1--;
1533 				}
1534 				ed.addr1 = a1;
1535 				if (c == ';')
1536 					ed.dot = a1;
1537 			}
1538 			if (lastsep != '\n' && !a1)
1539 				a1 = ed.dol;
1540 		}
1541 		if (!(ed.addr2 = a1)) {
1542 			ed.given = 0;
1543 			ed.addr2 = ed.dot;
1544 		}
1545 		else
1546 			ed.given = 1;
1547 		if (!ed.addr1)
1548 			ed.addr1 = ed.addr2;
1549 		switch (c) {
1550 
1551 		case 'a':
1552 			add(0);
1553 			continue;
1554 
1555 		case 'c':
1556 			nonzero();
1557 			newline();
1558 			rdelete(ed.addr1, ed.addr2);
1559 			append(getline, ed.addr1 - 1, NiL);
1560 			continue;
1561 
1562 		case 'd':
1563 			nonzero();
1564 			newline();
1565 			rdelete(ed.addr1, ed.addr2);
1566 			continue;
1567 
1568 		case 'E':
1569 			ed.modified = 0;
1570 			c = 'e';
1571 			/*FALLTHROUGH*/
1572 		case 'e':
1573 			setnoaddr();
1574 			if (ed.verbose && ed.modified) {
1575 				ed.modified = 0;
1576 				error(2, "modified data not written");
1577 			}
1578 			/*FALLTHROUGH*/
1579 		case 'r':
1580 			filename(c);
1581 			setwide();
1582 			squeeze(0);
1583 			c = ed.zero != ed.dol;
1584 			append(getfile, ed.addr2, NiL);
1585 			ed.modified = c;
1586 			exfile();
1587 			continue;
1588 
1589 		case 'f':
1590 			setnoaddr();
1591 			filename(c);
1592 			putrec(sfstrbase(ed.buffer.file));
1593 			continue;
1594 
1595 		case 'G':
1596 			global(1, 1);
1597 			continue;
1598 
1599 		case 'g':
1600 			global(1, 0);
1601 			continue;
1602 
1603 		case 'H':
1604 			ed.help = !ed.help;
1605 			/*FALLTHROUGH*/
1606 		case 'h':
1607 			setnoaddr();
1608 			newline();
1609 			if (ed.help || c == 'h')
1610 				sfputr(ed.msg, sfstrbase(ed.buffer.help), '\n');
1611 			continue;
1612 
1613 		case 'i':
1614 			add(-1);
1615 			continue;
1616 
1617 		case 'j':
1618 			if (!ed.given)
1619 				ed.addr2++;
1620 			newline();
1621 			join();
1622 			continue;
1623 
1624 		case 'k':
1625 			nonzero();
1626 			if ((c = getchr()) == EOF || (c -= MARK_MIN) < 0 || c >= elementsof(ed.marks))
1627 				error(2, "invalid mark");
1628 			newline();
1629 			ed.addr2->offset |= LINE_MARKED;
1630 			ed.marks[c] = ed.addr2->offset & ~LINE_GLOBAL;
1631 			ed.marked = 1;
1632 			continue;
1633 
1634 		case 'm':
1635 			move(0);
1636 			continue;
1637 
1638 		case 'n':
1639 			ed.print |= REG_SUB_NUMBER;
1640 			newline();
1641 			print();
1642 			continue;
1643 
1644 		case '\n':
1645 			if (!a1) {
1646 				a1 = ed.dot + 1;
1647 				ed.addr2 = a1;
1648 				ed.addr1 = a1;
1649 			}
1650 			if (lastsep == ';')
1651 				ed.addr1 = a1;
1652 			print();
1653 			continue;
1654 
1655 		case 'l':
1656 			ed.print |= REG_SUB_LIST;
1657 			/*FALLTHROUGH*/
1658 		case 'p':
1659 			newline();
1660 			print();
1661 			continue;
1662 
1663 		case 'P':
1664 			setnoaddr();
1665 			s = getrec(ed.buffer.line, '\n', 0);
1666 			if (*s || !(ed.prompt = -ed.prompt) && (s = "*")) {
1667 				sfstrseek(ed.buffer.prompt, 0, SEEK_SET);
1668 				sfputr(ed.buffer.prompt, s, 0);
1669 				ed.prompt = 1;
1670 			}
1671 			continue;
1672 
1673 		case 'Q':
1674 			ed.modified = 0;
1675 			/*FALLTHROUGH*/
1676 		case 'q':
1677 			setnoaddr();
1678 			newline();
1679 			quit(0);
1680 			continue;
1681 
1682 		case 'S':
1683 			setnoaddr();
1684 			newline();
1685 			s = strchr(usage, '\n') + 5;
1686 			sfprintf(ed.msg, "file=\"%s\"%s%s%s prompt=\"%s\" tmp=%lu%s event=%lu version=\"%-.*s\"\n", sfstrbase(ed.buffer.file), ed.modified ? " modified" : "", ed.help ? " help" : "", ed.verbose ? " verbose" : "", sfstrbase(ed.buffer.prompt), ed.tmpoff, ed.tmpoff > BLOCK_TMP ? "[file]" : "", ed.event, strchr(s, '\n') - s, s);
1687 			continue;
1688 
1689 		case 's':
1690 			nonzero();
1691 			substitute(ed.global != 0);
1692 			continue;
1693 
1694 		case 't':
1695 			move(1);
1696 			continue;
1697 
1698 		case 'u':
1699 			setnoaddr();
1700 			newline();
1701 			undo();
1702 			continue;
1703 
1704 		case 'V':
1705 			global(0, 1);
1706 			continue;
1707 
1708 		case 'v':
1709 			global(0, 0);
1710 			continue;
1711 
1712 		case 'W':
1713 		case 'w':
1714 			setwide();
1715 			squeeze(ed.dol > ed.zero);
1716 			if ((n = getchr()) != 'q' && n != 'Q') {
1717 				ed.peekc = n;
1718 				n = 0;
1719 			}
1720 			filename(c);
1721 			if (ed.dol > ed.zero)
1722 				putfile();
1723 			exfile();
1724 			if (n == 'Q' || ed.addr1 <= ed.zero + 1 && ed.addr2 == ed.dol)
1725 				ed.modified = 0;
1726 			if (n)
1727 				quit(0);
1728 			continue;
1729 
1730 		case 'z':
1731 			nonzero();
1732 			page();
1733 			continue;
1734 
1735 		case '=':
1736 			setwide();
1737 			squeeze(0);
1738 			newline();
1739 			sfprintf(ed.msg, "%d\n", ed.addr2 - ed.zero);
1740 			continue;
1741 
1742 		case '!':
1743 			if (ed.restricted)
1744 				error(2, "%c: restricted command", c);
1745 			shell();
1746 			continue;
1747 
1748 		case '#':
1749 			setnoaddr();
1750 			getrec(ed.buffer.line, '\n', REC_IGNORE);
1751 			continue;
1752 
1753 		case EOF:
1754 			return;
1755 
1756 		}
1757 		error(2, "unknown command");
1758 	}
1759 }
1760 
1761 int
main(int argc,char ** argv)1762 main(int argc, char** argv)
1763 {
1764 	char*	s;
1765 
1766 	NoP(argc);
1767 	if (s = strrchr(*argv, '/')) s++;
1768 	else s = *argv;
1769 	ed.restricted = streq(s, "red");
1770 	error_info.id = s;
1771 	error_info.write = helpwrite;
1772 	init();
1773 	for (;;)
1774 	{
1775 		for (;;) {
1776 			switch (optget(argv, usage)) {
1777 
1778 			case 'O':
1779 				ed.reflags |= REG_LENIENT;
1780 				continue;
1781 
1782 			case 'S':
1783 				ed.reflags &= ~REG_LENIENT;
1784 				continue;
1785 
1786 			case 'h':
1787 				ed.help = 1;
1788 				continue;
1789 
1790 			case 'o':
1791 				ed.msg = sfstderr;
1792 				sfstrseek(ed.buffer.file, 0, SEEK_SET);
1793 				sfputr(ed.buffer.file, "/dev/stdout", 0);
1794 				continue;
1795 
1796 			case 'p':
1797 				sfstrseek(ed.buffer.prompt, 0, SEEK_SET);
1798 				sfputr(ed.buffer.prompt, opt_info.arg, 0);
1799 				ed.prompt = 1;
1800 				continue;
1801 
1802 			case 'q':
1803 				signal(SIGQUIT, SIG_DFL);
1804 				ed.verbose = 1;
1805 				continue;
1806 
1807 			case 's':
1808 				ed.verbose = 0;
1809 				continue;
1810 
1811 			case '?':
1812 				ed.help++;
1813 				error(ERROR_USAGE|4, "%s", opt_info.arg);
1814 				ed.help--;
1815 				break;
1816 
1817 			case ':':
1818 				ed.help++;
1819 				error(2, "%s", opt_info.arg);
1820 				ed.help--;
1821 				continue;
1822 
1823 			}
1824 			break;
1825 		}
1826 		if (!*(argv += opt_info.index) || **argv != '-' || *(*argv + 1))
1827 			break;
1828 		ed.verbose = 0;
1829 	}
1830 	if (*argv) {
1831 		if (*(argv + 1))
1832 			error(ERROR_USAGE|4, "%s", optusage(NiL));
1833 		sfprintf(ed.buffer.global, "e %s", *argv);
1834 		if (!(ed.global = sfstruse(ed.buffer.global)))
1835 			error(ERROR_SYSTEM|3, "out of space");
1836 	}
1837 	edit();
1838 	sfdcslow(sfstdin);
1839 	setjmp(ed.again);
1840 	commands();
1841 	quit(0);
1842 	exit(0);
1843 }
1844