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