ed.c - 9base - revived minimalist port of Plan 9 userland to Unix | |
git clone git://git.suckless.org/9base | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
ed.c (22554B) | |
--- | |
1 /* | |
2 * Editor | |
3 */ | |
4 #include <u.h> | |
5 #include <libc.h> | |
6 #include <bio.h> | |
7 #include <regexp.h> | |
8 | |
9 #undef EOF /* stdio? */ | |
10 | |
11 enum | |
12 { | |
13 FNSIZE = 128, /* file name */ | |
14 LBSIZE = 4096, /* max line size */ | |
15 BLKSIZE = 4096, /* block size in temp file… | |
16 NBLK = 8191, /* max size of temp file */ | |
17 ESIZE = 256, /* max size of reg exp */ | |
18 GBSIZE = 256, /* max size of global comman… | |
19 MAXSUB = 9, /* max number of sub reg exp */ | |
20 ESCFLG = 0xFFFF, /* escape Rune - user defined cod… | |
21 EOF = -1 | |
22 }; | |
23 | |
24 void (*oldhup)(int); | |
25 void (*oldquit)(int); | |
26 int* addr1; | |
27 int* addr2; | |
28 int anymarks; | |
29 int col; | |
30 long count; | |
31 int* dol; | |
32 int* dot; | |
33 int fchange; | |
34 char file[FNSIZE]; | |
35 Rune genbuf[LBSIZE]; | |
36 int given; | |
37 Rune* globp; | |
38 int iblock; | |
39 int ichanged; | |
40 int io; | |
41 Biobuf iobuf; | |
42 int lastc; | |
43 char line[70]; | |
44 Rune* linebp; | |
45 Rune linebuf[LBSIZE]; | |
46 int listf; | |
47 int listn; | |
48 Rune* loc1; | |
49 Rune* loc2; | |
50 int names[26]; | |
51 int nleft; | |
52 int oblock; | |
53 int oflag; | |
54 Reprog *pattern; | |
55 int peekc; | |
56 int pflag; | |
57 int rescuing; | |
58 Rune rhsbuf[LBSIZE/sizeof(Rune)]; | |
59 char savedfile[FNSIZE]; | |
60 jmp_buf savej; | |
61 int subnewa; | |
62 int subolda; | |
63 Resub subexp[MAXSUB]; | |
64 char* tfname; | |
65 int tline; | |
66 int waiting; | |
67 int wrapp; | |
68 int* zero; | |
69 | |
70 char Q[] = ""; | |
71 char T[] = "TMP"; | |
72 char WRERR[] = "WRITE ERROR"; | |
73 int bpagesize = 20; | |
74 char hex[] = "0123456789abcdef"; | |
75 char* linp = line; | |
76 ulong nlall = 128; | |
77 int tfile = -1; | |
78 int vflag = 1; | |
79 | |
80 #define getline p9getline | |
81 void add(int); | |
82 int* address(void); | |
83 int append(int(*)(void), int*); | |
84 void browse(void); | |
85 void callunix(void); | |
86 void commands(void); | |
87 void compile(int); | |
88 int compsub(void); | |
89 void dosub(void); | |
90 void error(char*); | |
91 int match(int*); | |
92 void exfile(int); | |
93 void filename(int); | |
94 Rune* getblock(int, int); | |
95 int getchr(void); | |
96 int getcopy(void); | |
97 int getfile(void); | |
98 Rune* getline(int); | |
99 int getnum(void); | |
100 int getsub(void); | |
101 int gettty(void); | |
102 void global(int); | |
103 void init(void); | |
104 void join(void); | |
105 void move(int); | |
106 void newline(void); | |
107 void nonzero(void); | |
108 void notifyf(void*, char*); | |
109 Rune* place(Rune*, Rune*, Rune*); | |
110 void printcom(void); | |
111 void putchr(int); | |
112 void putd(void); | |
113 void putfile(void); | |
114 int putline(void); | |
115 void putshst(Rune*); | |
116 void putst(char*); | |
117 void quit(void); | |
118 void rdelete(int*, int*); | |
119 void regerror(char *); | |
120 void reverse(int*, int*); | |
121 void setnoaddr(void); | |
122 void setwide(void); | |
123 void squeeze(int); | |
124 void substitute(int); | |
125 char* __mktemp(char *as); | |
126 | |
127 Rune La[] = { 'a', 0 }; | |
128 Rune Lr[] = { 'r', 0 }; | |
129 | |
130 char tmp[] = "/var/tmp/eXXXXX"; | |
131 | |
132 void | |
133 main(int argc, char *argv[]) | |
134 { | |
135 char *p1, *p2; | |
136 | |
137 notify(notifyf); | |
138 ARGBEGIN { | |
139 case 'o': | |
140 oflag = 1; | |
141 vflag = 0; | |
142 break; | |
143 } ARGEND | |
144 | |
145 USED(argc); | |
146 if(*argv && (strcmp(*argv, "-") == 0)) { | |
147 argv++; | |
148 vflag = 0; | |
149 } | |
150 if(oflag) { | |
151 p1 = "/dev/stdout"; | |
152 p2 = savedfile; | |
153 while(*p2++ = *p1++) | |
154 ; | |
155 globp = La; | |
156 } else | |
157 if(*argv) { | |
158 p1 = *argv; | |
159 p2 = savedfile; | |
160 while(*p2++ = *p1++) | |
161 if(p2 >= &savedfile[sizeof(savedfile)]) | |
162 p2--; | |
163 globp = Lr; | |
164 } | |
165 zero = malloc((nlall+5)*sizeof(int*)); | |
166 tfname = __mktemp(tmp); | |
167 init(); | |
168 setjmp(savej); | |
169 commands(); | |
170 quit(); | |
171 } | |
172 | |
173 void | |
174 commands(void) | |
175 { | |
176 int *a1, c, temp; | |
177 char lastsep; | |
178 Dir *d; | |
179 | |
180 for(;;) { | |
181 if(pflag) { | |
182 pflag = 0; | |
183 addr1 = addr2 = dot; | |
184 printcom(); | |
185 } | |
186 c = '\n'; | |
187 for(addr1 = 0;;) { | |
188 lastsep = c; | |
189 a1 = address(); | |
190 c = getchr(); | |
191 if(c != ',' && c != ';') | |
192 break; | |
193 if(lastsep == ',') | |
194 error(Q); | |
195 if(a1 == 0) { | |
196 a1 = zero+1; | |
197 if(a1 > dol) | |
198 a1--; | |
199 } | |
200 addr1 = a1; | |
201 if(c == ';') | |
202 dot = a1; | |
203 } | |
204 if(lastsep != '\n' && a1 == 0) | |
205 a1 = dol; | |
206 if((addr2=a1) == 0) { | |
207 given = 0; | |
208 addr2 = dot; | |
209 } else | |
210 given = 1; | |
211 if(addr1 == 0) | |
212 addr1 = addr2; | |
213 switch(c) { | |
214 | |
215 case 'a': | |
216 add(0); | |
217 continue; | |
218 | |
219 case 'b': | |
220 nonzero(); | |
221 browse(); | |
222 continue; | |
223 | |
224 case 'c': | |
225 nonzero(); | |
226 newline(); | |
227 rdelete(addr1, addr2); | |
228 append(gettty, addr1-1); | |
229 continue; | |
230 | |
231 case 'd': | |
232 nonzero(); | |
233 newline(); | |
234 rdelete(addr1, addr2); | |
235 continue; | |
236 | |
237 case 'E': | |
238 fchange = 0; | |
239 c = 'e'; | |
240 case 'e': | |
241 setnoaddr(); | |
242 if(vflag && fchange) { | |
243 fchange = 0; | |
244 error(Q); | |
245 } | |
246 filename(c); | |
247 init(); | |
248 addr2 = zero; | |
249 goto caseread; | |
250 | |
251 case 'f': | |
252 setnoaddr(); | |
253 filename(c); | |
254 putst(savedfile); | |
255 continue; | |
256 | |
257 case 'g': | |
258 global(1); | |
259 continue; | |
260 | |
261 case 'i': | |
262 add(-1); | |
263 continue; | |
264 | |
265 | |
266 case 'j': | |
267 if(!given) | |
268 addr2++; | |
269 newline(); | |
270 join(); | |
271 continue; | |
272 | |
273 case 'k': | |
274 nonzero(); | |
275 c = getchr(); | |
276 if(c < 'a' || c > 'z') | |
277 error(Q); | |
278 newline(); | |
279 names[c-'a'] = *addr2 & ~01; | |
280 anymarks |= 01; | |
281 continue; | |
282 | |
283 case 'm': | |
284 move(0); | |
285 continue; | |
286 | |
287 case 'n': | |
288 listn++; | |
289 newline(); | |
290 printcom(); | |
291 continue; | |
292 | |
293 case '\n': | |
294 if(a1==0) { | |
295 a1 = dot+1; | |
296 addr2 = a1; | |
297 addr1 = a1; | |
298 } | |
299 if(lastsep==';') | |
300 addr1 = a1; | |
301 printcom(); | |
302 continue; | |
303 | |
304 case 'l': | |
305 listf++; | |
306 case 'p': | |
307 case 'P': | |
308 newline(); | |
309 printcom(); | |
310 continue; | |
311 | |
312 case 'Q': | |
313 fchange = 0; | |
314 case 'q': | |
315 setnoaddr(); | |
316 newline(); | |
317 quit(); | |
318 | |
319 case 'r': | |
320 filename(c); | |
321 caseread: | |
322 if((io=open(file, OREAD)) < 0) { | |
323 lastc = '\n'; | |
324 error(file); | |
325 } | |
326 if((d = dirfstat(io)) != nil){ | |
327 if(d->mode & DMAPPEND) | |
328 print("warning: %s is append onl… | |
329 free(d); | |
330 } | |
331 Binit(&iobuf, io, OREAD); | |
332 setwide(); | |
333 squeeze(0); | |
334 c = zero != dol; | |
335 append(getfile, addr2); | |
336 exfile(OREAD); | |
337 | |
338 fchange = c; | |
339 continue; | |
340 | |
341 case 's': | |
342 nonzero(); | |
343 substitute(globp != 0); | |
344 continue; | |
345 | |
346 case 't': | |
347 move(1); | |
348 continue; | |
349 | |
350 case 'u': | |
351 nonzero(); | |
352 newline(); | |
353 if((*addr2&~01) != subnewa) | |
354 error(Q); | |
355 *addr2 = subolda; | |
356 dot = addr2; | |
357 continue; | |
358 | |
359 case 'v': | |
360 global(0); | |
361 continue; | |
362 | |
363 case 'W': | |
364 wrapp++; | |
365 case 'w': | |
366 setwide(); | |
367 squeeze(dol>zero); | |
368 temp = getchr(); | |
369 if(temp != 'q' && temp != 'Q') { | |
370 peekc = temp; | |
371 temp = 0; | |
372 } | |
373 filename(c); | |
374 if(!wrapp || | |
375 ((io = open(file, OWRITE)) == -1) || | |
376 ((seek(io, 0L, 2)) == -1)) | |
377 if((io = create(file, OWRITE, 0666)) < 0) | |
378 error(file); | |
379 Binit(&iobuf, io, OWRITE); | |
380 wrapp = 0; | |
381 if(dol > zero) | |
382 putfile(); | |
383 exfile(OWRITE); | |
384 if(addr1<=zero+1 && addr2==dol) | |
385 fchange = 0; | |
386 if(temp == 'Q') | |
387 fchange = 0; | |
388 if(temp) | |
389 quit(); | |
390 continue; | |
391 | |
392 case '=': | |
393 setwide(); | |
394 squeeze(0); | |
395 newline(); | |
396 count = addr2 - zero; | |
397 putd(); | |
398 putchr('\n'); | |
399 continue; | |
400 | |
401 case '!': | |
402 callunix(); | |
403 continue; | |
404 | |
405 case EOF: | |
406 return; | |
407 | |
408 } | |
409 error(Q); | |
410 } | |
411 } | |
412 | |
413 void | |
414 printcom(void) | |
415 { | |
416 int *a1; | |
417 | |
418 nonzero(); | |
419 a1 = addr1; | |
420 do { | |
421 if(listn) { | |
422 count = a1-zero; | |
423 putd(); | |
424 putchr('\t'); | |
425 } | |
426 putshst(getline(*a1++)); | |
427 } while(a1 <= addr2); | |
428 dot = addr2; | |
429 listf = 0; | |
430 listn = 0; | |
431 pflag = 0; | |
432 } | |
433 | |
434 int* | |
435 address(void) | |
436 { | |
437 int sign, *a, opcnt, nextopand, *b, c; | |
438 | |
439 nextopand = -1; | |
440 sign = 1; | |
441 opcnt = 0; | |
442 a = dot; | |
443 do { | |
444 do { | |
445 c = getchr(); | |
446 } while(c == ' ' || c == '\t'); | |
447 if(c >= '0' && c <= '9') { | |
448 peekc = c; | |
449 if(!opcnt) | |
450 a = zero; | |
451 a += sign*getnum(); | |
452 } else | |
453 switch(c) { | |
454 case '$': | |
455 a = dol; | |
456 case '.': | |
457 if(opcnt) | |
458 error(Q); | |
459 break; | |
460 case '\'': | |
461 c = getchr(); | |
462 if(opcnt || c < 'a' || c > 'z') | |
463 error(Q); | |
464 a = zero; | |
465 do { | |
466 a++; | |
467 } while(a <= dol && names[c-'a'] != (*a & ~01)); | |
468 break; | |
469 case '?': | |
470 sign = -sign; | |
471 case '/': | |
472 compile(c); | |
473 b = a; | |
474 for(;;) { | |
475 a += sign; | |
476 if(a <= zero) | |
477 a = dol; | |
478 if(a > dol) | |
479 a = zero; | |
480 if(match(a)) | |
481 break; | |
482 if(a == b) | |
483 error(Q); | |
484 } | |
485 break; | |
486 default: | |
487 if(nextopand == opcnt) { | |
488 a += sign; | |
489 if(a < zero || dol < a) | |
490 continue; /* error(Q); */ | |
491 } | |
492 if(c != '+' && c != '-' && c != '^') { | |
493 peekc = c; | |
494 if(opcnt == 0) | |
495 a = 0; | |
496 return a; | |
497 } | |
498 sign = 1; | |
499 if(c != '+') | |
500 sign = -sign; | |
501 nextopand = ++opcnt; | |
502 continue; | |
503 } | |
504 sign = 1; | |
505 opcnt++; | |
506 } while(zero <= a && a <= dol); | |
507 error(Q); | |
508 return 0; | |
509 } | |
510 | |
511 int | |
512 getnum(void) | |
513 { | |
514 int r, c; | |
515 | |
516 r = 0; | |
517 for(;;) { | |
518 c = getchr(); | |
519 if(c < '0' || c > '9') | |
520 break; | |
521 r = r*10 + (c-'0'); | |
522 } | |
523 peekc = c; | |
524 return r; | |
525 } | |
526 | |
527 void | |
528 setwide(void) | |
529 { | |
530 if(!given) { | |
531 addr1 = zero + (dol>zero); | |
532 addr2 = dol; | |
533 } | |
534 } | |
535 | |
536 void | |
537 setnoaddr(void) | |
538 { | |
539 if(given) | |
540 error(Q); | |
541 } | |
542 | |
543 void | |
544 nonzero(void) | |
545 { | |
546 squeeze(1); | |
547 } | |
548 | |
549 void | |
550 squeeze(int i) | |
551 { | |
552 if(addr1 < zero+i || addr2 > dol || addr1 > addr2) | |
553 error(Q); | |
554 } | |
555 | |
556 void | |
557 newline(void) | |
558 { | |
559 int c; | |
560 | |
561 c = getchr(); | |
562 if(c == '\n' || c == EOF) | |
563 return; | |
564 if(c == 'p' || c == 'l' || c == 'n') { | |
565 pflag++; | |
566 if(c == 'l') | |
567 listf++; | |
568 else | |
569 if(c == 'n') | |
570 listn++; | |
571 c = getchr(); | |
572 if(c == '\n') | |
573 return; | |
574 } | |
575 error(Q); | |
576 } | |
577 | |
578 void | |
579 filename(int comm) | |
580 { | |
581 char *p1, *p2; | |
582 Rune rune; | |
583 int c; | |
584 | |
585 count = 0; | |
586 c = getchr(); | |
587 if(c == '\n' || c == EOF) { | |
588 p1 = savedfile; | |
589 if(*p1 == 0 && comm != 'f') | |
590 error(Q); | |
591 p2 = file; | |
592 while(*p2++ = *p1++) | |
593 ; | |
594 return; | |
595 } | |
596 if(c != ' ') | |
597 error(Q); | |
598 while((c=getchr()) == ' ') | |
599 ; | |
600 if(c == '\n') | |
601 error(Q); | |
602 p1 = file; | |
603 do { | |
604 if(p1 >= &file[sizeof(file)-6] || c == ' ' || c == EOF) | |
605 error(Q); | |
606 rune = c; | |
607 p1 += runetochar(p1, &rune); | |
608 } while((c=getchr()) != '\n'); | |
609 *p1 = 0; | |
610 if(savedfile[0] == 0 || comm == 'e' || comm == 'f') { | |
611 p1 = savedfile; | |
612 p2 = file; | |
613 while(*p1++ = *p2++) | |
614 ; | |
615 } | |
616 } | |
617 | |
618 void | |
619 exfile(int om) | |
620 { | |
621 | |
622 if(om == OWRITE) | |
623 if(Bflush(&iobuf) < 0) | |
624 error(Q); | |
625 close(io); | |
626 io = -1; | |
627 if(vflag) { | |
628 putd(); | |
629 putchr('\n'); | |
630 } | |
631 } | |
632 | |
633 void | |
634 error1(char *s) | |
635 { | |
636 int c; | |
637 | |
638 wrapp = 0; | |
639 listf = 0; | |
640 listn = 0; | |
641 count = 0; | |
642 seek(0, 0, 2); | |
643 pflag = 0; | |
644 if(globp) | |
645 lastc = '\n'; | |
646 globp = 0; | |
647 peekc = lastc; | |
648 if(lastc) | |
649 for(;;) { | |
650 c = getchr(); | |
651 if(c == '\n' || c == EOF) | |
652 break; | |
653 } | |
654 if(io > 0) { | |
655 close(io); | |
656 io = -1; | |
657 } | |
658 putchr('?'); | |
659 putst(s); | |
660 } | |
661 | |
662 void | |
663 error(char *s) | |
664 { | |
665 error1(s); | |
666 longjmp(savej, 1); | |
667 } | |
668 | |
669 void | |
670 rescue(void) | |
671 { | |
672 rescuing = 1; | |
673 if(dol > zero) { | |
674 addr1 = zero+1; | |
675 addr2 = dol; | |
676 io = create("ed.hup", OWRITE, 0666); | |
677 if(io > 0){ | |
678 Binit(&iobuf, io, OWRITE); | |
679 putfile(); | |
680 } | |
681 } | |
682 fchange = 0; | |
683 quit(); | |
684 } | |
685 | |
686 void | |
687 notifyf(void *a, char *s) | |
688 { | |
689 if(strcmp(s, "interrupt") == 0){ | |
690 if(rescuing || waiting) | |
691 noted(NCONT); | |
692 putchr('\n'); | |
693 lastc = '\n'; | |
694 error1(Q); | |
695 notejmp(a, savej, 0); | |
696 } | |
697 if(strcmp(s, "hangup") == 0 || strcmp(s, "kill") == 0){ | |
698 if(rescuing) | |
699 noted(NDFLT); | |
700 rescue(); | |
701 } | |
702 if(strstr(s, "child")) | |
703 noted(NCONT); | |
704 fprint(2, "ed: note: %s\n", s); | |
705 abort(); | |
706 } | |
707 | |
708 int | |
709 getchr(void) | |
710 { | |
711 char s[UTFmax]; | |
712 int i; | |
713 Rune r; | |
714 | |
715 if(lastc = peekc) { | |
716 peekc = 0; | |
717 return lastc; | |
718 } | |
719 if(globp) { | |
720 if((lastc=*globp++) != 0) | |
721 return lastc; | |
722 globp = 0; | |
723 return EOF; | |
724 } | |
725 for(i=0;;) { | |
726 if(read(0, s+i, 1) <= 0) | |
727 return lastc = EOF; | |
728 i++; | |
729 if(fullrune(s, i)) | |
730 break; | |
731 | |
732 } | |
733 chartorune(&r, s); | |
734 lastc = r; | |
735 return lastc; | |
736 } | |
737 | |
738 int | |
739 gety(void) | |
740 { | |
741 int c; | |
742 Rune *gf, *p; | |
743 | |
744 p = linebuf; | |
745 gf = globp; | |
746 for(;;) { | |
747 c = getchr(); | |
748 if(c == '\n') { | |
749 *p = 0; | |
750 return 0; | |
751 } | |
752 if(c == EOF) { | |
753 if(gf) | |
754 peekc = c; | |
755 return c; | |
756 } | |
757 if(c == 0) | |
758 continue; | |
759 *p++ = c; | |
760 if(p >= &linebuf[LBSIZE-2]) | |
761 error(Q); | |
762 } | |
763 } | |
764 | |
765 int | |
766 gettty(void) | |
767 { | |
768 int rc; | |
769 | |
770 rc = gety(); | |
771 if(rc) | |
772 return rc; | |
773 if(linebuf[0] == '.' && linebuf[1] == 0) | |
774 return EOF; | |
775 return 0; | |
776 } | |
777 | |
778 int | |
779 getfile(void) | |
780 { | |
781 int c; | |
782 Rune *lp; | |
783 | |
784 lp = linebuf; | |
785 do { | |
786 c = Bgetrune(&iobuf); | |
787 if(c < 0) { | |
788 if(lp > linebuf) { | |
789 putst("'\\n' appended"); | |
790 c = '\n'; | |
791 } else | |
792 return EOF; | |
793 } | |
794 if(lp >= &linebuf[LBSIZE]) { | |
795 lastc = '\n'; | |
796 error(Q); | |
797 } | |
798 *lp++ = c; | |
799 count++; | |
800 } while(c != '\n'); | |
801 lp[-1] = 0; | |
802 return 0; | |
803 } | |
804 | |
805 void | |
806 putfile(void) | |
807 { | |
808 int *a1; | |
809 Rune *lp; | |
810 long c; | |
811 | |
812 a1 = addr1; | |
813 do { | |
814 lp = getline(*a1++); | |
815 for(;;) { | |
816 count++; | |
817 c = *lp++; | |
818 if(c == 0) { | |
819 if(Bputrune(&iobuf, '\n') < 0) | |
820 error(Q); | |
821 break; | |
822 } | |
823 if(Bputrune(&iobuf, c) < 0) | |
824 error(Q); | |
825 } | |
826 } while(a1 <= addr2); | |
827 if(Bflush(&iobuf) < 0) | |
828 error(Q); | |
829 } | |
830 | |
831 int | |
832 append(int (*f)(void), int *a) | |
833 { | |
834 int *a1, *a2, *rdot, nline, d; | |
835 | |
836 nline = 0; | |
837 dot = a; | |
838 while((*f)() == 0) { | |
839 if((dol-zero) >= nlall) { | |
840 nlall += 512; | |
841 a1 = realloc(zero, (nlall+50)*sizeof(int*)); | |
842 if(a1 == 0) { | |
843 error("MEM?"); | |
844 rescue(); | |
845 } | |
846 /* relocate pointers; avoid wraparound if sizeof… | |
847 d = addr1 - zero; | |
848 addr1 = a1 + d; | |
849 d = addr2 - zero; | |
850 addr2 = a1 + d; | |
851 d = dol - zero; | |
852 dol = a1 + d; | |
853 d = dot - zero; | |
854 dot = a1 + d; | |
855 zero = a1; | |
856 } | |
857 d = putline(); | |
858 nline++; | |
859 a1 = ++dol; | |
860 a2 = a1+1; | |
861 rdot = ++dot; | |
862 while(a1 > rdot) | |
863 *--a2 = *--a1; | |
864 *rdot = d; | |
865 } | |
866 return nline; | |
867 } | |
868 | |
869 void | |
870 add(int i) | |
871 { | |
872 if(i && (given || dol > zero)) { | |
873 addr1--; | |
874 addr2--; | |
875 } | |
876 squeeze(0); | |
877 newline(); | |
878 append(gettty, addr2); | |
879 } | |
880 | |
881 void | |
882 browse(void) | |
883 { | |
884 int forward, n; | |
885 static int bformat, bnum; /* 0 */ | |
886 | |
887 forward = 1; | |
888 peekc = getchr(); | |
889 if(peekc != '\n'){ | |
890 if(peekc == '-' || peekc == '+') { | |
891 if(peekc == '-') | |
892 forward = 0; | |
893 getchr(); | |
894 } | |
895 n = getnum(); | |
896 if(n > 0) | |
897 bpagesize = n; | |
898 } | |
899 newline(); | |
900 if(pflag) { | |
901 bformat = listf; | |
902 bnum = listn; | |
903 } else { | |
904 listf = bformat; | |
905 listn = bnum; | |
906 } | |
907 if(forward) { | |
908 addr1 = addr2; | |
909 addr2 += bpagesize; | |
910 if(addr2 > dol) | |
911 addr2 = dol; | |
912 } else { | |
913 addr1 = addr2-bpagesize; | |
914 if(addr1 <= zero) | |
915 addr1 = zero+1; | |
916 } | |
917 printcom(); | |
918 } | |
919 | |
920 void | |
921 callunix(void) | |
922 { | |
923 int c, pid; | |
924 Rune rune; | |
925 char buf[512]; | |
926 char *p; | |
927 | |
928 setnoaddr(); | |
929 p = buf; | |
930 while((c=getchr()) != EOF && c != '\n') | |
931 if(p < &buf[sizeof(buf) - 6]) { | |
932 rune = c; | |
933 p += runetochar(p, &rune); | |
934 } | |
935 *p = 0; | |
936 pid = fork(); | |
937 if(pid == 0) { | |
938 execlp("rc", "rc", "-c", buf, (char*)0); | |
939 sysfatal("exec failed: %r"); | |
940 exits("execl failed"); | |
941 } | |
942 waiting = 1; | |
943 while(waitpid() != pid) | |
944 ; | |
945 waiting = 0; | |
946 if(vflag) | |
947 putst("!"); | |
948 } | |
949 | |
950 void | |
951 quit(void) | |
952 { | |
953 if(vflag && fchange && dol!=zero) { | |
954 fchange = 0; | |
955 error(Q); | |
956 } | |
957 remove(tfname); | |
958 exits(0); | |
959 } | |
960 | |
961 void | |
962 onquit(int sig) | |
963 { | |
964 USED(sig); | |
965 quit(); | |
966 } | |
967 | |
968 void | |
969 rdelete(int *ad1, int *ad2) | |
970 { | |
971 int *a1, *a2, *a3; | |
972 | |
973 a1 = ad1; | |
974 a2 = ad2+1; | |
975 a3 = dol; | |
976 dol -= a2 - a1; | |
977 do { | |
978 *a1++ = *a2++; | |
979 } while(a2 <= a3); | |
980 a1 = ad1; | |
981 if(a1 > dol) | |
982 a1 = dol; | |
983 dot = a1; | |
984 fchange = 1; | |
985 } | |
986 | |
987 void | |
988 gdelete(void) | |
989 { | |
990 int *a1, *a2, *a3; | |
991 | |
992 a3 = dol; | |
993 for(a1=zero; (*a1&01)==0; a1++) | |
994 if(a1>=a3) | |
995 return; | |
996 for(a2=a1+1; a2<=a3;) { | |
997 if(*a2 & 01) { | |
998 a2++; | |
999 dot = a1; | |
1000 } else | |
1001 *a1++ = *a2++; | |
1002 } | |
1003 dol = a1-1; | |
1004 if(dot > dol) | |
1005 dot = dol; | |
1006 fchange = 1; | |
1007 } | |
1008 | |
1009 Rune* | |
1010 getline(int tl) | |
1011 { | |
1012 Rune *lp, *bp; | |
1013 int nl; | |
1014 | |
1015 lp = linebuf; | |
1016 bp = getblock(tl, OREAD); | |
1017 nl = nleft; | |
1018 tl &= ~((BLKSIZE/sizeof(Rune)) - 1); | |
1019 while(*lp++ = *bp++) { | |
1020 nl -= sizeof(Rune); | |
1021 if(nl == 0) { | |
1022 bp = getblock(tl += BLKSIZE/sizeof(Rune), OREAD); | |
1023 nl = nleft; | |
1024 } | |
1025 } | |
1026 return linebuf; | |
1027 } | |
1028 | |
1029 int | |
1030 putline(void) | |
1031 { | |
1032 Rune *lp, *bp; | |
1033 int nl, tl; | |
1034 | |
1035 fchange = 1; | |
1036 lp = linebuf; | |
1037 tl = tline; | |
1038 bp = getblock(tl, OWRITE); | |
1039 nl = nleft; | |
1040 tl &= ~((BLKSIZE/sizeof(Rune))-1); | |
1041 while(*bp = *lp++) { | |
1042 if(*bp++ == '\n') { | |
1043 bp[-1] = 0; | |
1044 linebp = lp; | |
1045 break; | |
1046 } | |
1047 nl -= sizeof(Rune); | |
1048 if(nl == 0) { | |
1049 tl += BLKSIZE/sizeof(Rune); | |
1050 bp = getblock(tl, OWRITE); | |
1051 nl = nleft; | |
1052 } | |
1053 } | |
1054 nl = tline; | |
1055 tline += ((lp-linebuf) + 03) & 077776; | |
1056 return nl; | |
1057 } | |
1058 | |
1059 void | |
1060 blkio(int b, uchar *buf, int isread) | |
1061 { | |
1062 int n; | |
1063 | |
1064 seek(tfile, b*BLKSIZE, 0); | |
1065 if(isread) | |
1066 n = read(tfile, buf, BLKSIZE); | |
1067 else | |
1068 n = write(tfile, buf, BLKSIZE); | |
1069 if(n != BLKSIZE) | |
1070 error(T); | |
1071 } | |
1072 | |
1073 Rune* | |
1074 getblock(int atl, int iof) | |
1075 { | |
1076 int bno, off; | |
1077 | |
1078 static uchar ibuff[BLKSIZE]; | |
1079 static uchar obuff[BLKSIZE]; | |
1080 | |
1081 bno = atl / (BLKSIZE/sizeof(Rune)); | |
1082 off = (atl*sizeof(Rune)) & (BLKSIZE-1) & ~03; | |
1083 if(bno >= NBLK) { | |
1084 lastc = '\n'; | |
1085 error(T); | |
1086 } | |
1087 nleft = BLKSIZE - off; | |
1088 if(bno == iblock) { | |
1089 ichanged |= iof; | |
1090 return (Rune*)(ibuff+off); | |
1091 } | |
1092 if(bno == oblock) | |
1093 return (Rune*)(obuff+off); | |
1094 if(iof == OREAD) { | |
1095 if(ichanged) | |
1096 blkio(iblock, ibuff, 0); | |
1097 ichanged = 0; | |
1098 iblock = bno; | |
1099 blkio(bno, ibuff, 1); | |
1100 return (Rune*)(ibuff+off); | |
1101 } | |
1102 if(oblock >= 0) | |
1103 blkio(oblock, obuff, 0); | |
1104 oblock = bno; | |
1105 return (Rune*)(obuff+off); | |
1106 } | |
1107 | |
1108 void | |
1109 init(void) | |
1110 { | |
1111 int *markp; | |
1112 | |
1113 close(tfile); | |
1114 tline = 2; | |
1115 for(markp = names; markp < &names[26]; ) | |
1116 *markp++ = 0; | |
1117 subnewa = 0; | |
1118 anymarks = 0; | |
1119 iblock = -1; | |
1120 oblock = -1; | |
1121 ichanged = 0; | |
1122 if((tfile = create(tfname, ORDWR, 0600)) < 0){ | |
1123 error1(T); | |
1124 exits(0); | |
1125 } | |
1126 dot = dol = zero; | |
1127 } | |
1128 | |
1129 void | |
1130 global(int k) | |
1131 { | |
1132 Rune *gp, globuf[GBSIZE]; | |
1133 int c, *a1; | |
1134 | |
1135 if(globp) | |
1136 error(Q); | |
1137 setwide(); | |
1138 squeeze(dol > zero); | |
1139 c = getchr(); | |
1140 if(c == '\n') | |
1141 error(Q); | |
1142 compile(c); | |
1143 gp = globuf; | |
1144 while((c=getchr()) != '\n') { | |
1145 if(c == EOF) | |
1146 error(Q); | |
1147 if(c == '\\') { | |
1148 c = getchr(); | |
1149 if(c != '\n') | |
1150 *gp++ = '\\'; | |
1151 } | |
1152 *gp++ = c; | |
1153 if(gp >= &globuf[GBSIZE-2]) | |
1154 error(Q); | |
1155 } | |
1156 if(gp == globuf) | |
1157 *gp++ = 'p'; | |
1158 *gp++ = '\n'; | |
1159 *gp = 0; | |
1160 for(a1=zero; a1<=dol; a1++) { | |
1161 *a1 &= ~01; | |
1162 if(a1 >= addr1 && a1 <= addr2 && match(a1) == k) | |
1163 *a1 |= 01; | |
1164 } | |
1165 | |
1166 /* | |
1167 * Special case: g/.../d (avoid n^2 algorithm) | |
1168 */ | |
1169 if(globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == 0) { | |
1170 gdelete(); | |
1171 return; | |
1172 } | |
1173 for(a1=zero; a1<=dol; a1++) { | |
1174 if(*a1 & 01) { | |
1175 *a1 &= ~01; | |
1176 dot = a1; | |
1177 globp = globuf; | |
1178 commands(); | |
1179 a1 = zero; | |
1180 } | |
1181 } | |
1182 } | |
1183 | |
1184 void | |
1185 join(void) | |
1186 { | |
1187 Rune *gp, *lp; | |
1188 int *a1; | |
1189 | |
1190 nonzero(); | |
1191 gp = genbuf; | |
1192 for(a1=addr1; a1<=addr2; a1++) { | |
1193 lp = getline(*a1); | |
1194 while(*gp = *lp++) | |
1195 if(gp++ >= &genbuf[LBSIZE-2]) | |
1196 error(Q); | |
1197 } | |
1198 lp = linebuf; | |
1199 gp = genbuf; | |
1200 while(*lp++ = *gp++) | |
1201 ; | |
1202 *addr1 = putline(); | |
1203 if(addr1 < addr2) | |
1204 rdelete(addr1+1, addr2); | |
1205 dot = addr1; | |
1206 } | |
1207 | |
1208 void | |
1209 substitute(int inglob) | |
1210 { | |
1211 int *mp, *a1, nl, gsubf, n; | |
1212 | |
1213 n = getnum(); /* OK even if n==0 */ | |
1214 gsubf = compsub(); | |
1215 for(a1 = addr1; a1 <= addr2; a1++) { | |
1216 if(match(a1)){ | |
1217 int *ozero; | |
1218 int m = n; | |
1219 | |
1220 do { | |
1221 int span = loc2-loc1; | |
1222 | |
1223 if(--m <= 0) { | |
1224 dosub(); | |
1225 if(!gsubf) | |
1226 break; | |
1227 if(span == 0) { /* null R… | |
1228 if(*loc2 == 0) | |
1229 break; | |
1230 loc2++; | |
1231 } | |
1232 } | |
1233 } while(match(0)); | |
1234 if(m <= 0) { | |
1235 inglob |= 01; | |
1236 subnewa = putline(); | |
1237 *a1 &= ~01; | |
1238 if(anymarks) { | |
1239 for(mp=names; mp<&names[26]; mp+… | |
1240 if(*mp == *a1) | |
1241 *mp = subnewa; | |
1242 } | |
1243 subolda = *a1; | |
1244 *a1 = subnewa; | |
1245 ozero = zero; | |
1246 nl = append(getsub, a1); | |
1247 addr2 += nl; | |
1248 nl += zero-ozero; | |
1249 a1 += nl; | |
1250 } | |
1251 } | |
1252 } | |
1253 if(inglob == 0) | |
1254 error(Q); | |
1255 } | |
1256 | |
1257 int | |
1258 compsub(void) | |
1259 { | |
1260 int seof, c; | |
1261 Rune *p; | |
1262 | |
1263 seof = getchr(); | |
1264 if(seof == '\n' || seof == ' ') | |
1265 error(Q); | |
1266 compile(seof); | |
1267 p = rhsbuf; | |
1268 for(;;) { | |
1269 c = getchr(); | |
1270 if(c == '\\') { | |
1271 c = getchr(); | |
1272 *p++ = ESCFLG; | |
1273 if(p >= &rhsbuf[LBSIZE/sizeof(Rune)]) | |
1274 error(Q); | |
1275 } else | |
1276 if(c == '\n' && (!globp || !globp[0])) { | |
1277 peekc = c; | |
1278 pflag++; | |
1279 break; | |
1280 } else | |
1281 if(c == seof) | |
1282 break; | |
1283 *p++ = c; | |
1284 if(p >= &rhsbuf[LBSIZE/sizeof(Rune)]) | |
1285 error(Q); | |
1286 } | |
1287 *p = 0; | |
1288 peekc = getchr(); | |
1289 if(peekc == 'g') { | |
1290 peekc = 0; | |
1291 newline(); | |
1292 return 1; | |
1293 } | |
1294 newline(); | |
1295 return 0; | |
1296 } | |
1297 | |
1298 int | |
1299 getsub(void) | |
1300 { | |
1301 Rune *p1, *p2; | |
1302 | |
1303 p1 = linebuf; | |
1304 if((p2 = linebp) == 0) | |
1305 return EOF; | |
1306 while(*p1++ = *p2++) | |
1307 ; | |
1308 linebp = 0; | |
1309 return 0; | |
1310 } | |
1311 | |
1312 void | |
1313 dosub(void) | |
1314 { | |
1315 Rune *lp, *sp, *rp; | |
1316 int c, n; | |
1317 | |
1318 lp = linebuf; | |
1319 sp = genbuf; | |
1320 rp = rhsbuf; | |
1321 while(lp < loc1) | |
1322 *sp++ = *lp++; | |
1323 while(c = *rp++) { | |
1324 if(c == '&'){ | |
1325 sp = place(sp, loc1, loc2); | |
1326 continue; | |
1327 } | |
1328 if(c == ESCFLG && (c = *rp++) >= '1' && c < MAXSUB+'0') { | |
1329 n = c-'0'; | |
1330 if(subexp[n].s.rsp && subexp[n].e.rep) { | |
1331 sp = place(sp, subexp[n].s.rsp, subexp[n… | |
1332 continue; | |
1333 } | |
1334 error(Q); | |
1335 } | |
1336 *sp++ = c; | |
1337 if(sp >= &genbuf[LBSIZE]) | |
1338 error(Q); | |
1339 } | |
1340 lp = loc2; | |
1341 loc2 = sp - genbuf + linebuf; | |
1342 while(*sp++ = *lp++) | |
1343 if(sp >= &genbuf[LBSIZE]) | |
1344 error(Q); | |
1345 lp = linebuf; | |
1346 sp = genbuf; | |
1347 while(*lp++ = *sp++) | |
1348 ; | |
1349 } | |
1350 | |
1351 Rune* | |
1352 place(Rune *sp, Rune *l1, Rune *l2) | |
1353 { | |
1354 | |
1355 while(l1 < l2) { | |
1356 *sp++ = *l1++; | |
1357 if(sp >= &genbuf[LBSIZE]) | |
1358 error(Q); | |
1359 } | |
1360 return sp; | |
1361 } | |
1362 | |
1363 void | |
1364 move(int cflag) | |
1365 { | |
1366 int *adt, *ad1, *ad2; | |
1367 | |
1368 nonzero(); | |
1369 if((adt = address())==0) /* address() guarantees addr is … | |
1370 error(Q); | |
1371 newline(); | |
1372 if(cflag) { | |
1373 int *ozero, delta; | |
1374 ad1 = dol; | |
1375 ozero = zero; | |
1376 append(getcopy, ad1++); | |
1377 ad2 = dol; | |
1378 delta = zero - ozero; | |
1379 ad1 += delta; | |
1380 adt += delta; | |
1381 } else { | |
1382 ad2 = addr2; | |
1383 for(ad1 = addr1; ad1 <= ad2;) | |
1384 *ad1++ &= ~01; | |
1385 ad1 = addr1; | |
1386 } | |
1387 ad2++; | |
1388 if(adt<ad1) { | |
1389 dot = adt + (ad2-ad1); | |
1390 if((++adt)==ad1) | |
1391 return; | |
1392 reverse(adt, ad1); | |
1393 reverse(ad1, ad2); | |
1394 reverse(adt, ad2); | |
1395 } else | |
1396 if(adt >= ad2) { | |
1397 dot = adt++; | |
1398 reverse(ad1, ad2); | |
1399 reverse(ad2, adt); | |
1400 reverse(ad1, adt); | |
1401 } else | |
1402 error(Q); | |
1403 fchange = 1; | |
1404 } | |
1405 | |
1406 void | |
1407 reverse(int *a1, int *a2) | |
1408 { | |
1409 int t; | |
1410 | |
1411 for(;;) { | |
1412 t = *--a2; | |
1413 if(a2 <= a1) | |
1414 return; | |
1415 *a2 = *a1; | |
1416 *a1++ = t; | |
1417 } | |
1418 } | |
1419 | |
1420 int | |
1421 getcopy(void) | |
1422 { | |
1423 if(addr1 > addr2) | |
1424 return EOF; | |
1425 getline(*addr1++); | |
1426 return 0; | |
1427 } | |
1428 | |
1429 void | |
1430 compile(int eof) | |
1431 { | |
1432 Rune c; | |
1433 char *ep; | |
1434 char expbuf[ESIZE]; | |
1435 | |
1436 if((c = getchr()) == '\n') { | |
1437 peekc = c; | |
1438 c = eof; | |
1439 } | |
1440 if(c == eof) { | |
1441 if(!pattern) | |
1442 error(Q); | |
1443 return; | |
1444 } | |
1445 if(pattern) { | |
1446 free(pattern); | |
1447 pattern = 0; | |
1448 } | |
1449 ep = expbuf; | |
1450 do { | |
1451 if(c == '\\') { | |
1452 if(ep >= expbuf+sizeof(expbuf)) { | |
1453 error(Q); | |
1454 return; | |
1455 } | |
1456 ep += runetochar(ep, &c); | |
1457 if((c = getchr()) == '\n') { | |
1458 error(Q); | |
1459 return; | |
1460 } | |
1461 } | |
1462 if(ep >= expbuf+sizeof(expbuf)) { | |
1463 error(Q); | |
1464 return; | |
1465 } | |
1466 ep += runetochar(ep, &c); | |
1467 } while((c = getchr()) != eof && c != '\n'); | |
1468 if(c == '\n') | |
1469 peekc = c; | |
1470 *ep = 0; | |
1471 pattern = regcomp(expbuf); | |
1472 } | |
1473 | |
1474 int | |
1475 match(int *addr) | |
1476 { | |
1477 if(!pattern) | |
1478 return 0; | |
1479 if(addr){ | |
1480 if(addr == zero) | |
1481 return 0; | |
1482 subexp[0].s.rsp = getline(*addr); | |
1483 } else | |
1484 subexp[0].s.rsp = loc2; | |
1485 subexp[0].e.rep = 0; | |
1486 if(rregexec(pattern, linebuf, subexp, MAXSUB)) { | |
1487 loc1 = subexp[0].s.rsp; | |
1488 loc2 = subexp[0].e.rep; | |
1489 return 1; | |
1490 } | |
1491 loc1 = loc2 = 0; | |
1492 return 0; | |
1493 | |
1494 } | |
1495 | |
1496 void | |
1497 putd(void) | |
1498 { | |
1499 int r; | |
1500 | |
1501 r = count%10; | |
1502 count /= 10; | |
1503 if(count) | |
1504 putd(); | |
1505 putchr(r + '0'); | |
1506 } | |
1507 | |
1508 void | |
1509 putst(char *sp) | |
1510 { | |
1511 Rune r; | |
1512 | |
1513 col = 0; | |
1514 for(;;) { | |
1515 sp += chartorune(&r, sp); | |
1516 if(r == 0) | |
1517 break; | |
1518 putchr(r); | |
1519 } | |
1520 putchr('\n'); | |
1521 } | |
1522 | |
1523 void | |
1524 putshst(Rune *sp) | |
1525 { | |
1526 col = 0; | |
1527 while(*sp) | |
1528 putchr(*sp++); | |
1529 putchr('\n'); | |
1530 } | |
1531 | |
1532 void | |
1533 putchr(int ac) | |
1534 { | |
1535 char *lp; | |
1536 int c; | |
1537 Rune rune; | |
1538 | |
1539 lp = linp; | |
1540 c = ac; | |
1541 if(listf) { | |
1542 if(c == '\n') { | |
1543 if(linp != line && linp[-1] == ' ') { | |
1544 *lp++ = '\\'; | |
1545 *lp++ = 'n'; | |
1546 } | |
1547 } else { | |
1548 if(col > (72-6-2)) { | |
1549 col = 8; | |
1550 *lp++ = '\\'; | |
1551 *lp++ = '\n'; | |
1552 *lp++ = '\t'; | |
1553 } | |
1554 col++; | |
1555 if(c=='\b' || c=='\t' || c=='\\') { | |
1556 *lp++ = '\\'; | |
1557 if(c == '\b') | |
1558 c = 'b'; | |
1559 else | |
1560 if(c == '\t') | |
1561 c = 't'; | |
1562 col++; | |
1563 } else | |
1564 if(c<' ' || c>='\177') { | |
1565 *lp++ = '\\'; | |
1566 *lp++ = 'x'; | |
1567 *lp++ = hex[c>>12]; | |
1568 *lp++ = hex[c>>8&0xF]; | |
1569 *lp++ = hex[c>>4&0xF]; | |
1570 c = hex[c&0xF]; | |
1571 col += 5; | |
1572 } | |
1573 } | |
1574 } | |
1575 | |
1576 rune = c; | |
1577 lp += runetochar(lp, &rune); | |
1578 | |
1579 if(c == '\n' || lp >= &line[sizeof(line)-5]) { | |
1580 linp = line; | |
1581 write(oflag? 2: 1, line, lp-line); | |
1582 return; | |
1583 } | |
1584 linp = lp; | |
1585 } | |
1586 | |
1587 char* | |
1588 __mktemp(char *as) | |
1589 { | |
1590 char *s; | |
1591 unsigned pid; | |
1592 int i; | |
1593 | |
1594 pid = getpid(); | |
1595 s = as; | |
1596 while(*s++) | |
1597 ; | |
1598 s--; | |
1599 while(*--s == 'X') { | |
1600 *s = pid % 10 + '0'; | |
1601 pid /= 10; | |
1602 } | |
1603 s++; | |
1604 i = 'a'; | |
1605 while(access(as, 0) != -1) { | |
1606 if(i == 'z') | |
1607 return "/"; | |
1608 *s = i++; | |
1609 } | |
1610 return as; | |
1611 } | |
1612 | |
1613 void | |
1614 regerror(char *s) | |
1615 { | |
1616 USED(s); | |
1617 error(Q); | |
1618 } |