]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ed.c
make all the commands agnostic about Rune width. (from sources)
[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  = Runemax,      /* 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/sizeof(Rune)];
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                         wrapp = 0;
376                         if(dol > zero)
377                                 putfile();
378                         exfile(OWRITE);
379                         if(addr1<=zero+1 && addr2==dol)
380                                 fchange = 0;
381                         if(temp == 'Q')
382                                 fchange = 0;
383                         if(temp)
384                                 quit();
385                         continue;
386
387                 case '=':
388                         setwide();
389                         squeeze(0);
390                         newline();
391                         count = addr2 - zero;
392                         putd();
393                         putchr(L'\n');
394                         continue;
395
396                 case '!':
397                         callunix();
398                         continue;
399
400                 case EOF:
401                         return;
402
403                 }
404                 error(Q);
405         }
406 }
407
408 void
409 printcom(void)
410 {
411         int *a1;
412
413         nonzero();
414         a1 = addr1;
415         do {
416                 if(listn) {
417                         count = a1-zero;
418                         putd();
419                         putchr(L'\t');
420                 }
421                 putshst(getline(*a1++));
422         } while(a1 <= addr2);
423         dot = addr2;
424         listf = 0;
425         listn = 0;
426         pflag = 0;
427 }
428
429 int*
430 address(void)
431 {
432         int sign, *a, opcnt, nextopand, *b, c;
433
434         nextopand = -1;
435         sign = 1;
436         opcnt = 0;
437         a = dot;
438         do {
439                 do {
440                         c = getchr();
441                 } while(c == ' ' || c == '\t');
442                 if(c >= '0' && c <= '9') {
443                         peekc = c;
444                         if(!opcnt)
445                                 a = zero;
446                         a += sign*getnum();
447                 } else
448                 switch(c) {
449                 case '$':
450                         a = dol;
451                 case '.':
452                         if(opcnt)
453                                 error(Q);
454                         break;
455                 case '\'':
456                         c = getchr();
457                         if(opcnt || c < 'a' || c > 'z')
458                                 error(Q);
459                         a = zero;
460                         do {
461                                 a++;
462                         } while(a <= dol && names[c-'a'] != (*a & ~01));
463                         break;
464                 case '?':
465                         sign = -sign;
466                 case '/':
467                         compile(c);
468                         b = a;
469                         for(;;) {
470                                 a += sign;
471                                 if(a <= zero)
472                                         a = dol;
473                                 if(a > dol)
474                                         a = zero;
475                                 if(match(a))
476                                         break;
477                                 if(a == b)
478                                         error(Q);
479                         }
480                         break;
481                 default:
482                         if(nextopand == opcnt) {
483                                 a += sign;
484                                 if(a < zero || dol < a)
485                                         continue;       /* error(Q); */
486                         }
487                         if(c != '+' && c != '-' && c != '^') {
488                                 peekc = c;
489                                 if(opcnt == 0)
490                                         a = 0;
491                                 return a;
492                         }
493                         sign = 1;
494                         if(c != '+')
495                                 sign = -sign;
496                         nextopand = ++opcnt;
497                         continue;
498                 }
499                 sign = 1;
500                 opcnt++;
501         } while(zero <= a && a <= dol);
502         error(Q);
503         return 0;
504 }
505
506 int
507 getnum(void)
508 {
509         int r, c;
510
511         r = 0;
512         for(;;) {
513                 c = getchr();
514                 if(c < '0' || c > '9')
515                         break;
516                 r = r*10 + (c-'0');
517         }
518         peekc = c;
519         return r;
520 }
521
522 void
523 setwide(void)
524 {
525         if(!given) {
526                 addr1 = zero + (dol>zero);
527                 addr2 = dol;
528         }
529 }
530
531 void
532 setnoaddr(void)
533 {
534         if(given)
535                 error(Q);
536 }
537
538 void
539 nonzero(void)
540 {
541         squeeze(1);
542 }
543
544 void
545 squeeze(int i)
546 {
547         if(addr1 < zero+i || addr2 > dol || addr1 > addr2)
548                 error(Q);
549 }
550
551 void
552 newline(void)
553 {
554         int c;
555
556         c = getchr();
557         if(c == '\n' || c == EOF)
558                 return;
559         if(c == 'p' || c == 'l' || c == 'n') {
560                 pflag++;
561                 if(c == 'l')
562                         listf++;
563                 else
564                 if(c == 'n')
565                         listn++;
566                 c = getchr();
567                 if(c == '\n')
568                         return;
569         }
570         error(Q);
571 }
572
573 void
574 filename(int comm)
575 {
576         char *p1, *p2;
577         Rune rune;
578         int c;
579
580         count = 0;
581         c = getchr();
582         if(c == '\n' || c == EOF) {
583                 p1 = savedfile;
584                 if(*p1 == 0 && comm != 'f')
585                         error(Q);
586                 p2 = file;
587                 while(*p2++ = *p1++)
588                         ;
589                 return;
590         }
591         if(c != ' ')
592                 error(Q);
593         while((c=getchr()) == ' ')
594                 ;
595         if(c == '\n')
596                 error(Q);
597         p1 = file;
598         do {
599                 if(p1 >= &file[sizeof(file)-6] || c == ' ' || c == EOF)
600                         error(Q);
601                 rune = c;
602                 p1 += runetochar(p1, &rune);
603         } while((c=getchr()) != '\n');
604         *p1 = 0;
605         if(savedfile[0] == 0 || comm == 'e' || comm == 'f') {
606                 p1 = savedfile;
607                 p2 = file;
608                 while(*p1++ = *p2++)
609                         ;
610         }
611 }
612
613 void
614 exfile(int om)
615 {
616
617         if(om == OWRITE)
618                 if(Bflush(&iobuf) < 0)
619                         error(Q);
620         close(io);
621         io = -1;
622         if(vflag) {
623                 putd();
624                 putchr(L'\n');
625         }
626 }
627
628 void
629 error1(char *s)
630 {
631         int c;
632
633         wrapp = 0;
634         listf = 0;
635         listn = 0;
636         count = 0;
637         seek(0, 0, 2);
638         pflag = 0;
639         if(globp)
640                 lastc = '\n';
641         globp = 0;
642         peekc = lastc;
643         if(lastc)
644                 for(;;) {
645                         c = getchr();
646                         if(c == '\n' || c == EOF)
647                                 break;
648                 }
649         if(io > 0) {
650                 close(io);
651                 io = -1;
652         }
653         putchr(L'?');
654         putst(s);
655 }
656
657 void
658 error(char *s)
659 {
660         error1(s);
661         longjmp(savej, 1);
662 }
663
664 void
665 rescue(void)
666 {
667         rescuing = 1;
668         if(dol > zero) {
669                 addr1 = zero+1;
670                 addr2 = dol;
671                 io = create("ed.hup", OWRITE, 0666);
672                 if(io > 0){
673                         Binit(&iobuf, io, OWRITE);
674                         putfile();
675                 }
676         }
677         fchange = 0;
678         quit();
679 }
680
681 void
682 notifyf(void *a, char *s)
683 {
684         if(strcmp(s, "interrupt") == 0){
685                 if(rescuing || waiting)
686                         noted(NCONT);
687                 putchr(L'\n');
688                 lastc = '\n';
689                 error1(Q);
690                 notejmp(a, savej, 0);
691         }
692         if(strcmp(s, "hangup") == 0){
693                 if(rescuing)
694                         noted(NDFLT);
695                 rescue();
696         }
697         fprint(2, "ed: note: %s\n", s);
698         abort();
699 }
700
701 int
702 getchr(void)
703 {
704         if(lastc = peekc) {
705                 peekc = 0;
706                 return lastc;
707         }
708         if(globp) {
709                 if((lastc=*globp++) != 0)
710                         return lastc;
711                 globp = 0;
712                 return EOF;
713         }
714         lastc = Bgetrune(&bcons);
715         return lastc;
716 }
717
718 int
719 gety(void)
720 {
721         int c;
722         Rune *gf, *p;
723
724         p = linebuf;
725         gf = globp;
726         for(;;) {
727                 c = getchr();
728                 if(c == '\n') {
729                         *p = 0;
730                         return 0;
731                 }
732                 if(c == EOF) {
733                         if(gf)
734                                 peekc = c;
735                         return c;
736                 }
737                 if(c == 0)
738                         continue;
739                 *p++ = c;
740                 if(p >= &linebuf[LBSIZE-sizeof(Rune)])
741                         error(Q);
742         }
743 }
744
745 int
746 gettty(void)
747 {
748         int rc;
749
750         rc = gety();
751         if(rc)
752                 return rc;
753         if(linebuf[0] == '.' && linebuf[1] == 0)
754                 return EOF;
755         return 0;
756 }
757
758 int
759 getfile(void)
760 {
761         int c;
762         Rune *lp;
763
764         lp = linebuf;
765         do {
766                 c = Bgetrune(&iobuf);
767                 if(c < 0) {
768                         if(lp > linebuf) {
769                                 putst("'\\n' appended");
770                                 c = '\n';
771                         } else
772                                 return EOF;
773                 }
774                 if(lp >= &linebuf[LBSIZE]) {
775                         lastc = '\n';
776                         error(Q);
777                 }
778                 *lp++ = c;
779                 count++;
780         } while(c != '\n');
781         lp[-1] = 0;
782         return 0;
783 }
784
785 void
786 putfile(void)
787 {
788         int *a1;
789         Rune *lp;
790         long c;
791
792         a1 = addr1;
793         do {
794                 lp = getline(*a1++);
795                 for(;;) {
796                         count++;
797                         c = *lp++;
798                         if(c == 0) {
799                                 if(Bputrune(&iobuf, '\n') < 0)
800                                         error(Q);
801                                 break;
802                         }
803                         if(Bputrune(&iobuf, c) < 0)
804                                 error(Q);
805                 }
806         } while(a1 <= addr2);
807         if(Bflush(&iobuf) < 0)
808                 error(Q);
809 }
810
811 int
812 append(int (*f)(void), int *a)
813 {
814         int *a1, *a2, *rdot, nline, tl;
815
816         nline = 0;
817         dot = a;
818         while((*f)() == 0) {
819                 if((dol-zero) >= nlall) {
820                         nlall += 512;
821                         a1 = realloc(zero, (nlall+5)*sizeof(int*));
822                         if(a1 == 0) {
823                                 error("MEM?");
824                                 rescue();
825                         }
826                         tl = a1 - zero; /* relocate pointers */
827                         zero += tl;
828                         addr1 += tl;
829                         addr2 += tl;
830                         dol += tl;
831                         dot += tl;
832                 }
833                 tl = putline();
834                 nline++;
835                 a1 = ++dol;
836                 a2 = a1+1;
837                 rdot = ++dot;
838                 while(a1 > rdot)
839                         *--a2 = *--a1;
840                 *rdot = tl;
841         }
842         return nline;
843 }
844
845 void
846 add(int i)
847 {
848         if(i && (given || dol > zero)) {
849                 addr1--;
850                 addr2--;
851         }
852         squeeze(0);
853         newline();
854         append(gettty, addr2);
855 }
856
857 void
858 browse(void)
859 {
860         int forward, n;
861         static int bformat, bnum; /* 0 */
862
863         forward = 1;
864         peekc = getchr();
865         if(peekc != '\n'){
866                 if(peekc == '-' || peekc == '+') {
867                         if(peekc == '-')
868                                 forward = 0;
869                         getchr();
870                 }
871                 n = getnum();
872                 if(n > 0)
873                         bpagesize = n;
874         }
875         newline();
876         if(pflag) {
877                 bformat = listf;
878                 bnum = listn;
879         } else {
880                 listf = bformat;
881                 listn = bnum;
882         }
883         if(forward) {
884                 addr1 = addr2;
885                 addr2 += bpagesize;
886                 if(addr2 > dol)
887                         addr2 = dol;
888         } else {
889                 addr1 = addr2-bpagesize;
890                 if(addr1 <= zero)
891                         addr1 = zero+1;
892         }
893         printcom();
894 }
895
896 void
897 callunix(void)
898 {
899         int c, pid;
900         Rune rune;
901         char buf[512];
902         char *p;
903
904         setnoaddr();
905         p = buf;
906         while((c=getchr()) != EOF && c != '\n')
907                 if(p < &buf[sizeof(buf) - 6]) {
908                         rune = c;
909                         p += runetochar(p, &rune);
910                 }
911         *p = 0;
912         pid = fork();
913         if(pid == 0) {
914                 execl("/bin/rc", "rc", "-c", buf, nil);
915                 exits("execl failed");
916         }
917         waiting = 1;
918         while(waitpid() != pid)
919                 ;
920         waiting = 0;
921         if(vflag)
922                 putst("!");
923 }
924
925 void
926 quit(void)
927 {
928         if(vflag && fchange && dol!=zero) {
929                 fchange = 0;
930                 error(Q);
931         }
932         remove(tfname);
933         exits(0);
934 }
935
936 void
937 onquit(int sig)
938 {
939         USED(sig);
940         quit();
941 }
942
943 void
944 rdelete(int *ad1, int *ad2)
945 {
946         int *a1, *a2, *a3;
947
948         a1 = ad1;
949         a2 = ad2+1;
950         a3 = dol;
951         dol -= a2 - a1;
952         do {
953                 *a1++ = *a2++;
954         } while(a2 <= a3);
955         a1 = ad1;
956         if(a1 > dol)
957                 a1 = dol;
958         dot = a1;
959         fchange = 1;
960 }
961
962 void
963 gdelete(void)
964 {
965         int *a1, *a2, *a3;
966
967         a3 = dol;
968         for(a1=zero; (*a1&01)==0; a1++)
969                 if(a1>=a3)
970                         return;
971         for(a2=a1+1; a2<=a3;) {
972                 if(*a2 & 01) {
973                         a2++;
974                         dot = a1;
975                 } else
976                         *a1++ = *a2++;
977         }
978         dol = a1-1;
979         if(dot > dol)
980                 dot = dol;
981         fchange = 1;
982 }
983
984 Rune*
985 getline(int tl)
986 {
987         Rune *lp, *bp;
988         int nl;
989
990         lp = linebuf;
991         bp = getblock(tl, OREAD);
992         nl = nleft;
993         tl &= ~((BLKSIZE/sizeof(Rune)) - 1);
994         while(*lp++ = *bp++) {
995                 nl -= sizeof(Rune);
996                 if(nl == 0) {
997                         bp = getblock(tl += BLKSIZE/sizeof(Rune), OREAD);
998                         nl = nleft;
999                 }
1000         }
1001         return linebuf;
1002 }
1003
1004 int
1005 putline(void)
1006 {
1007         Rune *lp, *bp;
1008         int nl, tl;
1009
1010         fchange = 1;
1011         lp = linebuf;
1012         tl = tline;
1013         bp = getblock(tl, OWRITE);
1014         nl = nleft;
1015         tl &= ~((BLKSIZE/sizeof(Rune))-1);
1016         while(*bp = *lp++) {
1017                 if(*bp++ == '\n') {
1018                         bp[-1] = 0;
1019                         linebp = lp;
1020                         break;
1021                 }
1022                 nl -= sizeof(Rune);
1023                 if(nl == 0) {
1024                         tl += BLKSIZE/sizeof(Rune);
1025                         bp = getblock(tl, OWRITE);
1026                         nl = nleft;
1027                 }
1028         }
1029         nl = tline;
1030         tline += ((lp-linebuf) + 03) & 077776;
1031         return nl;
1032 }
1033
1034 void
1035 blkio(int b, uchar *buf, long (*iofcn)(int, void *, long))
1036 {
1037         seek(tfile, b*BLKSIZE, 0);
1038         if((*iofcn)(tfile, buf, BLKSIZE) != BLKSIZE) {
1039                 error(T);
1040         }
1041 }
1042
1043 Rune*
1044 getblock(int atl, int iof)
1045 {
1046         int bno, off;
1047         
1048         static uchar ibuff[BLKSIZE];
1049         static uchar obuff[BLKSIZE];
1050
1051         bno = atl / (BLKSIZE/sizeof(Rune));
1052         off = (atl*sizeof(Rune)) & (BLKSIZE-1) & ~03;
1053         if(bno >= NBLK) {
1054                 lastc = '\n';
1055                 error(T);
1056         }
1057         nleft = BLKSIZE - off;
1058         if(bno == iblock) {
1059                 ichanged |= iof;
1060                 return (Rune*)(ibuff+off);
1061         }
1062         if(bno == oblock)
1063                 return (Rune*)(obuff+off);
1064         if(iof == OREAD) {
1065                 if(ichanged)
1066                         blkio(iblock, ibuff, write);
1067                 ichanged = 0;
1068                 iblock = bno;
1069                 blkio(bno, ibuff, read);
1070                 return (Rune*)(ibuff+off);
1071         }
1072         if(oblock >= 0)
1073                 blkio(oblock, obuff, write);
1074         oblock = bno;
1075         return (Rune*)(obuff+off);
1076 }
1077
1078 void
1079 init(void)
1080 {
1081         int *markp;
1082
1083         close(tfile);
1084         tline = 2;
1085         for(markp = names; markp < &names[26]; )
1086                 *markp++ = 0;
1087         subnewa = 0;
1088         anymarks = 0;
1089         iblock = -1;
1090         oblock = -1;
1091         ichanged = 0;
1092         if((tfile = create(tfname, ORDWR, 0600)) < 0){
1093                 error1(T);
1094                 exits(0);
1095         }
1096         dot = dol = zero;
1097 }
1098
1099 void
1100 global(int k)
1101 {
1102         Rune *gp, globuf[GBSIZE];
1103         int c, *a1;
1104
1105         if(globp)
1106                 error(Q);
1107         setwide();
1108         squeeze(dol > zero);
1109         c = getchr();
1110         if(c == '\n')
1111                 error(Q);
1112         compile(c);
1113         gp = globuf;
1114         while((c=getchr()) != '\n') {
1115                 if(c == EOF)
1116                         error(Q);
1117                 if(c == '\\') {
1118                         c = getchr();
1119                         if(c != '\n')
1120                                 *gp++ = '\\';
1121                 }
1122                 *gp++ = c;
1123                 if(gp >= &globuf[GBSIZE-2])
1124                         error(Q);
1125         }
1126         if(gp == globuf)
1127                 *gp++ = 'p';
1128         *gp++ = '\n';
1129         *gp = 0;
1130         for(a1=zero; a1<=dol; a1++) {
1131                 *a1 &= ~01;
1132                 if(a1 >= addr1 && a1 <= addr2 && match(a1) == k)
1133                         *a1 |= 01;
1134         }
1135
1136         /*
1137          * Special case: g/.../d (avoid n^2 algorithm)
1138          */
1139         if(globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == 0) {
1140                 gdelete();
1141                 return;
1142         }
1143         for(a1=zero; a1<=dol; a1++) {
1144                 if(*a1 & 01) {
1145                         *a1 &= ~01;
1146                         dot = a1;
1147                         globp = globuf;
1148                         commands();
1149                         a1 = zero;
1150                 }
1151         }
1152 }
1153
1154 void
1155 join(void)
1156 {
1157         Rune *gp, *lp;
1158         int *a1;
1159
1160         nonzero();
1161         gp = genbuf;
1162         for(a1=addr1; a1<=addr2; a1++) {
1163                 lp = getline(*a1);
1164                 while(*gp = *lp++)
1165                         if(gp++ >= &genbuf[LBSIZE-sizeof(Rune)])
1166                                 error(Q);
1167         }
1168         lp = linebuf;
1169         gp = genbuf;
1170         while(*lp++ = *gp++)
1171                 ;
1172         *addr1 = putline();
1173         if(addr1 < addr2)
1174                 rdelete(addr1+1, addr2);
1175         dot = addr1;
1176 }
1177
1178 void
1179 substitute(int inglob)
1180 {
1181         int *mp, *a1, nl, gsubf, n;
1182
1183         n = getnum();   /* OK even if n==0 */
1184         gsubf = compsub();
1185         for(a1 = addr1; a1 <= addr2; a1++) {
1186                 if(match(a1)){
1187                         int *ozero;
1188                         int m = n;
1189
1190                         do {
1191                                 int span = loc2-loc1;
1192
1193                                 if(--m <= 0) {
1194                                         dosub();
1195                                         if(!gsubf)
1196                                                 break;
1197                                         if(span == 0) { /* null RE match */
1198                                                 if(*loc2 == 0)
1199                                                         break;
1200                                                 loc2++;
1201                                         }
1202                                 }
1203                         } while(match(0));
1204                         if(m <= 0) {
1205                                 inglob |= 01;
1206                                 subnewa = putline();
1207                                 *a1 &= ~01;
1208                                 if(anymarks) {
1209                                         for(mp=names; mp<&names[26]; mp++)
1210                                                 if(*mp == *a1)
1211                                                         *mp = subnewa;
1212                                 }
1213                                 subolda = *a1;
1214                                 *a1 = subnewa;
1215                                 ozero = zero;
1216                                 nl = append(getsub, a1);
1217                                 addr2 += nl;
1218                                 nl += zero-ozero;
1219                                 a1 += nl;
1220                         }
1221                 }
1222         }
1223         if(inglob == 0)
1224                 error(Q);
1225 }
1226
1227 int
1228 compsub(void)
1229 {
1230         int seof, c;
1231         Rune *p;
1232
1233         seof = getchr();
1234         if(seof == '\n' || seof == ' ')
1235                 error(Q);
1236         compile(seof);
1237         p = rhsbuf;
1238         for(;;) {
1239                 c = getchr();
1240                 if(c == '\\') {
1241                         c = getchr();
1242                         *p++ = ESCFLG;
1243                         if(p >= &rhsbuf[nelem(rhsbuf)])
1244                                 error(Q);
1245                 } else
1246                 if(c == '\n' && (!globp || !globp[0])) {
1247                         peekc = c;
1248                         pflag++;
1249                         break;
1250                 } else
1251                 if(c == seof)
1252                         break;
1253                 *p++ = c;
1254                 if(p >= &rhsbuf[nelem(rhsbuf)])
1255                         error(Q);
1256         }
1257         *p = 0;
1258         peekc = getchr();
1259         if(peekc == 'g') {
1260                 peekc = 0;
1261                 newline();
1262                 return 1;
1263         }
1264         newline();
1265         return 0;
1266 }
1267
1268 int
1269 getsub(void)
1270 {
1271         Rune *p1, *p2;
1272
1273         p1 = linebuf;
1274         if((p2 = linebp) == 0)
1275                 return EOF;
1276         while(*p1++ = *p2++)
1277                 ;
1278         linebp = 0;
1279         return 0;
1280 }
1281
1282 void
1283 dosub(void)
1284 {
1285         Rune *lp, *sp, *rp;
1286         int c, n;
1287
1288         lp = linebuf;
1289         sp = genbuf;
1290         rp = rhsbuf;
1291         while(lp < loc1)
1292                 *sp++ = *lp++;
1293         while(c = *rp++) {
1294                 if(c == '&'){
1295                         sp = place(sp, loc1, loc2);
1296                         continue;
1297                 }
1298                 if(c == ESCFLG && (c = *rp++) >= '1' && c < MAXSUB+'0') {
1299                         n = c-'0';
1300                         if(subexp[n].rsp && subexp[n].rep) {
1301                                 sp = place(sp, subexp[n].rsp, subexp[n].rep);
1302                                 continue;
1303                         }
1304                         error(Q);
1305                 }
1306                 *sp++ = c;
1307                 if(sp >= &genbuf[LBSIZE])
1308                         error(Q);
1309         }
1310         lp = loc2;
1311         loc2 = sp - genbuf + linebuf;
1312         while(*sp++ = *lp++)
1313                 if(sp >= &genbuf[LBSIZE])
1314                         error(Q);
1315         lp = linebuf;
1316         sp = genbuf;
1317         while(*lp++ = *sp++)
1318                 ;
1319 }
1320
1321 Rune*
1322 place(Rune *sp, Rune *l1, Rune *l2)
1323 {
1324
1325         while(l1 < l2) {
1326                 *sp++ = *l1++;
1327                 if(sp >= &genbuf[LBSIZE])
1328                         error(Q);
1329         }
1330         return sp;
1331 }
1332
1333 void
1334 move(int cflag)
1335 {
1336         int *adt, *ad1, *ad2;
1337
1338         nonzero();
1339         if((adt = address())==0)        /* address() guarantees addr is in range */
1340                 error(Q);
1341         newline();
1342         if(cflag) {
1343                 int *ozero, delta;
1344                 ad1 = dol;
1345                 ozero = zero;
1346                 append(getcopy, ad1++);
1347                 ad2 = dol;
1348                 delta = zero - ozero;
1349                 ad1 += delta;
1350                 adt += delta;
1351         } else {
1352                 ad2 = addr2;
1353                 for(ad1 = addr1; ad1 <= ad2;)
1354                         *ad1++ &= ~01;
1355                 ad1 = addr1;
1356         }
1357         ad2++;
1358         if(adt<ad1) {
1359                 dot = adt + (ad2-ad1);
1360                 if((++adt)==ad1)
1361                         return;
1362                 reverse(adt, ad1);
1363                 reverse(ad1, ad2);
1364                 reverse(adt, ad2);
1365         } else
1366         if(adt >= ad2) {
1367                 dot = adt++;
1368                 reverse(ad1, ad2);
1369                 reverse(ad2, adt);
1370                 reverse(ad1, adt);
1371         } else
1372                 error(Q);
1373         fchange = 1;
1374 }
1375
1376 void
1377 reverse(int *a1, int *a2)
1378 {
1379         int t;
1380
1381         for(;;) {
1382                 t = *--a2;
1383                 if(a2 <= a1)
1384                         return;
1385                 *a2 = *a1;
1386                 *a1++ = t;
1387         }
1388 }
1389
1390 int
1391 getcopy(void)
1392 {
1393         if(addr1 > addr2)
1394                 return EOF;
1395         getline(*addr1++);
1396         return 0;
1397 }
1398
1399 void
1400 compile(int eof)
1401 {
1402         Rune c;
1403         char *ep;
1404         char expbuf[ESIZE];
1405
1406         if((c = getchr()) == '\n') {
1407                 peekc = c;
1408                 c = eof;
1409         }
1410         if(c == eof) {
1411                 if(!pattern)
1412                         error(Q);
1413                 return;
1414         }
1415         if(pattern) {
1416                 free(pattern);
1417                 pattern = 0;
1418         }
1419         ep = expbuf;
1420         do {
1421                 if(c == '\\') {
1422                         if(ep >= expbuf+sizeof(expbuf)) {
1423                                 error(Q);
1424                                 return;
1425                         }
1426                         ep += runetochar(ep, &c);
1427                         if((c = getchr()) == '\n') {
1428                                 error(Q);
1429                                 return;
1430                         }
1431                 }
1432                 if(ep >= expbuf+sizeof(expbuf)) {
1433                         error(Q);
1434                         return;
1435                 }
1436                 ep += runetochar(ep, &c);
1437         } while((c = getchr()) != eof && c != '\n');
1438         if(c == '\n')
1439                 peekc = c;
1440         *ep = 0;
1441         pattern = regcomp(expbuf);
1442 }
1443
1444 int
1445 match(int *addr)
1446 {
1447         if(!pattern)
1448                 return 0;
1449         if(addr){
1450                 if(addr == zero)
1451                         return 0;
1452                 subexp[0].rsp = getline(*addr);
1453         } else
1454                 subexp[0].rsp = loc2;
1455         subexp[0].rep = 0;
1456         if(rregexec(pattern, linebuf, subexp, MAXSUB)) {
1457                 loc1 = subexp[0].rsp;
1458                 loc2 = subexp[0].rep;
1459                 return 1;
1460         }
1461         loc1 = loc2 = 0;
1462         return 0;
1463         
1464 }
1465
1466 void
1467 putd(void)
1468 {
1469         int r;
1470
1471         r = count%10;
1472         count /= 10;
1473         if(count)
1474                 putd();
1475         putchr(r + L'0');
1476 }
1477
1478 void
1479 putst(char *sp)
1480 {
1481         Rune r;
1482
1483         col = 0;
1484         for(;;) {
1485                 sp += chartorune(&r, sp);
1486                 if(r == 0)
1487                         break;
1488                 putchr(r);
1489         }
1490         putchr(L'\n');
1491 }
1492
1493 void
1494 putshst(Rune *sp)
1495 {
1496         col = 0;
1497         while(*sp)
1498                 putchr(*sp++);
1499         putchr(L'\n');
1500 }
1501
1502 void
1503 putchr(int ac)
1504 {
1505         char *lp;
1506         int c;
1507         Rune rune;
1508
1509         lp = linp;
1510         c = ac;
1511         if(listf) {
1512                 if(c == '\n') {
1513                         if(linp != line && linp[-1] == ' ') {
1514                                 *lp++ = '\\';
1515                                 *lp++ = 'n';
1516                         }
1517                 } else {
1518                         if(col > (72-6-2)) {
1519                                 col = 8;
1520                                 *lp++ = '\\';
1521                                 *lp++ = '\n';
1522                                 *lp++ = '\t';
1523                         }
1524                         col++;
1525                         if(c=='\b' || c=='\t' || c=='\\') {
1526                                 *lp++ = '\\';
1527                                 if(c == '\b')
1528                                         c = 'b';
1529                                 else
1530                                 if(c == '\t')
1531                                         c = 't';
1532                                 col++;
1533                         } else
1534                         if(c<' ' || c>='\177') {
1535                                 *lp++ = '\\';
1536                                 *lp++ = 'x';
1537                                 *lp++ =  hex[c>>12];
1538                                 *lp++ =  hex[c>>8&0xF];
1539                                 *lp++ =  hex[c>>4&0xF];
1540                                 c     =  hex[c&0xF];
1541                                 col += 5;
1542                         }
1543                 }
1544         }
1545
1546         rune = c;
1547         lp += runetochar(lp, &rune);
1548
1549         if(c == '\n' || lp >= &line[sizeof(line)-5]) {
1550                 linp = line;
1551                 write(oflag? 2: 1, line, lp-line);
1552                 return;
1553         }
1554         linp = lp;
1555 }
1556
1557 char*
1558 mktemp(char *as)
1559 {
1560         char *s;
1561         unsigned pid;
1562         int i;
1563
1564         pid = getpid();
1565         s = as;
1566         while(*s++)
1567                 ;
1568         s--;
1569         while(*--s == 'X') {
1570                 *s = pid % 10 + '0';
1571                 pid /= 10;
1572         }
1573         s++;
1574         i = 'a';
1575         while(access(as, 0) != -1) {
1576                 if(i == 'z')
1577                         return "/";
1578                 *s = i++;
1579         }
1580         return as;
1581 }
1582
1583 void
1584 regerror(char *s)
1585 {
1586         USED(s);
1587         error(Q);
1588 }