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