commit d57fef66e55bccc2607f8007b1cdb89a5e3e8a76
parent 346735a1b782b0c4ce9c056c340e996919d0f21c
Author: sin <sin@2f30.org>
Date: Tue, 28 Mar 2017 14:34:10 +0100
Sync ed from sbase
Diffstat:
A | arg.h | | | 65 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | ed.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;