sbase

suckless unix tools
git clone git://git.2f30.org/sbase.git
Log | Files | Refs | README | LICENSE

commit fd9160cfa19ecb9545ee2ac0bc033af6f57963a3
parent 44be14231a095668f36756a47a71608f5fdd85a2
Author: Roberto E. Vargas Caballero <k0ga@shike2.com>
Date:   Mon Dec 14 12:09:34 +0000

Add ed(1) - the standard text editor

Diffstat:
Makefile | 1+
README | 1+
TODO | 2+-
ed.1 | 9+++++++++
ed.c | 1377+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 1389 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile @@ -92,6 +92,7 @@ BIN =\ dirname\ du\ echo\ + ed\ env\ expand\ expr\ diff --git a/README b/README @@ -30,6 +30,7 @@ The following tools are implemented: =*|o dirname . =*|o du . =*|o echo . + o ed . =*|o env . #*|o expand . #*|o expr . diff --git a/TODO b/TODO @@ -7,7 +7,7 @@ at awk bc diff -ed +ed manpage install patch pathchk diff --git a/ed.1 b/ed.1 @@ -0,0 +1,9 @@ +.Dd 2015-12-14 +.Dt ED 1 +.Os sbase +.Sh NAME +.Nm ed +.Nd text editor +.Sh SYNOPSIS +.Nm +is the standard text editor. diff --git a/ed.c b/ed.c @@ -0,0 +1,1377 @@ +/* See LICENSE file for copyright and license details. */ +#include <sys/stat.h> +#include <fcntl.h> +#include <regex.h> +#include <unistd.h> + +#include <ctype.h> +#include <limits.h> +#include <setjmp.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define REGEXSIZE 100 +#define LINESIZE 80 +#define NUMLINES 32 +#define CACHESIZ 4096 + +struct hline { + off_t seek; + char global; + int next, prev; +}; + +struct undo { + int curln; + size_t nr, cap; + struct link { + int to1, from1; + int to2, from2; + } *vec; +}; + +char *prompt = "*"; +regex_t *pattern; +regmatch_t matchs[10]; +char *lastre; + +int optverbose, optprompt, exstatus, optdiag = 1; +int marks['z' - 'a']; +int nlines, line1, line2; +int curln, lastln, ocurln; +jmp_buf savesp; +char *lasterr; +size_t idxsize, lastidx; +struct hline *zero; +char *text; +char savfname[FILENAME_MAX]; +char tmpname[FILENAME_MAX]; +size_t sizetxt, memtxt; +int scratch; +int pflag, modflag, uflag, gflag; +size_t csize; +char *cmdline; +char *ocmdline; +size_t cmdsiz, cmdcap; +int repidx; +char *rhs; +char *lastmatch; +struct undo udata; +int newcmd; + +static void +error(char *msg) +{ + int c; + + exstatus = 1; + lasterr = msg; + fputs("?\n", stderr); + + if (optverbose) + fprintf(stderr, "%s\n", msg); + + /* discard until end of line */ + if (repidx < 0 && cmdsiz > 0 && cmdline[cmdsiz-1] != '\n') { + while ((c = getchar()) != '\n' && c != EOF) + /* nothing */; + } + + longjmp(savesp, 1); +} + +static int +nextln(int line) +{ + ++line; + return (line > lastln) ? 0 : line; +} + +static int +prevln(int line) +{ + --line; + return (line < 0) ? lastln : line; +} + +static char * +addchar(char c, char *t, size_t *capacity, size_t *size) +{ + size_t cap = *capacity, siz = *size; + + if (siz >= cap && + (cap > SIZE_MAX - LINESIZE || + (t = realloc(t, cap += LINESIZE)) == NULL)) + error("out of memory"); + t[siz++] = c; + *size = siz; + *capacity = cap; + return t; +} + +static int +input(void) +{ + int c; + + if (repidx >= 0) + return ocmdline[repidx++]; + + if ((c = getchar()) != EOF) + cmdline = addchar(c, cmdline, &cmdcap, &cmdsiz); + return c; +} + +static int +back(int c) +{ + if (repidx > 0) { + --repidx; + } else { + ungetc(c, stdin); + if (c != EOF) + --cmdsiz; + } + return c; +} + +static int +makeline(char *s, int *off) +{ + struct hline *lp; + size_t len; + char c, *begin = s; + + if (lastidx >= idxsize) { + if (idxsize > SIZE_MAX - NUMLINES || + !(lp = realloc(zero, (idxsize + NUMLINES) * sizeof(*lp)))) + error("out of memory"); + idxsize += NUMLINES; + zero = lp; + } + lp = zero + lastidx; + + while ((c = *s) && *s != '\n') + ++s; + if (c == '\n') + ++s; + len = s - begin; + if (off) + *off = len; + + if (len > 0) + if ((lp->seek = lseek(scratch, 0, SEEK_END)) < 0 || + write(scratch, begin, len) < 0) { + error("input/output error"); + } + + ++lastidx; + return lp - zero; +} + +static int +getindex(int line) +{ + struct hline *lp; + int n; + + for (n = 0, lp = zero; n != line; ++n) + lp = zero + lp->next; + + return lp - zero; +} + +static char * +gettxt(int line) +{ + static char buf[CACHESIZ]; + static off_t lasto; + struct hline *lp; + off_t off, block; + ssize_t n; + char *p; + + lp = zero + getindex(line); + off = lp->seek; + sizetxt = 0; + +repeat: + if (!csize || off < lasto || off - lasto >= csize) { + block = off & ~(CACHESIZ-1); + if (lseek(scratch, block, SEEK_SET) < 0 || + (n = read(scratch, buf, CACHESIZ)) < 0) { + error("input/output error"); + } + csize = n; + lasto = block; + } + for (p = buf + off - lasto; p < buf + csize && *p != '\n'; ++p) { + ++off; + text = addchar(*p, text, &memtxt, &sizetxt); + } + if (csize && p == buf + csize) + goto repeat; + + text = addchar('\n', text, &memtxt, &sizetxt); + text = addchar('\0', text, &memtxt, &sizetxt); + return text; +} + +static void +setglobal(int i, int v) +{ + zero[getindex(i)].global = v; +} + +static void +clearundo(void) +{ + free(udata.vec); + udata.vec = NULL; + newcmd = udata.nr = udata.cap = 0; +} + +static void +relink(int to1, int from1, int from2, int to2) +{ + struct link *p; + + if (newcmd) { + clearundo(); + udata.curln = ocurln; + } + if (udata.nr >= udata.cap) { + size_t siz = (udata.cap + 10) * sizeof(struct link); + if ((p = realloc(udata.vec, siz)) == NULL) + error("out of memory"); + udata.vec = p; + udata.cap = udata.cap + 10; + } + p = &udata.vec[udata.nr++]; + p->from1 = from1; + p->to1 = zero[from1].next; + p->from2 = from2; + p->to2 = zero[from2].prev; + + zero[from1].next = to1; + zero[from2].prev = to2; + modflag = 1; +} + +static void +undo(void) +{ + int i; + struct link *p; + + if (udata.nr == 0) + error("nothing to undo"); + for (p = &udata.vec[udata.nr-1]; udata.nr--; --p) { + zero[p->from1].next = p->to1; + zero[p->from2].prev = p->to2; + } + free(udata.vec); + udata.vec = NULL; + udata.cap = 0; + curln = udata.curln; +} + +static void +inject(char *s) +{ + int off, k, begin, end; + + begin = getindex(curln); + end = getindex(nextln(curln)); + + while (*s) { + k = makeline(s, &off); + s += off; + relink(k, begin, k, begin); + relink(end, k, end, k); + ++lastln; + ++curln; + begin = k; + } +} + +static void +clearbuf() +{ + if (scratch) + close(scratch); + remove(tmpname); + free(zero); + zero = NULL; + scratch = csize = idxsize = lastidx = curln = lastln = 0; + lastln = curln = 0; +} + +static void +setscratch() +{ + int k; + + clearbuf(); + clearundo(); + strcpy(tmpname, "ed.XXXXXX"); + if ((scratch = mkstemp(tmpname)) < 0) { + /* try /tmp if cwd is not writable */ + strcpy(tmpname, "/tmp/ed.XXXXXX"); + if ((scratch = mkstemp(tmpname)) < 0) + error("failed to create scratch file"); + } + if ((k = makeline("", NULL))) + error("input/output error in scratch file"); + relink(k, k, k, k); + clearundo(); + modflag = 0; +} + +static void +compile(int delim) +{ + int n, ret, c,bracket; + static size_t siz, cap; + static char buf[BUFSIZ]; + + if (!isgraph(delim)) + error("invalid pattern delimiter"); + + bracket = siz = 0; + for (n = 0;; ++n) { + if ((c = input()) == delim && !bracket) + break; + if (c == '\n' || c == EOF) { + back(c); + break; + } + + if (c == '\\') { + lastre = addchar(c, lastre, &cap, &siz); + c = input(); + } else if (c == '[') { + bracket = 1; + } else if (c == ']') { + bracket = 0; + } + lastre = addchar(c, lastre, &cap, &siz); + } + if (n == 0) { + if (!pattern) + error("no previous pattern"); + return; + } + lastre = addchar('\0', lastre, &cap, &siz); + + if (pattern) + regfree(pattern); + if (!pattern && (!(pattern = malloc(sizeof(*pattern))))) + error("out of memory"); + if ((ret = regcomp(pattern, lastre, REG_NEWLINE))) { + regerror(ret, pattern, buf, sizeof(buf)); + error(buf); + } +} + +static int +match(int num) +{ + lastmatch = gettxt(num); + return !regexec(pattern, lastmatch, 10, matchs, 0); +} + +static int +rematch(int num) +{ + lastmatch += matchs[0].rm_eo; + return !regexec(pattern, lastmatch, 10, matchs, 0); +} + +static int +search(int way) +{ + int i; + + i = curln; + do { + i = (way == '?') ? prevln(i) : nextln(i); + if (match(i)) + return i; + } while (i != curln); + + error("invalid address"); +} + +static void +skipblank(void) +{ + char c; + + while ((c = input()) == ' ' || c == '\t') + /* nothing */; + back(c); +} + +static int +getnum(void) +{ + int ln, n, c; + + for (ln = 0; isdigit(c = input()); ln += n) { + if (ln > INT_MAX/10) + goto invalid; + n = c - '0'; + ln *= 10; + if (INT_MAX - ln < n) + goto invalid; + } + back(c); + return ln; + +invalid: + error("invalid address"); +} + +static int +linenum(int *line) +{ + int ln, c; + + skipblank(); + + switch (c = input()) { + case '.': + ln = curln; + break; + case '\'': + skipblank(); + if (!isalpha(c = input())) + error("invalid mark character"); + if (!(ln = marks[c])) + error("invalid address"); + break; + case '$': + ln = lastln; + break; + case '?': + case '/': + compile(c); + ln = search(c); + break; + case '^': + case '-': + case '+': + ln = curln; + back(c); + break; + default: + back(c); + if (isdigit(c)) + ln = getnum(); + else + return 0; + break; + } + *line = ln; + return 1; +} + +static int +address(int *line) +{ + int ln, sign, c, num; + + if (!linenum(&ln)) + return 0; + + for (;;) { + skipblank(); + if ((c = input()) != '+' && c != '-' && c != '^') + break; + sign = c == '+' ? 1 : -1; + num = isdigit(back(input())) ? getnum() : 1; + num *= sign; + if (INT_MAX - ln < num) + goto invalid; + ln += num; + } + back(c); + + if (ln < 0 || ln > lastln) + error("invalid address"); + *line = ln; + return 1; + +invalid: + error("invalid address"); +} + +static void +getlst() +{ + int ln, c; + + if ((c = input()) == ',') { + line1 = 1; + line2 = lastln; + nlines = lastln; + return; + } else if (c == ';') { + line1 = curln; + line2 = lastln; + nlines = lastln - curln + 1; + return; + } + back(c); + line2 = curln; + for (nlines = 0; address(&ln); ) { + line1 = line2; + line2 = ln; + ++nlines; + + skipblank(); + if ((c = input()) != ',' && c != ';') { + back(c); + break; + } + if (c == ';') + curln = line2; + } + if (nlines > 2) + nlines = 2; + else if (nlines <= 1) + line1 = line2; +} + +static void +deflines(int def1, int def2) +{ + if (!nlines) { + line1 = def1; + line2 = def2; + } + if (line1 > line2 || line1 < 0 || line2 > lastln) + error("invalid address"); +} + +static void +dowrite(char *fname, int trunc) +{ + FILE *fp; + int i; + + if (!(fp = fopen(fname, (trunc) ? "w" : "a"))) + error("input/output error"); + + for (i = line1; i <= line2; ++i) + fputs(gettxt(i), fp); + + curln = line2; + if (fclose(fp)) + error("input/output error"); + strcpy(savfname, fname); + modflag = 0; +} + +static void +doread(char *fname) +{ + size_t cnt; + ssize_t n; + char *p; + FILE *aux; + static size_t len; + static char *s; + static FILE *fp; + + if (fp) + fclose(fp); + if (!(fp = fopen(fname, "r"))) + error("input/output error"); + + curln = line2; + for (cnt = 0; (n = getline(&s, &len, fp)) > 0; cnt += (size_t)n) { + if (s[n-1] != '\n') { + if (len == SIZE_MAX || !(p = realloc(s, ++len))) + error("out of memory"); + s = p; + s[n-1] = '\n'; + s[n] = '\0'; + } + inject(s); + } + printf("%zu\n", cnt); + + aux = fp; + fp = NULL; + if (fclose(aux)) + error("input/output error"); + + if (savfname[0] == '\0') { + modflag = 0; + clearundo(); + strcpy(savfname, fname); + } +} + +static void +doprint(void) +{ + int i, c; + char *s, *str; + + if (line1 <= 0 || line2 > lastln) + error("incorrect address"); + for (i = line1; i <= line2; ++i) { + if (pflag == 'n') + printf("%d\t", i); + for (s = gettxt(i); (c = *s) != '\n'; ++s) { + if (pflag != 'l') + goto print_char; + switch (c) { + case '$': + str = "\\$"; + goto print_str; + case '\t': + str = "\\t"; + goto print_str; + case '\b': + str = "\\b"; + goto print_str; + case '\\': + str = "\\\\"; + goto print_str; + default: + if (!isprint(c)) { + printf("\\x%x", 0xFF & c); + break; + } + print_char: + putchar(c); + break; + print_str: + fputs(str, stdout); + break; + } + } + if (pflag == 'l') + fputs("$", stdout); + putc('\n', stdout); + } + curln = i - 1; +} + +static void +dohelp(void) +{ + if (lasterr) + fprintf(stderr, "%s\n", lasterr); +} + +static void +chkprint(int flag) +{ + char c; + + if (flag) { + if ((c = input()) == 'p' || c == 'l' || c == 'n') + pflag = c; + else + back(c); + } + if (input() != '\n') + error("invalid command suffix"); +} + +static char * +getfname(void) +{ + int c; + char *bp; + static char fname[FILENAME_MAX]; + + skipblank(); + for (bp = fname; bp < &fname[FILENAME_MAX]; *bp++ = c) { + if ((c = input()) == EOF || c == '\n') + break; + } + if (bp == fname) { + if (savfname[0] == '\0') + error("no current filename"); + return savfname; + } else if (bp == &fname[FILENAME_MAX]) { + error("file name too long"); + } else { + *bp = '\0'; + return fname; + } +} + +static void +append(int num) +{ + char *s = NULL; + size_t len = 0; + + curln = num; + while (getline(&s, &len, stdin) > 0) { + if (*s == '.' && s[1] == '\n') + break; + inject(s); + } + free(s); +} + +static void +delete(int from, int to) +{ + int lto, lfrom; + + if (!from) + error("incorrect address"); + + lfrom = getindex(prevln(from)); + lto = getindex(nextln(to)); + lastln -= to - from + 1; + curln = (from > lastln) ? lastln : from;; + relink(lto, lfrom, lto, lfrom); +} + +static void +move(int where) +{ + int before, after, lto, lfrom; + + if (!line1 || where >= line1 && where <= line2) + error("incorrect address"); + + before = getindex(prevln(line1)); + after = getindex(nextln(line2)); + lfrom = getindex(line1); + lto = getindex(line2); + relink(after, before, after, before); + + if (where < line1) { + curln = where + line1 - line2 + 1; + } else { + curln = where; + where -= line1 - line2 + 1; + } + before = getindex(where); + after = getindex(nextln(where)); + relink(lfrom, before, lfrom, before); + relink(after, lto, after, lto); +} + +static void +join(void) +{ + int i; + char *t, c; + size_t len = 0, cap = 0; + static char *s; + + free(s); + for (s = NULL, i = line1; i <= line2; i = nextln(i)) { + for (t = gettxt(i); (c = *t) != '\n'; ++t) + s = addchar(*t, s, &cap, &len); + } + + s = addchar('\n', s, &cap, &len); + s = addchar('\0', s, &cap, &len); + delete(line1, line2); + inject(s); + free(s); +} + +static void +scroll(int num) +{ + int i; + + if (!line1 || line1 == lastln) + error("incorrect address"); + + for (i = line1; i <= line1 + num && i <= lastln; ++i) + fputs(gettxt(i), stdout); + curln = i; +} + +static void +copy(int where) +{ + int i; + + if (!line1 || where >= line1 && where <= line2) + error("incorrect address"); + curln = where; + + for (i = line1; i <= line2; ++i) + inject(gettxt(i)); +} + +static void +quit(void) +{ + clearbuf(); + exit(exstatus); +} + +static void +execsh(void) +{ + static char *cmd; + static size_t siz, cap; + char c, *p; + int repl = 0; + + skipblank(); + if ((c = input()) != '!') { + back(c); + siz = 0; + } else if (cmd) { + --siz; + repl = 1; + } else { + error("no previous command"); + } + + while ((c = input()) != EOF && c != '\n') { + if (c == '%' && (siz == 0 || cmd[siz - 1] != '\\')) { + if (savfname[0] == '\0') + error("no current filename"); + repl = 1; + for (p = savfname; *p; ++p) + cmd = addchar(*p, cmd, &cap, &siz); + } else { + cmd = addchar(c, cmd, &cap, &siz); + } + } + cmd = addchar('\0', cmd, &cap, &siz); + + if (repl) + puts(cmd); + system(cmd); + puts("!"); +} + +static void +getrhs(int delim) +{ + int c; + size_t n, siz, cap; + static char *s; + + free(s); + s = NULL; + siz = cap = 0; + while ((c = input()) != '\n' && c != EOF && c != delim) { + if (c == '\\') { + if (isdigit(c = input())) { + back(c); + c = '\\'; + } + } + s = addchar(c, s, &siz, &cap); + } + s = addchar('\0', s, &siz, &cap); + if (c == EOF) + error("invalid pattern delimiter"); + if (c == '\n') { + pflag = 'p'; + back(c); + } + + if (!strcmp("%", s)) { + free(s); + if (!rhs) + error("no previous substitution"); + } else { + free(rhs); + rhs = s; + } + s = NULL; +} + +static int +getnth(void) +{ + int c; + + if ((c = input()) == 'g') { + return -1; + } else if (isdigit(c)) { + if (c == '0') + return -1; + return c - '0'; + } else { + back(c); + return 1; + } +} + +static void +addpre(char **s, size_t *cap, size_t *siz) +{ + char *p; + + for (p = lastmatch; p < lastmatch + matchs[0].rm_so; ++p) + *s = addchar(*p, *s, cap, siz); +} + +static void +addpost(char **s, size_t *cap, size_t *siz) +{ + char c, *p; + + for (p = lastmatch + matchs[0].rm_eo; c = *p; ++p) + *s = addchar(c, *s, cap, siz); + *s = addchar('\0', *s, cap, siz); +} + +static void +addsub(char **s, size_t *cap, size_t *siz) +{ + char *end, *q, *p, c; + int sub; + + for (p = rhs; c = *p; ++p) { + switch (c) { + case '&': + sub = 0; + goto copy_match; + case '\\': + if ((c = *++p) == '\0') + return; + if (!isdigit(c)) + goto copy_char; + sub = c - '0'; + copy_match: + q = lastmatch + matchs[sub].rm_so; + end = lastmatch + matchs[sub].rm_eo; + while (q < end) + *s = addchar(*q++, *s, cap, siz); + break; + default: + copy_char: + *s = addchar(c, *s, cap, siz); + break; + } + } +} + +static void +subline(int num, int nth) +{ + int m, changed; + static char *s; + static size_t siz, cap; + + siz = 0; + for (m = match(num); m; m = rematch(num)) { + if (--nth > 0) + continue; + changed = 1; + addpre(&s, &cap, &siz); + addsub(&s, &cap, &siz); + if (nth == 0) + break; + } + if (!changed) + return; + addpost(&s, &cap, &siz); + delete(num, num); + curln = prevln(num); + inject(s); +} + +static void +subst(int nth) +{ + int i; + + for (i = line1; i <= line2; ++i) + subline(i, nth); +} + +static void +docmd(void) +{ + char *s, cmd; + int rep = 0, c, line3, num, trunc; + +repeat: + skipblank(); + cmd = input(); + trunc = pflag = 0; + switch (cmd) { + case '&': + skipblank(); + chkprint(0); + if (!ocmdline) + error("no previous command"); + rep = 1; + repidx = 0; + getlst(); + goto repeat; + case '!': + execsh(); + break; + case EOF: + if (cmdsiz == 0) + quit(); + case '\n': + if (gflag && uflag) + return; + num = gflag ? curln : curln+1; + deflines(num, num); + pflag = 'p'; + goto print; + case 'l': + case 'n': + case 'p': + back(cmd); + chkprint(1); + deflines(curln, curln); + goto print; + case 'g': + case 'G': + case 'v': + case 'V': + error("cannot nest global commands"); + case 'H': + if (nlines > 0) + goto unexpected; + chkprint(0); + optverbose ^= 1; + break; + case 'h': + if (nlines > 0) + goto unexpected; + chkprint(0); + dohelp(); + break; + case 'w': + trunc = 1; + case 'W': + deflines(nextln(0), lastln); + dowrite(getfname(), trunc); + break; + case 'r': + if (nlines > 1) + goto bad_address; + deflines(lastln, lastln); + doread(getfname()); + break; + case 'd': + chkprint(1); + deflines(curln, curln); + delete(line1, line2); + break; + case '=': + if (nlines > 1) + goto bad_address; + chkprint(1); + deflines(lastln, lastln); + printf("%d\n", line1); + break; + case 'u': + if (nlines > 0) + goto bad_address; + chkprint(1); + undo(); + break; + case 's': + deflines(curln, curln); + c = input(); + compile(c); + getrhs(c); + num = getnth(); + chkprint(1); + subst(num); + break; + case 'i': + if (nlines > 1) + goto bad_address; + chkprint(1); + deflines(curln, curln); + if (!line1) + goto bad_address; + append(prevln(line1)); + break; + case 'a': + if (nlines > 1) + goto bad_address; + chkprint(1); + deflines(curln, curln); + append(line1); + break; + case 'm': + deflines(curln, curln); + if (!address(&line3)) + line3 = curln; + chkprint(1); + move(line3); + break; + case 't': + deflines(curln, curln); + if (!address(&line3)) + line3 = curln; + chkprint(1); + copy(line3); + break; + case 'c': + chkprint(1); + deflines(curln, curln); + delete(line1, line2); + append(prevln(line1)); + break; + case 'j': + chkprint(1); + deflines(curln, curln+1); + if (!line1) + goto bad_address; + join(); + break; + case 'z': + if (nlines > 1) + goto bad_address; + if (isdigit(back(input()))) + num = getnum(); + else + num = 24; + chkprint(1); + scroll(num); + break; + case 'k': + if (nlines > 1) + goto bad_address; + if (!islower(c = input())) + error("invalid mark character"); + chkprint(1); + deflines(curln, curln); + marks[c] = line1; + break; + case 'P': + if (nlines > 0) + goto unexpected; + chkprint(1); + optprompt ^= 1; + break; + case 'Q': + modflag = 0; + case 'q': + if (nlines > 0) + goto unexpected; + if (modflag) + goto modified; + quit(); + break; + case 'f': + if (nlines > 0) + goto unexpected; + if (!strcmp(s = getfname(), savfname)) + puts(savfname); + else + strcpy(savfname, s); + break; + case 'E': + modflag = 0; + case 'e': + if (nlines > 0) + goto unexpected; + if (modflag) + goto modified; + setscratch(); + strcpy(savfname, getfname()); + deflines(curln, curln); + doread(savfname); + clearundo(); + modflag = 0; + break; + default: + error("unknown command"); + bad_address: + error("invalid address"); + modified: + modflag = 0; + error("warning: file modified"); + unexpected: + error("unexpected address"); + } + + if (!pflag) + goto save_last_cmd; + + line1 = line2 = curln; +print: + doprint(); + +save_last_cmd: + if (!uflag) + repidx = 0; + if (rep) + return; + free(ocmdline); + cmdline = addchar('\0', cmdline, &cmdcap, &cmdsiz); + if ((ocmdline = strdup(cmdline)) == NULL) + error("out of memory"); +} + +static int +chkglobal(void) +{ + int delim, c, dir, i, v; + + uflag = 1; + gflag = 0; + skipblank(); + + switch (c = input()) { + case 'g': + uflag = 0; + case 'G': + dir = 1; + break; + case 'v': + uflag = 0; + case 'V': + dir = 0; + break; + default: + back(c); + return 0; + } + gflag = 1; + deflines(nextln(0), lastln); + delim = input(); + compile(delim); + + for (i = 1; i <= lastln; ++i) { + if (i >= line1 && i <= line2) + v = match(i) == dir; + else + v = 0; + setglobal(i, v); + } + + return 1; +} + +static void +doglobal(void) +{ + int i, k; + + skipblank(); + cmdsiz = 0; + gflag = 1; + if (uflag) + chkprint(0); + + for (i = 1; i <= lastln; i++) { + k = getindex(i); + if (!zero[k].global) + continue; + curln = i; + nlines = 0; + if (uflag) { + line1 = line2 = i; + pflag = 0; + doprint(); + } + docmd(); + } +} + +static void +usage(void) +{ + fputs("ed [-s][-p][file]\n", stderr); + exit(1); +} + +static void +sigintr(int n) +{ + signal(SIGINT, sigintr); + error("interrupt"); +} + +static void +sighup(int dummy) +{ + int n; + char *home = getenv("HOME"), fname[FILENAME_MAX]; + + if (modflag) { + line1 = nextln(0); + line2 = lastln; + if (!setjmp(savesp)) { + dowrite("ed.hup", 1); + } else if (home && !setjmp(savesp)) { + n = snprintf(fname, + sizeof(fname), "%s/%s", home, "ed.hup"); + if (n < sizeof(fname) && n > 0) + dowrite(fname, 1); + } + } + exstatus = 1; + quit(); +} + +int +main(int argc, char *argv[]) +{ + char *p; + + while (*++argv) { + if (argv[0][0] != '-') + break; + for (p = argv[0] + 1; *p; ++p) { + switch (*p) { + case 's': + optdiag = 0; + break; + case 'p': + if (!*++argv) + usage(); + prompt = *argv; + optprompt = 1; + break; + default: + usage(); + } + } + } + + signal(SIGINT, sigintr); + signal(SIGHUP, sighup); + signal(SIGQUIT, SIG_IGN); + if (!setjmp(savesp)) { + setscratch(); + if (*argv) { + if (strlen(*argv) >= FILENAME_MAX) + error("file name too long"); + doread(*argv); + } + } + + for (;;) { + newcmd = 1; + ocurln = curln; + cmdsiz = 0; + repidx = -1; + if (optprompt) + fputs(prompt, stdout); + getlst(); + chkglobal() ? doglobal() : docmd(); + } + + /* not reached */ + return 0; +}