Introduction
Introduction Statistics Contact Development Disclaimer Help
ed.c - sbase - suckless unix tools
git clone git://git.suckless.org/sbase
Log
Files
Refs
README
LICENSE
---
ed.c (25385B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 #include <regex.h>
5 #include <unistd.h>
6
7 #include <ctype.h>
8 #include <limits.h>
9 #include <setjmp.h>
10 #include <signal.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "util.h"
17
18 #define REGEXSIZE 100
19 #define LINESIZE 80
20 #define NUMLINES 32
21 #define CACHESIZ 4096
22 #define AFTER 0
23 #define BEFORE 1
24
25 typedef struct {
26 char *str;
27 size_t cap;
28 size_t siz;
29 } String;
30
31 struct hline {
32 off_t seek;
33 char global;
34 int next, prev;
35 };
36
37 struct undo {
38 int curln, lastln;
39 size_t nr, cap;
40 struct link {
41 int to1, from1;
42 int to2, from2;
43 } *vec;
44 };
45
46 static char *prompt = "*";
47 static regex_t *pattern;
48 static regmatch_t matchs[10];
49 static String lastre;
50
51 static int optverbose, optprompt, exstatus, optdiag = 1;
52 static int marks['z' - 'a'];
53 static int nlines, line1, line2;
54 static int curln, lastln, ocurln, olastln;
55 static jmp_buf savesp;
56 static char *lasterr;
57 static size_t idxsize, lastidx;
58 static struct hline *zero;
59 static String text;
60 static char savfname[FILENAME_MAX];
61 static char tmpname[FILENAME_MAX];
62 static int scratch;
63 static int pflag, modflag, uflag, gflag;
64 static size_t csize;
65 static String cmdline;
66 static char *ocmdline;
67 static int inputidx;
68 static char *rhs;
69 static char *lastmatch;
70 static struct undo udata;
71 static int newcmd;
72 static int eol, bol;
73
74 static sig_atomic_t intr, hup;
75
76 static void undo(void);
77
78 static void
79 error(char *msg)
80 {
81 exstatus = 1;
82 lasterr = msg;
83 puts("?");
84
85 if (optverbose)
86 puts(msg);
87 if (!newcmd)
88 undo();
89
90 curln = ocurln;
91 longjmp(savesp, 1);
92 }
93
94 static int
95 nextln(int line)
96 {
97 ++line;
98 return (line > lastln) ? 0 : line;
99 }
100
101 static int
102 prevln(int line)
103 {
104 --line;
105 return (line < 0) ? lastln : line;
106 }
107
108 static String *
109 copystring(String *s, char *from)
110 {
111 size_t len;
112 char *t;
113
114 if ((t = strdup(from)) == NULL)
115 error("out of memory");
116 len = strlen(t);
117
118 free(s->str);
119 s->str = t;
120 s->siz = len;
121 s->cap = len;
122
123 return s;
124 }
125
126 static String *
127 string(String *s)
128 {
129 free(s->str);
130 s->str = NULL;
131 s->siz = 0;
132 s->cap = 0;
133
134 return s;
135 }
136
137 static char *
138 addchar(char c, String *s)
139 {
140 size_t cap = s->cap, siz = s->siz;
141 char *t = s->str;
142
143 if (siz >= cap &&
144 (cap > SIZE_MAX - LINESIZE ||
145 (t = realloc(t, cap += LINESIZE)) == NULL))
146 error("out of memory");
147 t[siz++] = c;
148 s->siz = siz;
149 s->cap = cap;
150 s->str = t;
151 return t;
152 }
153
154 static void chksignals(void);
155
156 static int
157 input(void)
158 {
159 int ch;
160
161 chksignals();
162
163 ch = cmdline.str[inputidx];
164 if (ch != '\0')
165 inputidx++;
166 return ch;
167 }
168
169 static int
170 back(int c)
171 {
172 if (c == '\0')
173 return c;
174 return cmdline.str[--inputidx] = c;
175 }
176
177 static int
178 makeline(char *s, int *off)
179 {
180 struct hline *lp;
181 size_t len;
182 char *begin = s;
183 int c;
184
185 if (lastidx >= idxsize) {
186 lp = NULL;
187 if (idxsize <= SIZE_MAX - NUMLINES)
188 lp = reallocarray(zero, idxsize + NUMLINES, size…
189 if (!lp)
190 error("out of memory");
191 idxsize += NUMLINES;
192 zero = lp;
193 }
194 lp = zero + lastidx;
195 lp->global = 0;
196
197 if (!s) {
198 lp->seek = -1;
199 len = 0;
200 } else {
201 while ((c = *s++) && c != '\n')
202 ;
203 len = s - begin;
204 if ((lp->seek = lseek(scratch, 0, SEEK_END)) < 0 ||
205 write(scratch, begin, len) < 0) {
206 error("input/output error");
207 }
208 }
209 if (off)
210 *off = len;
211 ++lastidx;
212 return lp - zero;
213 }
214
215 static int
216 getindex(int line)
217 {
218 struct hline *lp;
219 int n;
220
221 if (line == -1)
222 line = 0;
223 for (n = 0, lp = zero; n != line; n++)
224 lp = zero + lp->next;
225
226 return lp - zero;
227 }
228
229 static char *
230 gettxt(int line)
231 {
232 static char buf[CACHESIZ];
233 static off_t lasto;
234 struct hline *lp;
235 off_t off, block;
236 ssize_t n;
237 char *p;
238
239 lp = zero + getindex(line);
240 text.siz = 0;
241 off = lp->seek;
242
243 if (off == (off_t) -1)
244 return addchar('\0', &text);
245
246 repeat:
247 if (!csize || off < lasto || off - lasto >= csize) {
248 block = off & ~(CACHESIZ-1);
249 if (lseek(scratch, block, SEEK_SET) < 0 ||
250 (n = read(scratch, buf, CACHESIZ)) < 0) {
251 error("input/output error");
252 }
253 csize = n;
254 lasto = block;
255 }
256 for (p = buf + off - lasto; p < buf + csize && *p != '\n'; ++p) {
257 ++off;
258 addchar(*p, &text);
259 }
260 if (csize && p == buf + csize)
261 goto repeat;
262
263 addchar('\n', &text);
264 addchar('\0', &text);
265 return text.str;
266 }
267
268 static void
269 setglobal(int i, int v)
270 {
271 zero[getindex(i)].global = v;
272 }
273
274 static void
275 clearundo(void)
276 {
277 free(udata.vec);
278 udata.vec = NULL;
279 newcmd = udata.nr = udata.cap = 0;
280 modflag = 0;
281 }
282
283 static void
284 newundo(int from1, int from2)
285 {
286 struct link *p;
287
288 if (newcmd) {
289 clearundo();
290 udata.curln = ocurln;
291 udata.lastln = olastln;
292 }
293 if (udata.nr >= udata.cap) {
294 size_t siz = (udata.cap + 10) * sizeof(struct link);
295 if ((p = realloc(udata.vec, siz)) == NULL)
296 error("out of memory");
297 udata.vec = p;
298 udata.cap = udata.cap + 10;
299 }
300 p = &udata.vec[udata.nr++];
301 p->from1 = from1;
302 p->to1 = zero[from1].next;
303 p->from2 = from2;
304 p->to2 = zero[from2].prev;
305 }
306
307 /*
308 * relink: to1 <- from1
309 * from2 -> to2
310 */
311 static void
312 relink(int to1, int from1, int from2, int to2)
313 {
314 newundo(from1, from2);
315 zero[from1].next = to1;
316 zero[from2].prev = to2;
317 modflag = 1;
318 }
319
320 static void
321 undo(void)
322 {
323 struct link *p;
324
325 if (udata.nr == 0)
326 return;
327 for (p = &udata.vec[udata.nr-1]; udata.nr > 0; --p) {
328 --udata.nr;
329 zero[p->from1].next = p->to1;
330 zero[p->from2].prev = p->to2;
331 }
332 free(udata.vec);
333 udata.vec = NULL;
334 udata.cap = 0;
335 curln = udata.curln;
336 lastln = udata.lastln;
337 }
338
339 static void
340 inject(char *s, int where)
341 {
342 int off, k, begin, end;
343
344 if (where == BEFORE) {
345 begin = getindex(curln-1);
346 end = getindex(nextln(curln-1));
347 } else {
348 begin = getindex(curln);
349 end = getindex(nextln(curln));
350 }
351 while (*s) {
352 k = makeline(s, &off);
353 s += off;
354 relink(k, begin, k, begin);
355 relink(end, k, end, k);
356 ++lastln;
357 ++curln;
358 begin = k;
359 }
360 }
361
362 static void
363 clearbuf(void)
364 {
365 if (scratch)
366 close(scratch);
367 remove(tmpname);
368 free(zero);
369 zero = NULL;
370 scratch = csize = idxsize = lastidx = curln = lastln = 0;
371 modflag = lastln = curln = 0;
372 }
373
374 static void
375 setscratch(void)
376 {
377 int r, k;
378 char *dir;
379
380 clearbuf();
381 clearundo();
382 if ((dir = getenv("TMPDIR")) == NULL)
383 dir = "/tmp";
384 r = snprintf(tmpname, sizeof(tmpname), "%s/%s",
385 dir, "ed.XXXXXX");
386 if (r < 0 || (size_t)r >= sizeof(tmpname))
387 error("scratch filename too long");
388 if ((scratch = mkstemp(tmpname)) < 0)
389 error("failed to create scratch file");
390 if ((k = makeline(NULL, NULL)))
391 error("input/output error in scratch file");
392 relink(k, k, k, k);
393 clearundo();
394 }
395
396 static void
397 compile(int delim)
398 {
399 int n, ret, c,bracket;
400 static char buf[BUFSIZ];
401
402 if (!isgraph(delim))
403 error("invalid pattern delimiter");
404
405 eol = bol = bracket = lastre.siz = 0;
406 for (n = 0;; ++n) {
407 c = input();
408 if (c == delim && !bracket || c == '\0') {
409 break;
410 } else if (c == '^') {
411 bol = 1;
412 } else if (c == '$') {
413 eol = 1;
414 } else if (c == '\\') {
415 addchar(c, &lastre);
416 c = input();
417 } else if (c == '[') {
418 bracket = 1;
419 } else if (c == ']') {
420 bracket = 0;
421 }
422 addchar(c, &lastre);
423 }
424 if (n == 0) {
425 if (!pattern)
426 error("no previous pattern");
427 return;
428 }
429 addchar('\0', &lastre);
430
431 if (pattern)
432 regfree(pattern);
433 if (!pattern && (!(pattern = malloc(sizeof(*pattern)))))
434 error("out of memory");
435 if ((ret = regcomp(pattern, lastre.str, REG_NEWLINE))) {
436 regerror(ret, pattern, buf, sizeof(buf));
437 error(buf);
438 }
439 }
440
441 static int
442 match(int num)
443 {
444 lastmatch = gettxt(num);
445 return !regexec(pattern, lastmatch, 10, matchs, 0);
446 }
447
448 static int
449 rematch(int num)
450 {
451 regoff_t off = matchs[0].rm_eo;
452
453 if (!regexec(pattern, lastmatch + off, 10, matchs, 0)) {
454 lastmatch += off;
455 return 1;
456 }
457
458 return 0;
459 }
460
461 static int
462 search(int way)
463 {
464 int i;
465
466 i = curln;
467 do {
468 chksignals();
469
470 i = (way == '?') ? prevln(i) : nextln(i);
471 if (i > 0 && match(i))
472 return i;
473 } while (i != curln);
474
475 error("invalid address");
476 return -1; /* not reached */
477 }
478
479 static void
480 skipblank(void)
481 {
482 char c;
483
484 while ((c = input()) == ' ' || c == '\t')
485 ;
486 back(c);
487 }
488
489 static void
490 ensureblank(void)
491 {
492 char c;
493
494 switch ((c = input())) {
495 case ' ':
496 case '\t':
497 skipblank();
498 case '\0':
499 back(c);
500 break;
501 default:
502 error("unknown command");
503 }
504 }
505
506 static int
507 getnum(void)
508 {
509 int ln, n, c;
510
511 for (ln = 0; isdigit(c = input()); ln += n) {
512 if (ln > INT_MAX/10)
513 goto invalid;
514 n = c - '0';
515 ln *= 10;
516 if (INT_MAX - ln < n)
517 goto invalid;
518 }
519 back(c);
520 return ln;
521
522 invalid:
523 error("invalid address");
524 return -1; /* not reached */
525 }
526
527 static int
528 linenum(int *line)
529 {
530 int ln, c;
531
532 skipblank();
533
534 switch (c = input()) {
535 case '.':
536 ln = curln;
537 break;
538 case '\'':
539 skipblank();
540 if (!islower(c = input()))
541 error("invalid mark character");
542 if (!(ln = marks[c - 'a']))
543 error("invalid address");
544 break;
545 case '$':
546 ln = lastln;
547 break;
548 case '?':
549 case '/':
550 compile(c);
551 ln = search(c);
552 break;
553 case '^':
554 case '-':
555 case '+':
556 ln = curln;
557 back(c);
558 break;
559 default:
560 back(c);
561 if (isdigit(c))
562 ln = getnum();
563 else
564 return 0;
565 break;
566 }
567 *line = ln;
568 return 1;
569 }
570
571 static int
572 address(int *line)
573 {
574 int ln, sign, c, num;
575
576 if (!linenum(&ln))
577 return 0;
578
579 for (;;) {
580 skipblank();
581 if ((c = input()) != '+' && c != '-' && c != '^')
582 break;
583 sign = c == '+' ? 1 : -1;
584 num = isdigit(back(input())) ? getnum() : 1;
585 num *= sign;
586 if (INT_MAX - ln < num)
587 goto invalid;
588 ln += num;
589 }
590 back(c);
591
592 if (ln < 0 || ln > lastln)
593 error("invalid address");
594 *line = ln;
595 return 1;
596
597 invalid:
598 error("invalid address");
599 return -1; /* not reached */
600 }
601
602 static void
603 getlst(void)
604 {
605 int ln, c;
606
607 if ((c = input()) == ',') {
608 line1 = 1;
609 line2 = lastln;
610 nlines = lastln;
611 return;
612 } else if (c == ';') {
613 line1 = curln;
614 line2 = lastln;
615 nlines = lastln - curln + 1;
616 return;
617 }
618 back(c);
619 line2 = curln;
620 for (nlines = 0; address(&ln); ) {
621 line1 = line2;
622 line2 = ln;
623 ++nlines;
624
625 skipblank();
626 if ((c = input()) != ',' && c != ';') {
627 back(c);
628 break;
629 }
630 if (c == ';')
631 curln = line2;
632 }
633 if (nlines > 2)
634 nlines = 2;
635 else if (nlines <= 1)
636 line1 = line2;
637 }
638
639 static void
640 deflines(int def1, int def2)
641 {
642 if (!nlines) {
643 line1 = def1;
644 line2 = def2;
645 }
646 if (line1 > line2 || line1 < 0 || line2 > lastln)
647 error("invalid address");
648 }
649
650 static void
651 quit(void)
652 {
653 clearbuf();
654 exit(exstatus);
655 }
656
657 static void
658 setinput(char *s)
659 {
660 copystring(&cmdline, s);
661 inputidx = 0;
662 }
663
664 static void
665 getinput(void)
666 {
667 int ch;
668
669 string(&cmdline);
670
671 while ((ch = getchar()) != '\n' && ch != EOF) {
672 if (ch == '\\') {
673 if ((ch = getchar()) == EOF)
674 break;
675 if (ch != '\n') {
676 ungetc(ch, stdin);
677 ch = '\\';
678 }
679 }
680 addchar(ch, &cmdline);
681 }
682
683 addchar('\0', &cmdline);
684 inputidx = 0;
685
686 if (ch == EOF) {
687 chksignals();
688 if (ferror(stdin)) {
689 exstatus = 1;
690 fputs("ed: error reading input\n", stderr);
691 }
692 quit();
693 }
694 }
695
696 static int
697 moreinput(void)
698 {
699 if (!uflag)
700 return cmdline.str[inputidx] != '\0';
701
702 getinput();
703 return 1;
704 }
705
706 static void dowrite(const char *, int);
707
708 static void
709 dump(void)
710 {
711 char *home;
712
713 if (modflag)
714 return;
715
716 line1 = nextln(0);
717 line2 = lastln;
718
719 if (!setjmp(savesp)) {
720 dowrite("ed.hup", 1);
721 return;
722 }
723
724 home = getenv("HOME");
725 if (!home || chdir(home) < 0)
726 return;
727
728 if (!setjmp(savesp))
729 dowrite("ed.hup", 1);
730 }
731
732 static void
733 chksignals(void)
734 {
735 if (hup) {
736 exstatus = 1;
737 dump();
738 quit();
739 }
740
741 if (intr) {
742 intr = 0;
743 newcmd = 1;
744 clearerr(stdin);
745 error("Interrupt");
746 }
747 }
748
749 static void
750 dowrite(const char *fname, int trunc)
751 {
752 size_t bytecount = 0;
753 int i, r, line;
754 FILE *aux;
755 static int sh;
756 static FILE *fp;
757
758 if (fp) {
759 sh ? pclose(fp) : fclose(fp);
760 fp = NULL;
761 }
762
763 if(fname[0] == '!') {
764 sh = 1;
765 fname++;
766 if((fp = popen(fname, "w")) == NULL)
767 error("bad exec");
768 } else {
769 sh = 0;
770 if ((fp = fopen(fname, "w")) == NULL)
771 error("cannot open input file");
772 }
773
774 line = curln;
775 for (i = line1; i <= line2; ++i) {
776 chksignals();
777
778 gettxt(i);
779 bytecount += text.siz - 1;
780 fwrite(text.str, 1, text.siz - 1, fp);
781 }
782
783 curln = line2;
784
785 aux = fp;
786 fp = NULL;
787 r = sh ? pclose(aux) : fclose(aux);
788 if (r)
789 error("input/output error");
790 strcpy(savfname, fname);
791 modflag = 0;
792 curln = line;
793 if (optdiag)
794 printf("%zu\n", bytecount);
795 }
796
797 static void
798 doread(const char *fname)
799 {
800 size_t cnt;
801 ssize_t n;
802 char *p;
803 FILE *aux;
804 static size_t len;
805 static char *s;
806 static FILE *fp;
807
808 if (fp)
809 fclose(fp);
810 if ((fp = fopen(fname, "r")) == NULL)
811 error("cannot open input file");
812
813 curln = line2;
814 for (cnt = 0; (n = getline(&s, &len, fp)) > 0; cnt += (size_t)n)…
815 chksignals();
816 if (s[n-1] != '\n') {
817 if (len == SIZE_MAX || !(p = realloc(s, ++len)))
818 error("out of memory");
819 s = p;
820 s[n-1] = '\n';
821 s[n] = '\0';
822 }
823 inject(s, AFTER);
824 }
825 if (optdiag)
826 printf("%zu\n", cnt);
827
828 aux = fp;
829 fp = NULL;
830 if (fclose(aux))
831 error("input/output error");
832 }
833
834 static void
835 doprint(void)
836 {
837 int i, c;
838 char *s, *str;
839
840 if (line1 <= 0 || line2 > lastln)
841 error("incorrect address");
842 for (i = line1; i <= line2; ++i) {
843 chksignals();
844 if (pflag == 'n')
845 printf("%d\t", i);
846 for (s = gettxt(i); (c = *s) != '\n'; ++s) {
847 if (pflag != 'l')
848 goto print_char;
849 switch (c) {
850 case '$':
851 str = "\\$";
852 goto print_str;
853 case '\t':
854 str = "\\t";
855 goto print_str;
856 case '\b':
857 str = "\\b";
858 goto print_str;
859 case '\\':
860 str = "\\\\";
861 goto print_str;
862 default:
863 if (!isprint(c)) {
864 printf("\\x%x", 0xFF & c);
865 break;
866 }
867 print_char:
868 putchar(c);
869 break;
870 print_str:
871 fputs(str, stdout);
872 break;
873 }
874 }
875 if (pflag == 'l')
876 fputs("$", stdout);
877 putc('\n', stdout);
878 }
879 curln = i - 1;
880 }
881
882 static void
883 dohelp(void)
884 {
885 if (lasterr)
886 puts(lasterr);
887 }
888
889 static void
890 chkprint(int flag)
891 {
892 int c;
893
894 if (flag) {
895 if ((c = input()) == 'p' || c == 'l' || c == 'n')
896 pflag = c;
897 else
898 back(c);
899 }
900 if ((c = input()) != '\0' && c != '\n')
901 error("invalid command suffix");
902 }
903
904 static char *
905 getfname(int comm)
906 {
907 int c;
908 char *bp;
909 static char fname[FILENAME_MAX];
910
911 skipblank();
912 for (bp = fname; bp < &fname[FILENAME_MAX]; *bp++ = c) {
913 if ((c = input()) == '\0')
914 break;
915 }
916 if (bp == fname) {
917 if (savfname[0] == '\0')
918 error("no current filename");
919 return savfname;
920 } else if (bp == &fname[FILENAME_MAX]) {
921 error("file name too long");
922 } else {
923 *bp = '\0';
924 if (savfname[0] == '\0' || comm == 'e' || comm == 'f')
925 strcpy(savfname, fname);
926 return fname;
927 }
928
929 return NULL; /* not reached */
930 }
931
932 static void
933 append(int num)
934 {
935 int ch;
936 static String line;
937
938 curln = num;
939 while (moreinput()) {
940 string(&line);
941 while ((ch = input()) != '\n' && ch != '\0')
942 addchar(ch, &line);
943 addchar('\n', &line);
944 addchar('\0', &line);
945
946 if (!strcmp(line.str, ".\n") || !strcmp(line.str, "."))
947 break;
948 inject(line.str, AFTER);
949 }
950 }
951
952 static void
953 delete(int from, int to)
954 {
955 int lto, lfrom;
956
957 if (!from)
958 error("incorrect address");
959
960 lfrom = getindex(prevln(from));
961 lto = getindex(nextln(to));
962 lastln -= to - from + 1;
963 curln = (from > lastln) ? lastln : from;;
964 relink(lto, lfrom, lto, lfrom);
965 }
966
967 static void
968 move(int where)
969 {
970 int before, after, lto, lfrom;
971
972 if (!line1 || (where >= line1 && where <= line2))
973 error("incorrect address");
974
975 before = getindex(prevln(line1));
976 after = getindex(nextln(line2));
977 lfrom = getindex(line1);
978 lto = getindex(line2);
979 relink(after, before, after, before);
980
981 if (where < line1) {
982 curln = where + line1 - line2 + 1;
983 } else {
984 curln = where;
985 where -= line1 - line2 + 1;
986 }
987 before = getindex(where);
988 after = getindex(nextln(where));
989 relink(lfrom, before, lfrom, before);
990 relink(after, lto, after, lto);
991 }
992
993 static void
994 join(void)
995 {
996 int i;
997 char *t, c;
998 static String s;
999
1000 string(&s);
1001 for (i = line1;; i = nextln(i)) {
1002 chksignals();
1003 for (t = gettxt(i); (c = *t) != '\n'; ++t)
1004 addchar(*t, &s);
1005 if (i == line2)
1006 break;
1007 }
1008
1009 addchar('\n', &s);
1010 addchar('\0', &s);
1011 delete(line1, line2);
1012 inject(s.str, BEFORE);
1013 free(s.str);
1014 }
1015
1016 static void
1017 scroll(int num)
1018 {
1019 int max, ln, cnt;
1020
1021 if (!line1 || line1 == lastln)
1022 error("incorrect address");
1023
1024 ln = line1;
1025 max = line1 + num;
1026 if (max > lastln)
1027 max = lastln;
1028 for (cnt = line1; cnt < max; cnt++) {
1029 chksignals();
1030 fputs(gettxt(ln), stdout);
1031 ln = nextln(ln);
1032 }
1033 curln = ln;
1034 }
1035
1036 static void
1037 copy(int where)
1038 {
1039
1040 if (!line1)
1041 error("incorrect address");
1042 curln = where;
1043
1044 while (line1 <= line2) {
1045 chksignals();
1046 inject(gettxt(line1), AFTER);
1047 if (line2 >= curln)
1048 line2 = nextln(line2);
1049 line1 = nextln(line1);
1050 if (line1 >= curln)
1051 line1 = nextln(line1);
1052 }
1053 }
1054
1055 static void
1056 execsh(void)
1057 {
1058 static String cmd;
1059 char *p;
1060 int c, repl = 0;
1061
1062 skipblank();
1063 if ((c = input()) != '!') {
1064 back(c);
1065 string(&cmd);
1066 } else if (cmd.siz) {
1067 --cmd.siz;
1068 repl = 1;
1069 } else {
1070 error("no previous command");
1071 }
1072
1073 while ((c = input()) != '\0') {
1074 switch (c) {
1075 case '%':
1076 if (savfname[0] == '\0')
1077 error("no current filename");
1078 repl = 1;
1079 for (p = savfname; *p; ++p)
1080 addchar(*p, &cmd);
1081 break;
1082 case '\\':
1083 c = input();
1084 if (c != '%') {
1085 back(c);
1086 c = '\\';
1087 }
1088 default:
1089 addchar(c, &cmd);
1090 }
1091 }
1092 addchar('\0', &cmd);
1093
1094 if (repl)
1095 puts(cmd.str);
1096 system(cmd.str);
1097 if (optdiag)
1098 puts("!");
1099 }
1100
1101 static void
1102 getrhs(int delim)
1103 {
1104 int c;
1105 static String s;
1106
1107 string(&s);
1108 while ((c = input()) != '\0' && c != delim)
1109 addchar(c, &s);
1110 addchar('\0', &s);
1111 if (c == '\0') {
1112 pflag = 'p';
1113 back(c);
1114 }
1115
1116 if (!strcmp("%", s.str)) {
1117 if (!rhs)
1118 error("no previous substitution");
1119 free(s.str);
1120 } else {
1121 free(rhs);
1122 rhs = s.str;
1123 }
1124 s.str = NULL;
1125 }
1126
1127 static int
1128 getnth(void)
1129 {
1130 int c;
1131
1132 if ((c = input()) == 'g') {
1133 return -1;
1134 } else if (isdigit(c)) {
1135 if (c == '0')
1136 return -1;
1137 return c - '0';
1138 } else {
1139 back(c);
1140 return 1;
1141 }
1142 }
1143
1144 static void
1145 addpre(String *s)
1146 {
1147 char *p;
1148
1149 for (p = lastmatch; p < lastmatch + matchs[0].rm_so; ++p)
1150 addchar(*p, s);
1151 }
1152
1153 static void
1154 addpost(String *s)
1155 {
1156 char c, *p;
1157
1158 for (p = lastmatch + matchs[0].rm_eo; (c = *p); ++p)
1159 addchar(c, s);
1160 addchar('\0', s);
1161 }
1162
1163 static int
1164 addsub(String *s, int nth, int nmatch)
1165 {
1166 char *end, *q, *p, c;
1167 int sub;
1168
1169 if (nth != nmatch && nth != -1) {
1170 q = lastmatch + matchs[0].rm_so;
1171 end = lastmatch + matchs[0].rm_eo;
1172 while (q < end)
1173 addchar(*q++, s);
1174 return 0;
1175 }
1176
1177 for (p = rhs; (c = *p); ++p) {
1178 switch (c) {
1179 case '&':
1180 sub = 0;
1181 goto copy_match;
1182 case '\\':
1183 if ((c = *++p) == '\0')
1184 return 1;
1185 if (!isdigit(c))
1186 goto copy_char;
1187 sub = c - '0';
1188 copy_match:
1189 q = lastmatch + matchs[sub].rm_so;
1190 end = lastmatch + matchs[sub].rm_eo;
1191 while (q < end)
1192 addchar(*q++, s);
1193 break;
1194 default:
1195 copy_char:
1196 addchar(c, s);
1197 break;
1198 }
1199 }
1200 return 1;
1201 }
1202
1203 static void
1204 subline(int num, int nth)
1205 {
1206 int i, m, changed;
1207 static String s;
1208
1209 string(&s);
1210 i = changed = 0;
1211 for (m = match(num); m; m = rematch(num)) {
1212 chksignals();
1213 addpre(&s);
1214 changed |= addsub(&s, nth, ++i);
1215 if (eol || bol)
1216 break;
1217 }
1218 if (!changed)
1219 return;
1220 addpost(&s);
1221 delete(num, num);
1222 curln = prevln(num);
1223 inject(s.str, AFTER);
1224 }
1225
1226 static void
1227 subst(int nth)
1228 {
1229 int i, line, next;
1230
1231 line = line1;
1232 for (i = 0; i < line2 - line1 + 1; i++) {
1233 chksignals();
1234
1235 next = getindex(nextln(line));
1236 subline(line, nth);
1237
1238 /*
1239 * The substitution command can add lines, so
1240 * we have to skip lines until we find the
1241 * index that we saved before the substitution
1242 */
1243 do
1244 line = nextln(line);
1245 while (getindex(line) != next);
1246 }
1247 }
1248
1249 static void
1250 docmd(void)
1251 {
1252 int cmd, c, line3, num, trunc;
1253
1254 repeat:
1255 skipblank();
1256 cmd = input();
1257 trunc = pflag = 0;
1258 switch (cmd) {
1259 case '&':
1260 skipblank();
1261 chkprint(0);
1262 if (!ocmdline)
1263 error("no previous command");
1264 setinput(ocmdline);
1265 getlst();
1266 goto repeat;
1267 case '!':
1268 execsh();
1269 break;
1270 case '\0':
1271 num = gflag ? curln : curln+1;
1272 deflines(num, num);
1273 line1 = line2;
1274 pflag = 'p';
1275 goto print;
1276 case 'l':
1277 case 'n':
1278 case 'p':
1279 back(cmd);
1280 chkprint(1);
1281 deflines(curln, curln);
1282 goto print;
1283 case 'g':
1284 case 'G':
1285 case 'v':
1286 case 'V':
1287 error("cannot nest global commands");
1288 case 'H':
1289 if (nlines > 0)
1290 goto unexpected;
1291 chkprint(0);
1292 optverbose ^= 1;
1293 break;
1294 case 'h':
1295 if (nlines > 0)
1296 goto unexpected;
1297 chkprint(0);
1298 dohelp();
1299 break;
1300 case 'w':
1301 trunc = 1;
1302 case 'W':
1303 ensureblank();
1304 deflines(nextln(0), lastln);
1305 dowrite(getfname(cmd), trunc);
1306 break;
1307 case 'r':
1308 ensureblank();
1309 if (nlines > 1)
1310 goto bad_address;
1311 deflines(lastln, lastln);
1312 doread(getfname(cmd));
1313 break;
1314 case 'd':
1315 chkprint(1);
1316 deflines(curln, curln);
1317 delete(line1, line2);
1318 break;
1319 case '=':
1320 if (nlines > 1)
1321 goto bad_address;
1322 chkprint(1);
1323 deflines(lastln, lastln);
1324 printf("%d\n", line1);
1325 break;
1326 case 'u':
1327 if (nlines > 0)
1328 goto bad_address;
1329 chkprint(1);
1330 if (udata.nr == 0)
1331 error("nothing to undo");
1332 undo();
1333 break;
1334 case 's':
1335 deflines(curln, curln);
1336 c = input();
1337 compile(c);
1338 getrhs(c);
1339 num = getnth();
1340 chkprint(1);
1341 subst(num);
1342 break;
1343 case 'i':
1344 if (nlines > 1)
1345 goto bad_address;
1346 chkprint(1);
1347 deflines(curln, curln);
1348 if (!line1)
1349 line1++;
1350 append(prevln(line1));
1351 break;
1352 case 'a':
1353 if (nlines > 1)
1354 goto bad_address;
1355 chkprint(1);
1356 deflines(curln, curln);
1357 append(line1);
1358 break;
1359 case 'm':
1360 deflines(curln, curln);
1361 if (!address(&line3))
1362 line3 = curln;
1363 chkprint(1);
1364 move(line3);
1365 break;
1366 case 't':
1367 deflines(curln, curln);
1368 if (!address(&line3))
1369 line3 = curln;
1370 chkprint(1);
1371 copy(line3);
1372 break;
1373 case 'c':
1374 chkprint(1);
1375 deflines(curln, curln);
1376 delete(line1, line2);
1377 append(prevln(line1));
1378 break;
1379 case 'j':
1380 chkprint(1);
1381 deflines(curln, curln+1);
1382 if (line1 != line2 && curln != 0)
1383 join();
1384 break;
1385 case 'z':
1386 if (nlines > 1)
1387 goto bad_address;
1388 if (isdigit(back(input())))
1389 num = getnum();
1390 else
1391 num = 24;
1392 chkprint(1);
1393 scroll(num);
1394 break;
1395 case 'k':
1396 if (nlines > 1)
1397 goto bad_address;
1398 if (!islower(c = input()))
1399 error("invalid mark character");
1400 chkprint(1);
1401 deflines(curln, curln);
1402 marks[c - 'a'] = line1;
1403 break;
1404 case 'P':
1405 if (nlines > 0)
1406 goto unexpected;
1407 chkprint(1);
1408 optprompt ^= 1;
1409 break;
1410 case 'Q':
1411 modflag = 0;
1412 case 'q':
1413 if (nlines > 0)
1414 goto unexpected;
1415 if (modflag)
1416 goto modified;
1417 quit();
1418 break;
1419 case 'f':
1420 ensureblank();
1421 if (nlines > 0)
1422 goto unexpected;
1423 if (back(input()) != '\0')
1424 getfname(cmd);
1425 else
1426 puts(savfname);
1427 chkprint(0);
1428 break;
1429 case 'E':
1430 modflag = 0;
1431 case 'e':
1432 ensureblank();
1433 if (nlines > 0)
1434 goto unexpected;
1435 if (modflag)
1436 goto modified;
1437 getfname(cmd);
1438 setscratch();
1439 deflines(curln, curln);
1440 doread(savfname);
1441 clearundo();
1442 break;
1443 default:
1444 error("unknown command");
1445 bad_address:
1446 error("invalid address");
1447 modified:
1448 modflag = 0;
1449 error("warning: file modified");
1450 unexpected:
1451 error("unexpected address");
1452 }
1453
1454 if (!pflag)
1455 return;
1456 line1 = line2 = curln;
1457
1458 print:
1459 doprint();
1460 }
1461
1462 static int
1463 chkglobal(void)
1464 {
1465 int delim, c, dir, i, v;
1466
1467 uflag = 1;
1468 gflag = 0;
1469 skipblank();
1470
1471 switch (c = input()) {
1472 case 'g':
1473 uflag = 0;
1474 case 'G':
1475 dir = 1;
1476 break;
1477 case 'v':
1478 uflag = 0;
1479 case 'V':
1480 dir = 0;
1481 break;
1482 default:
1483 back(c);
1484 return 0;
1485 }
1486 gflag = 1;
1487 deflines(nextln(0), lastln);
1488 delim = input();
1489 compile(delim);
1490
1491 for (i = 1; i <= lastln; ++i) {
1492 chksignals();
1493 if (i >= line1 && i <= line2)
1494 v = match(i) == dir;
1495 else
1496 v = 0;
1497 setglobal(i, v);
1498 }
1499
1500 return 1;
1501 }
1502
1503 static void
1504 savecmd(void)
1505 {
1506 int ch;
1507
1508 skipblank();
1509 ch = input();
1510 if (ch != '&') {
1511 ocmdline = strdup(cmdline.str);
1512 if (ocmdline == NULL)
1513 error("out of memory");
1514 }
1515 back(ch);
1516 }
1517
1518 static void
1519 doglobal(void)
1520 {
1521 int cnt, ln, k, idx;
1522
1523 skipblank();
1524 gflag = 1;
1525 if (uflag)
1526 chkprint(0);
1527
1528 ln = line1;
1529 for (cnt = 0; cnt < lastln; ) {
1530 chksignals();
1531 k = getindex(ln);
1532 if (zero[k].global) {
1533 zero[k].global = 0;
1534 curln = ln;
1535 nlines = 0;
1536
1537 if (!uflag) {
1538 idx = inputidx;
1539 getlst();
1540 docmd();
1541 inputidx = idx;
1542 continue;
1543 }
1544
1545 line1 = line2 = ln;
1546 pflag = 0;
1547 doprint();
1548
1549 for (;;) {
1550 getinput();
1551 if (strcmp(cmdline.str, "") == 0)
1552 break;
1553 savecmd();
1554 getlst();
1555 docmd();
1556 }
1557
1558 } else {
1559 cnt++;
1560 ln = nextln(ln);
1561 }
1562 }
1563 }
1564
1565 static void
1566 usage(void)
1567 {
1568 eprintf("usage: %s [-s] [-p] [file]\n", argv0);
1569 }
1570
1571 static void
1572 sigintr(int n)
1573 {
1574 intr = 1;
1575 }
1576
1577 static void
1578 sighup(int dummy)
1579 {
1580 hup = 1;
1581 }
1582
1583 static void
1584 edit(void)
1585 {
1586 for (;;) {
1587 newcmd = 1;
1588 ocurln = curln;
1589 olastln = lastln;
1590 if (optprompt) {
1591 fputs(prompt, stdout);
1592 fflush(stdout);
1593 }
1594
1595 getinput();
1596 getlst();
1597 chkglobal() ? doglobal() : docmd();
1598 }
1599 }
1600
1601 static void
1602 init(char *fname)
1603 {
1604 size_t len;
1605
1606 setscratch();
1607 if (!fname)
1608 return;
1609 if ((len = strlen(fname)) >= FILENAME_MAX || len == 0)
1610 error("incorrect filename");
1611 memcpy(savfname, fname, len);
1612 doread(fname);
1613 clearundo();
1614 }
1615
1616 int
1617 main(int argc, char *argv[])
1618 {
1619 ARGBEGIN {
1620 case 'p':
1621 prompt = EARGF(usage());
1622 optprompt = 1;
1623 break;
1624 case 's':
1625 optdiag = 0;
1626 break;
1627 default:
1628 usage();
1629 } ARGEND
1630
1631 if (argc > 1)
1632 usage();
1633
1634 if (!setjmp(savesp)) {
1635 sigaction(SIGINT,
1636 &(struct sigaction) {.sa_handler = sigintr},
1637 NULL);
1638 sigaction(SIGHUP,
1639 &(struct sigaction) {.sa_handler = sighup},
1640 NULL);
1641 sigaction(SIGQUIT,
1642 &(struct sigaction) {.sa_handler = SIG_IGN},
1643 NULL);
1644 init(*argv);
1645 }
1646 edit();
1647
1648 /* not reached */
1649 return 0;
1650 }
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.