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