ed

simple ed
git clone git://git.2f30.org/ed
Log | Files | Refs | LICENSE

commit d57fef66e55bccc2607f8007b1cdb89a5e3e8a76
parent 346735a1b782b0c4ce9c056c340e996919d0f21c
Author: sin <sin@2f30.org>
Date:   Tue, 28 Mar 2017 14:34:10 +0100

Sync ed from sbase

Diffstat:
Aarg.h | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Med.c | 391+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
2 files changed, 287 insertions(+), 169 deletions(-)

diff --git a/arg.h b/arg.h @@ -0,0 +1,65 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX)) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define LNGARG() &argv[0][0] + +#endif diff --git a/ed.c b/ed.c @@ -13,6 +13,8 @@ #include <stdlib.h> #include <string.h> +#include "arg.h" + #define REGEXSIZE 100 #define LINESIZE 80 #define NUMLINES 32 @@ -33,53 +35,66 @@ struct undo { } *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; +char *argv0; +static char *prompt = "*"; +static regex_t *pattern; +static regmatch_t matchs[10]; +static char *lastre; + +static int optverbose, optprompt, exstatus, optdiag = 1; +static int marks['z' - 'a']; +static int nlines, line1, line2; +static int curln, lastln, ocurln; +static jmp_buf savesp; +static char *lasterr; +static size_t idxsize, lastidx; +static struct hline *zero; +static char *text; +static char savfname[FILENAME_MAX]; +static char tmpname[FILENAME_MAX]; +static size_t sizetxt, memtxt; +static int scratch; +static int pflag, modflag, uflag, gflag; +static size_t csize; +static char *cmdline; +static char *ocmdline; +static size_t cmdsiz, cmdcap; +static int repidx; +static char *rhs; +static char *lastmatch; +static struct undo udata; +static int newcmd; +int eol, bol; static void -error(char *msg) +discard(void) { int c; + /* discard until end of line */ + if (repidx < 0 && + ((cmdsiz > 0 && cmdline[cmdsiz-1] != '\n') || cmdsiz == 0)) { + while ((c = getchar()) != '\n' && c != EOF) + /* nothing */; + } +} + +static void undo(void); + +static void +error(char *msg) +{ exstatus = 1; lasterr = msg; fputs("?\n", stderr); if (optverbose) fprintf(stderr, "%s\n", msg); + if (!newcmd) + undo(); - /* discard until end of line */ - if (repidx < 0 && cmdsiz > 0 && cmdline[cmdsiz-1] != '\n') { - while ((c = getchar()) != '\n' && c != EOF) - /* nothing */; - } - + discard(); + curln = ocurln; longjmp(savesp, 1); } @@ -154,20 +169,20 @@ makeline(char *s, int *off) } lp = zero + lastidx; - while ((c = *s) && *s != '\n') - ++s; - if (c == '\n') - ++s; - len = s - begin; + if (!s) { + lp->seek = -1; + len = 0; + } else { + while ((c = *s++) != '\n') + /* nothing */; + len = s - begin; + if ((lp->seek = lseek(scratch, 0, SEEK_END)) < 0 || + write(scratch, begin, len) < 0) { + error("input/output error"); + } + } 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; } @@ -178,7 +193,9 @@ getindex(int line) struct hline *lp; int n; - for (n = 0, lp = zero; n != line; ++n) + if (line == -1) + line = 0; + for (n = 0, lp = zero; n != line; n++) lp = zero + lp->next; return lp - zero; @@ -195,8 +212,11 @@ gettxt(int line) char *p; lp = zero + getindex(line); - off = lp->seek; sizetxt = 0; + off = lp->seek; + + if (off == (off_t) -1) + return text = addchar('\0', text, &memtxt, &sizetxt); repeat: if (!csize || off < lasto || off - lasto >= csize) { @@ -232,6 +252,7 @@ clearundo(void) free(udata.vec); udata.vec = NULL; newcmd = udata.nr = udata.cap = 0; + modflag = 0; } static void @@ -264,11 +285,10 @@ relink(int to1, int from1, int from2, int to2) static void undo(void) { - int i; struct link *p; if (udata.nr == 0) - error("nothing to undo"); + return; for (p = &udata.vec[udata.nr-1]; udata.nr--; --p) { zero[p->from1].next = p->to1; zero[p->from2].prev = p->to2; @@ -280,13 +300,17 @@ undo(void) } static void -inject(char *s) +inject(char *s, int j) { int off, k, begin, end; - begin = getindex(curln); - end = getindex(nextln(curln)); - + if (j) { + begin = getindex(curln-1); + end = getindex(nextln(curln-1)); + } else { + begin = getindex(curln); + end = getindex(nextln(curln)); + } while (*s) { k = makeline(s, &off); s += off; @@ -299,7 +323,7 @@ inject(char *s) } static void -clearbuf() +clearbuf(void) { if (scratch) close(scratch); @@ -307,28 +331,29 @@ clearbuf() free(zero); zero = NULL; scratch = csize = idxsize = lastidx = curln = lastln = 0; - lastln = curln = 0; + modflag = lastln = curln = 0; } static void -setscratch() +setscratch(void) { - int k; + int r, k; + char *dir; 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))) + if ((dir = getenv("TMPDIR")) == NULL) + dir = "/tmp"; + r = snprintf(tmpname, sizeof(tmpname), "%s/%s", + dir, "ed.XXXXXX"); + if (r < 0 || (size_t)r >= sizeof(tmpname)) + error("scratch filename too long"); + if ((scratch = mkstemp(tmpname)) < 0) + error("failed to create scratch file"); + if ((k = makeline(NULL, NULL))) error("input/output error in scratch file"); relink(k, k, k, k); clearundo(); - modflag = 0; } static void @@ -341,11 +366,15 @@ compile(int delim) if (!isgraph(delim)) error("invalid pattern delimiter"); - bracket = siz = 0; + eol = bol = bracket = siz = 0; for (n = 0;; ++n) { if ((c = input()) == delim && !bracket) break; - if (c == '\n' || c == EOF) { + if (c == '^') { + bol = 1; + } else if (c == '$') { + eol = 1; + } else if (c == '\n' || c == EOF) { back(c); break; } @@ -387,8 +416,14 @@ match(int num) static int rematch(int num) { - lastmatch += matchs[0].rm_eo; - return !regexec(pattern, lastmatch, 10, matchs, 0); + regoff_t off = matchs[0].rm_eo; + + if (!regexec(pattern, lastmatch + off, 10, matchs, 0)) { + lastmatch += off; + return 1; + } + + return 0; } static int @@ -399,11 +434,12 @@ search(int way) i = curln; do { i = (way == '?') ? prevln(i) : nextln(i); - if (match(i)) + if (i > 0 && match(i)) return i; } while (i != curln); error("invalid address"); + return -1; /* not reached */ } static void @@ -434,6 +470,7 @@ getnum(void) invalid: error("invalid address"); + return -1; /* not reached */ } static int @@ -449,9 +486,9 @@ linenum(int *line) break; case '\'': skipblank(); - if (!isalpha(c = input())) + if (!islower(c = input())) error("invalid mark character"); - if (!(ln = marks[c])) + if (!(ln = marks[c - 'a'])) error("invalid address"); break; case '$': @@ -508,10 +545,11 @@ address(int *line) invalid: error("invalid address"); + return -1; /* not reached */ } static void -getlst() +getlst(void) { int ln, c; @@ -559,14 +597,15 @@ deflines(int def1, int def2) } static void -dowrite(char *fname, int trunc) +dowrite(const char *fname, int trunc) { FILE *fp; - int i; + int i, line; if (!(fp = fopen(fname, (trunc) ? "w" : "a"))) error("input/output error"); + line = curln; for (i = line1; i <= line2; ++i) fputs(gettxt(i), fp); @@ -575,10 +614,11 @@ dowrite(char *fname, int trunc) error("input/output error"); strcpy(savfname, fname); modflag = 0; + curln = line; } static void -doread(char *fname) +doread(const char *fname) { size_t cnt; ssize_t n; @@ -590,8 +630,8 @@ doread(char *fname) if (fp) fclose(fp); - if (!(fp = fopen(fname, "r"))) - error("input/output error"); + if ((fp = fopen(fname, "r")) == NULL) + error("cannot open input file"); curln = line2; for (cnt = 0; (n = getline(&s, &len, fp)) > 0; cnt += (size_t)n) { @@ -602,20 +642,15 @@ doread(char *fname) s[n-1] = '\n'; s[n] = '\0'; } - inject(s); + inject(s, 0); } - printf("%zu\n", cnt); + if (optdiag) + 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 @@ -688,7 +723,7 @@ chkprint(int flag) } static char * -getfname(void) +getfname(char comm) { int c; char *bp; @@ -707,8 +742,12 @@ getfname(void) error("file name too long"); } else { *bp = '\0'; + if (savfname[0] == '\0' || comm == 'e' || comm == 'f') + strcpy(savfname, fname); return fname; } + + return NULL; /* not reached */ } static void @@ -721,7 +760,7 @@ append(int num) while (getline(&s, &len, stdin) > 0) { if (*s == '.' && s[1] == '\n') break; - inject(s); + inject(s, 0); } free(s); } @@ -746,7 +785,7 @@ move(int where) { int before, after, lto, lfrom; - if (!line1 || where >= line1 && where <= line2) + if (!line1 || (where >= line1 && where <= line2)) error("incorrect address"); before = getindex(prevln(line1)); @@ -776,15 +815,17 @@ join(void) static char *s; free(s); - for (s = NULL, i = line1; i <= line2; i = nextln(i)) { + for (s = NULL, i = line1;; i = nextln(i)) { for (t = gettxt(i); (c = *t) != '\n'; ++t) s = addchar(*t, s, &cap, &len); + if (i == line2) + break; } s = addchar('\n', s, &cap, &len); s = addchar('\0', s, &cap, &len); delete(line1, line2); - inject(s); + inject(s, 1); free(s); } @@ -806,12 +847,12 @@ copy(int where) { int i; - if (!line1 || where >= line1 && where <= line2) + if (!line1 || (where >= line1 && where <= line2)) error("incorrect address"); curln = where; for (i = line1; i <= line2; ++i) - inject(gettxt(i)); + inject(gettxt(i), 0); } static void @@ -856,28 +897,22 @@ execsh(void) if (repl) puts(cmd); system(cmd); - puts("!"); + if (optdiag) + puts("!"); } static void getrhs(int delim) { int c; - size_t n, siz, cap; + size_t 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 = '\\'; - } - } + while ((c = input()) != '\n' && c != EOF && c != delim) s = addchar(c, s, &siz, &cap); - } s = addchar('\0', s, &siz, &cap); if (c == EOF) error("invalid pattern delimiter"); @@ -928,25 +963,33 @@ addpost(char **s, size_t *cap, size_t *siz) { char c, *p; - for (p = lastmatch + matchs[0].rm_eo; c = *p; ++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) +static int +addsub(char **s, size_t *cap, size_t *siz, int nth, int nmatch) { char *end, *q, *p, c; int sub; - for (p = rhs; c = *p; ++p) { + if (nth != nmatch && nth != -1) { + q = lastmatch + matchs[0].rm_so; + end = lastmatch + matchs[0].rm_eo; + while (q < end) + *s = addchar(*q++, *s, cap, siz); + return 0; + } + + for (p = rhs; (c = *p); ++p) { switch (c) { case '&': sub = 0; goto copy_match; case '\\': if ((c = *++p) == '\0') - return; + return 1; if (!isdigit(c)) goto copy_char; sub = c - '0'; @@ -962,23 +1005,21 @@ addsub(char **s, size_t *cap, size_t *siz) break; } } + return 1; } static void subline(int num, int nth) { - int m, changed; + int i, m, changed; static char *s; static size_t siz, cap; - siz = 0; + i = changed = 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) + changed |= addsub(&s, &cap, &siz, nth, ++i); + if (eol || bol) break; } if (!changed) @@ -986,7 +1027,7 @@ subline(int num, int nth) addpost(&s, &cap, &siz); delete(num, num); curln = prevln(num); - inject(s); + inject(s, 0); } static void @@ -1001,7 +1042,7 @@ subst(int nth) static void docmd(void) { - char *s, cmd; + char cmd; int rep = 0, c, line3, num, trunc; repeat: @@ -1059,13 +1100,13 @@ repeat: trunc = 1; case 'W': deflines(nextln(0), lastln); - dowrite(getfname(), trunc); + dowrite(getfname(cmd), trunc); break; case 'r': if (nlines > 1) goto bad_address; deflines(lastln, lastln); - doread(getfname()); + doread(getfname(cmd)); break; case 'd': chkprint(1); @@ -1083,6 +1124,8 @@ repeat: if (nlines > 0) goto bad_address; chkprint(1); + if (udata.nr == 0) + error("nothing to undo"); undo(); break; case 's': @@ -1100,7 +1143,7 @@ repeat: chkprint(1); deflines(curln, curln); if (!line1) - goto bad_address; + line1++; append(prevln(line1)); break; case 'a': @@ -1133,9 +1176,8 @@ repeat: case 'j': chkprint(1); deflines(curln, curln+1); - if (!line1) - goto bad_address; - join(); + if (line1 != line2 && curln != 0) + join(); break; case 'z': if (nlines > 1) @@ -1154,7 +1196,7 @@ repeat: error("invalid mark character"); chkprint(1); deflines(curln, curln); - marks[c] = line1; + marks[c - 'a'] = line1; break; case 'P': if (nlines > 0) @@ -1174,10 +1216,11 @@ repeat: case 'f': if (nlines > 0) goto unexpected; - if (!strcmp(s = getfname(), savfname)) - puts(savfname); + if (back(input()) != '\n') + getfname(cmd); else - strcpy(savfname, s); + puts(savfname); + chkprint(0); break; case 'E': modflag = 0; @@ -1186,12 +1229,11 @@ repeat: goto unexpected; if (modflag) goto modified; + getfname(cmd); setscratch(); - strcpy(savfname, getfname()); deflines(curln, curln); doread(savfname); clearundo(); - modflag = 0; break; default: error("unknown command"); @@ -1286,12 +1328,13 @@ doglobal(void) } docmd(); } + discard(); /* cover the case of not matching anything */ } static void usage(void) { - fputs("ed [-s][-p][file]\n", stderr); + fprintf(stderr, "usage: %s [-s] [-p] [file]\n", argv0); exit(1); } @@ -1324,43 +1367,10 @@ sighup(int dummy) quit(); } -int -main(int argc, char *argv[]) +static void +edit(void) { - 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); - } - } - + setjmp(savesp); for (;;) { newcmd = 1; ocurln = curln; @@ -1371,6 +1381,49 @@ main(int argc, char *argv[]) getlst(); chkglobal() ? doglobal() : docmd(); } +} + +static void +init(char *fname) +{ + size_t len; + + if (setjmp(savesp)) + return; + setscratch(); + if (!fname) + return; + if ((len = strlen(fname)) >= FILENAME_MAX || len == 0) + error("incorrect filename"); + memcpy(savfname, fname, len); + doread(fname); + clearundo(); +} + +int +main(int argc, char *argv[]) +{ + ARGBEGIN { + case 'p': + prompt = EARGF(usage()); + optprompt = 1; + break; + case 's': + optdiag = 0; + break; + default: + usage(); + } ARGEND + + if (argc > 1) + usage(); + + signal(SIGINT, sigintr); + signal(SIGHUP, sighup); + signal(SIGQUIT, SIG_IGN); + + init(*argv); + edit(); /* not reached */ return 0;