2019-11-14 23:15:48 +00:00
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include <draw.h>
|
|
|
|
#include <thread.h>
|
|
|
|
#include <cursor.h>
|
|
|
|
#include <mouse.h>
|
|
|
|
#include <keyboard.h>
|
|
|
|
#include <frame.h>
|
|
|
|
#include <fcall.h>
|
|
|
|
#include <plumb.h>
|
|
|
|
#include <libsec.h>
|
|
|
|
#include "dat.h"
|
|
|
|
#include "edit.h"
|
|
|
|
#include "fns.h"
|
|
|
|
|
2021-06-13 05:54:54 +00:00
|
|
|
static char linex[] = "\n";
|
|
|
|
static char wordx[] = " \t\n";
|
|
|
|
struct cmdtab cmdtab[] = {
|
|
|
|
/* cmdc text regexp addr defcmd defaddr count token fn */
|
|
|
|
'\n',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
nl_cmd,
|
|
|
|
'a',
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
a_cmd,
|
|
|
|
'b',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aNo,
|
|
|
|
0,
|
|
|
|
linex,
|
|
|
|
b_cmd,
|
|
|
|
'c',
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
c_cmd,
|
|
|
|
'd',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
d_cmd,
|
|
|
|
'e',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aNo,
|
|
|
|
0,
|
|
|
|
wordx,
|
|
|
|
e_cmd,
|
|
|
|
'f',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aNo,
|
|
|
|
0,
|
|
|
|
wordx,
|
|
|
|
f_cmd,
|
|
|
|
'g',
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
'p',
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
g_cmd,
|
|
|
|
'i',
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
i_cmd,
|
|
|
|
'm',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
m_cmd,
|
|
|
|
'p',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
p_cmd,
|
|
|
|
'r',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
wordx,
|
|
|
|
e_cmd,
|
|
|
|
's',
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
s_cmd,
|
|
|
|
't',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
m_cmd,
|
|
|
|
'u',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aNo,
|
|
|
|
2,
|
|
|
|
0,
|
|
|
|
u_cmd,
|
|
|
|
'v',
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
'p',
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
g_cmd,
|
|
|
|
'w',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aAll,
|
|
|
|
0,
|
|
|
|
wordx,
|
|
|
|
w_cmd,
|
|
|
|
'x',
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
'p',
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
x_cmd,
|
|
|
|
'y',
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
'p',
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
x_cmd,
|
|
|
|
'=',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
linex,
|
|
|
|
eq_cmd,
|
|
|
|
'B',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aNo,
|
|
|
|
0,
|
|
|
|
linex,
|
|
|
|
B_cmd,
|
|
|
|
'D',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aNo,
|
|
|
|
0,
|
|
|
|
linex,
|
|
|
|
D_cmd,
|
|
|
|
'X',
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
'f',
|
|
|
|
aNo,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
X_cmd,
|
|
|
|
'Y',
|
|
|
|
0,
|
|
|
|
1,
|
|
|
|
0,
|
|
|
|
'f',
|
|
|
|
aNo,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
X_cmd,
|
|
|
|
'<',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
linex,
|
|
|
|
pipe_cmd,
|
|
|
|
'|',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
linex,
|
|
|
|
pipe_cmd,
|
|
|
|
'>',
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
aDot,
|
|
|
|
0,
|
|
|
|
linex,
|
|
|
|
pipe_cmd,
|
|
|
|
/* deliberately unimplemented:
|
|
|
|
'k', 0, 0, 0, 0, aDot, 0, 0, k_cmd,
|
|
|
|
'n', 0, 0, 0, 0, aNo, 0, 0, n_cmd,
|
|
|
|
'q', 0, 0, 0, 0, aNo, 0, 0, q_cmd,
|
|
|
|
'!', 0, 0, 0, 0, aNo, 0, linex, plan9_cmd,
|
|
|
|
*/
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0};
|
|
|
|
|
|
|
|
Cmd* parsecmd(int);
|
|
|
|
Addr* compoundaddr(void);
|
|
|
|
Addr* simpleaddr(void);
|
|
|
|
void freecmd(void);
|
|
|
|
void okdelim(int);
|
|
|
|
|
|
|
|
Rune* cmdstartp;
|
|
|
|
Rune* cmdendp;
|
|
|
|
Rune* cmdp;
|
|
|
|
Channel* editerrc;
|
|
|
|
|
|
|
|
String* lastpat;
|
|
|
|
int patset;
|
|
|
|
|
|
|
|
List cmdlist;
|
|
|
|
List addrlist;
|
|
|
|
List stringlist;
|
|
|
|
Text* curtext;
|
|
|
|
int editing = Inactive;
|
|
|
|
|
|
|
|
String* newstring(int);
|
|
|
|
|
|
|
|
void editthread(void* v) {
|
|
|
|
Cmd* cmdp;
|
|
|
|
|
|
|
|
USED(v);
|
|
|
|
threadsetname("editthread");
|
|
|
|
while ((cmdp = parsecmd(0)) != 0) {
|
|
|
|
if (cmdexec(curtext, cmdp) == 0)
|
|
|
|
break;
|
|
|
|
freecmd();
|
|
|
|
}
|
|
|
|
sendp(editerrc, nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
void allelogterm(Window* w, void* x) {
|
|
|
|
USED(x);
|
|
|
|
elogterm(w->body.file);
|
|
|
|
}
|
|
|
|
|
|
|
|
void alleditinit(Window* w, void* x) {
|
|
|
|
USED(x);
|
|
|
|
textcommit(&w->tag, TRUE);
|
|
|
|
textcommit(&w->body, TRUE);
|
|
|
|
w->body.file->editclean = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void allupdate(Window* w, void* x) {
|
|
|
|
Text* t;
|
|
|
|
int i;
|
|
|
|
File* f;
|
|
|
|
|
|
|
|
USED(x);
|
|
|
|
t = &w->body;
|
|
|
|
f = t->file;
|
|
|
|
if (f->curtext != t) /* do curtext only */
|
|
|
|
return;
|
|
|
|
if (f->elog.type == Null)
|
|
|
|
elogterm(f);
|
|
|
|
else if (f->elog.type != Empty) {
|
|
|
|
elogapply(f);
|
|
|
|
if (f->editclean) {
|
|
|
|
f->mod = FALSE;
|
|
|
|
for (i = 0; i < f->ntext; i++)
|
|
|
|
f->text[i]->w->dirty = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
textsetselect(t, t->q0, t->q1);
|
|
|
|
textscrdraw(t);
|
|
|
|
winsettag(w);
|
|
|
|
}
|
|
|
|
|
|
|
|
void editerror(char* fmt, ...) {
|
|
|
|
va_list arg;
|
|
|
|
char* s;
|
|
|
|
|
|
|
|
va_start(arg, fmt);
|
|
|
|
s = vsmprint(fmt, arg);
|
|
|
|
va_end(arg);
|
|
|
|
freecmd();
|
|
|
|
allwindows(allelogterm, nil); /* truncate the edit logs */
|
|
|
|
sendp(editerrc, s);
|
|
|
|
threadexits(nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
void editcmd(Text* ct, Rune* r, uint n) {
|
|
|
|
char* err;
|
|
|
|
|
|
|
|
if (n == 0)
|
|
|
|
return;
|
|
|
|
if (2 * n > RBUFSIZE) {
|
|
|
|
warning(nil, "string too long\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
allwindows(alleditinit, nil);
|
|
|
|
if (cmdstartp)
|
|
|
|
free(cmdstartp);
|
|
|
|
cmdstartp = runemalloc(n + 2);
|
|
|
|
runemove(cmdstartp, r, n);
|
|
|
|
if (r[n - 1] != '\n')
|
|
|
|
cmdstartp[n++] = '\n';
|
|
|
|
cmdstartp[n] = '\0';
|
|
|
|
cmdendp = cmdstartp + n;
|
|
|
|
cmdp = cmdstartp;
|
|
|
|
if (ct->w == nil)
|
|
|
|
curtext = nil;
|
|
|
|
else
|
|
|
|
curtext = &ct->w->body;
|
|
|
|
resetxec();
|
|
|
|
if (editerrc == nil) {
|
|
|
|
editerrc = chancreate(sizeof(char*), 0);
|
|
|
|
chansetname(editerrc, "editerrc");
|
|
|
|
lastpat = allocstring(0);
|
|
|
|
}
|
|
|
|
threadcreate(editthread, nil, STACK);
|
|
|
|
err = recvp(editerrc);
|
|
|
|
editing = Inactive;
|
|
|
|
if (err != nil) {
|
|
|
|
if (err[0] != '\0')
|
|
|
|
warning(nil, "Edit: %s\n", err);
|
|
|
|
free(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update everyone whose edit log has data */
|
|
|
|
allwindows(allupdate, nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
int getch(void) {
|
|
|
|
if (cmdp == cmdendp)
|
|
|
|
return -1;
|
|
|
|
return *cmdp++;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nextc(void) {
|
|
|
|
if (cmdp == cmdendp)
|
|
|
|
return -1;
|
|
|
|
return *cmdp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ungetch(void) {
|
|
|
|
if (--cmdp < cmdstartp)
|
|
|
|
error("ungetch");
|
|
|
|
}
|
|
|
|
|
|
|
|
long getnum(int signok) {
|
|
|
|
long n;
|
|
|
|
int c, sign;
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
sign = 1;
|
|
|
|
if (signok > 1 && nextc() == '-') {
|
|
|
|
sign = -1;
|
|
|
|
getch();
|
|
|
|
}
|
|
|
|
if ((c = nextc()) < '0' || '9' < c) /* no number defaults to 1 */
|
|
|
|
return sign;
|
|
|
|
while ('0' <= (c = getch()) && c <= '9')
|
|
|
|
n = n * 10 + (c - '0');
|
|
|
|
ungetch();
|
|
|
|
return sign * n;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cmdskipbl(void) {
|
|
|
|
int c;
|
|
|
|
do
|
|
|
|
c = getch();
|
|
|
|
while (c == ' ' || c == '\t');
|
|
|
|
if (c >= 0)
|
|
|
|
ungetch();
|
|
|
|
return c;
|
2019-11-14 23:15:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that list has room for one more element.
|
|
|
|
*/
|
2021-06-13 05:54:54 +00:00
|
|
|
void growlist(List* l) {
|
|
|
|
if (l->u.listptr == 0 || l->nalloc == 0) {
|
|
|
|
l->nalloc = INCR;
|
|
|
|
l->u.listptr = emalloc(INCR * sizeof(void*));
|
|
|
|
l->nused = 0;
|
|
|
|
} else if (l->nused == l->nalloc) {
|
|
|
|
l->u.listptr = erealloc(l->u.listptr, (l->nalloc + INCR) * sizeof(void*));
|
|
|
|
memset(l->u.ptr + l->nalloc, 0, INCR * sizeof(void*));
|
|
|
|
l->nalloc += INCR;
|
|
|
|
}
|
2019-11-14 23:15:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove the ith element from the list
|
|
|
|
*/
|
2021-06-13 05:54:54 +00:00
|
|
|
void dellist(List* l, int i) {
|
|
|
|
memmove(&l->u.ptr[i], &l->u.ptr[i + 1], (l->nused - (i + 1)) * sizeof(void*));
|
|
|
|
l->nused--;
|
2019-11-14 23:15:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a new element, whose position is i, to the list
|
|
|
|
*/
|
2021-06-13 05:54:54 +00:00
|
|
|
void inslist(List* l, int i, void* v) {
|
|
|
|
growlist(l);
|
|
|
|
memmove(&l->u.ptr[i + 1], &l->u.ptr[i], (l->nused - i) * sizeof(void*));
|
|
|
|
l->u.ptr[i] = v;
|
|
|
|
l->nused++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void listfree(List* l) {
|
|
|
|
free(l->u.listptr);
|
|
|
|
free(l);
|
|
|
|
}
|
|
|
|
|
|
|
|
String* allocstring(int n) {
|
|
|
|
String* s;
|
|
|
|
|
|
|
|
s = emalloc(sizeof(String));
|
|
|
|
s->n = n;
|
|
|
|
s->nalloc = n + 10;
|
|
|
|
s->r = emalloc(s->nalloc * sizeof(Rune));
|
|
|
|
s->r[n] = '\0';
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
void freestring(String* s) {
|
|
|
|
free(s->r);
|
|
|
|
free(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
Cmd* newcmd(void) {
|
|
|
|
Cmd* p;
|
|
|
|
|
|
|
|
p = emalloc(sizeof(Cmd));
|
|
|
|
inslist(&cmdlist, cmdlist.nused, p);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
String* newstring(int n) {
|
|
|
|
String* p;
|
2019-11-14 23:15:48 +00:00
|
|
|
|
2021-06-13 05:54:54 +00:00
|
|
|
p = allocstring(n);
|
|
|
|
inslist(&stringlist, stringlist.nused, p);
|
|
|
|
return p;
|
2019-11-14 23:15:48 +00:00
|
|
|
}
|
|
|
|
|
2021-06-13 05:54:54 +00:00
|
|
|
Addr* newaddr(void) {
|
|
|
|
Addr* p;
|
|
|
|
|
|
|
|
p = emalloc(sizeof(Addr));
|
|
|
|
inslist(&addrlist, addrlist.nused, p);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void freecmd(void) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
while (cmdlist.nused > 0)
|
|
|
|
free(cmdlist.u.ucharptr[--cmdlist.nused]);
|
|
|
|
while (addrlist.nused > 0)
|
|
|
|
free(addrlist.u.ucharptr[--addrlist.nused]);
|
|
|
|
while (stringlist.nused > 0) {
|
|
|
|
i = --stringlist.nused;
|
|
|
|
freestring(stringlist.u.stringptr[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void okdelim(int c) {
|
|
|
|
if (
|
|
|
|
c == '\\' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') ||
|
|
|
|
('0' <= c && c <= '9'))
|
|
|
|
editerror("bad delimiter %c\n", c);
|
|
|
|
}
|
|
|
|
|
|
|
|
void atnl(void) {
|
|
|
|
int c;
|
|
|
|
|
|
|
|
cmdskipbl();
|
|
|
|
c = getch();
|
|
|
|
if (c != '\n')
|
|
|
|
editerror("newline expected (saw %C)", c);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Straddc(String* s, int c) {
|
|
|
|
if (s->n + 1 >= s->nalloc) {
|
|
|
|
s->nalloc += 10;
|
|
|
|
s->r = erealloc(s->r, s->nalloc * sizeof(Rune));
|
|
|
|
}
|
|
|
|
s->r[s->n++] = c;
|
|
|
|
s->r[s->n] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
void getrhs(String* s, int delim, int cmd) {
|
|
|
|
int c;
|
|
|
|
|
|
|
|
while ((c = getch()) > 0 && c != delim && c != '\n') {
|
|
|
|
if (c == '\\') {
|
|
|
|
if ((c = getch()) <= 0)
|
|
|
|
error("bad right hand side");
|
|
|
|
if (c == '\n') {
|
|
|
|
ungetch();
|
|
|
|
c = '\\';
|
|
|
|
} else if (c == 'n')
|
|
|
|
c = '\n';
|
|
|
|
else if (c != delim && (cmd == 's' || c != '\\')) /* s does its own */
|
|
|
|
Straddc(s, '\\');
|
|
|
|
}
|
|
|
|
Straddc(s, c);
|
|
|
|
}
|
|
|
|
ungetch(); /* let client read whether delimiter, '\n' or whatever */
|
|
|
|
}
|
|
|
|
|
|
|
|
String* collecttoken(char* end) {
|
|
|
|
String* s = newstring(0);
|
|
|
|
int c;
|
|
|
|
|
|
|
|
while ((c = nextc()) == ' ' || c == '\t')
|
|
|
|
Straddc(s, getch()); /* blanks significant for getname() */
|
|
|
|
while ((c = getch()) > 0 && utfrune(end, c) == 0)
|
|
|
|
Straddc(s, c);
|
|
|
|
if (c != '\n')
|
|
|
|
atnl();
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
String* collecttext(void) {
|
|
|
|
String* s;
|
|
|
|
int begline, i, c, delim;
|
|
|
|
|
|
|
|
s = newstring(0);
|
|
|
|
if (cmdskipbl() == '\n') {
|
|
|
|
getch();
|
|
|
|
i = 0;
|
|
|
|
do {
|
|
|
|
begline = i;
|
|
|
|
while ((c = getch()) > 0 && c != '\n')
|
|
|
|
i++, Straddc(s, c);
|
|
|
|
i++, Straddc(s, '\n');
|
|
|
|
if (c < 0)
|
|
|
|
goto Return;
|
|
|
|
} while (s->r[begline] != '.' || s->r[begline + 1] != '\n');
|
|
|
|
s->r[s->n - 2] = '\0';
|
|
|
|
s->n -= 2;
|
|
|
|
} else {
|
|
|
|
okdelim(delim = getch());
|
|
|
|
getrhs(s, delim, 'a');
|
|
|
|
if (nextc() == delim)
|
|
|
|
getch();
|
|
|
|
atnl();
|
|
|
|
}
|
|
|
|
Return:
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cmdlookup(int c) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; cmdtab[i].cmdc; i++)
|
|
|
|
if (cmdtab[i].cmdc == c)
|
|
|
|
return i;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
Cmd* parsecmd(int nest) {
|
|
|
|
int i, c;
|
|
|
|
struct cmdtab* ct;
|
|
|
|
Cmd *cp, *ncp;
|
|
|
|
Cmd cmd;
|
|
|
|
|
|
|
|
cmd.next = cmd.u.cmd = 0;
|
|
|
|
cmd.re = 0;
|
|
|
|
cmd.flag = cmd.num = 0;
|
|
|
|
cmd.addr = compoundaddr();
|
|
|
|
if (cmdskipbl() == -1)
|
|
|
|
return 0;
|
|
|
|
if ((c = getch()) == -1)
|
|
|
|
return 0;
|
|
|
|
cmd.cmdc = c;
|
|
|
|
if (cmd.cmdc == 'c' && nextc() == 'd') { /* sleazy two-character case */
|
|
|
|
getch(); /* the 'd' */
|
|
|
|
cmd.cmdc = 'c' | 0x100;
|
|
|
|
}
|
|
|
|
i = cmdlookup(cmd.cmdc);
|
|
|
|
if (i >= 0) {
|
|
|
|
if (cmd.cmdc == '\n')
|
|
|
|
goto Return; /* let nl_cmd work it all out */
|
|
|
|
ct = &cmdtab[i];
|
|
|
|
if (ct->defaddr == aNo && cmd.addr)
|
|
|
|
editerror("command takes no address");
|
|
|
|
if (ct->count)
|
|
|
|
cmd.num = getnum(ct->count);
|
|
|
|
if (ct->regexp) {
|
|
|
|
/* x without pattern -> .*\n, indicated by cmd.re==0 */
|
|
|
|
/* X without pattern is all files */
|
|
|
|
if (
|
|
|
|
(ct->cmdc != 'x' && ct->cmdc != 'X') ||
|
|
|
|
((c = nextc()) != ' ' && c != '\t' && c != '\n')) {
|
|
|
|
cmdskipbl();
|
|
|
|
if ((c = getch()) == '\n' || c < 0)
|
|
|
|
editerror("no address");
|
|
|
|
okdelim(c);
|
|
|
|
cmd.re = getregexp(c);
|
|
|
|
if (ct->cmdc == 's') {
|
|
|
|
cmd.u.text = newstring(0);
|
|
|
|
getrhs(cmd.u.text, c, 's');
|
|
|
|
if (nextc() == c) {
|
|
|
|
getch();
|
|
|
|
if (nextc() == 'g')
|
|
|
|
cmd.flag = getch();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ct->addr && (cmd.u.mtaddr = simpleaddr()) == 0)
|
|
|
|
editerror("bad address");
|
|
|
|
if (ct->defcmd) {
|
|
|
|
if (cmdskipbl() == '\n') {
|
|
|
|
getch();
|
|
|
|
cmd.u.cmd = newcmd();
|
|
|
|
cmd.u.cmd->cmdc = ct->defcmd;
|
|
|
|
} else if ((cmd.u.cmd = parsecmd(nest)) == 0)
|
|
|
|
error("defcmd");
|
|
|
|
} else if (ct->text)
|
|
|
|
cmd.u.text = collecttext();
|
|
|
|
else if (ct->token)
|
|
|
|
cmd.u.text = collecttoken(ct->token);
|
|
|
|
else
|
|
|
|
atnl();
|
|
|
|
} else
|
|
|
|
switch (cmd.cmdc) {
|
|
|
|
case '{':
|
|
|
|
cp = 0;
|
|
|
|
do {
|
|
|
|
if (cmdskipbl() == '\n')
|
|
|
|
getch();
|
|
|
|
ncp = parsecmd(nest + 1);
|
|
|
|
if (cp)
|
|
|
|
cp->next = ncp;
|
|
|
|
else
|
|
|
|
cmd.u.cmd = ncp;
|
|
|
|
} while (cp = ncp);
|
|
|
|
break;
|
|
|
|
case '}':
|
|
|
|
atnl();
|
|
|
|
if (nest == 0)
|
|
|
|
editerror("right brace with no left brace");
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
editerror("unknown command %c", cmd.cmdc);
|
|
|
|
}
|
|
|
|
Return:
|
|
|
|
cp = newcmd();
|
|
|
|
*cp = cmd;
|
|
|
|
return cp;
|
|
|
|
}
|
|
|
|
|
|
|
|
String* getregexp(int delim) {
|
|
|
|
String *buf, *r;
|
|
|
|
int i, c;
|
|
|
|
|
|
|
|
buf = allocstring(0);
|
|
|
|
for (i = 0;; i++) {
|
|
|
|
if ((c = getch()) == '\\') {
|
|
|
|
if (nextc() == delim)
|
|
|
|
c = getch();
|
|
|
|
else if (nextc() == '\\') {
|
|
|
|
Straddc(buf, c);
|
|
|
|
c = getch();
|
|
|
|
}
|
|
|
|
} else if (c == delim || c == '\n')
|
|
|
|
break;
|
|
|
|
if (i >= RBUFSIZE)
|
|
|
|
editerror("regular expression too long");
|
|
|
|
Straddc(buf, c);
|
|
|
|
}
|
|
|
|
if (c != delim && c)
|
|
|
|
ungetch();
|
|
|
|
if (buf->n > 0) {
|
|
|
|
patset = TRUE;
|
|
|
|
freestring(lastpat);
|
|
|
|
lastpat = buf;
|
|
|
|
} else
|
|
|
|
freestring(buf);
|
|
|
|
if (lastpat->n == 0)
|
|
|
|
editerror("no regular expression defined");
|
|
|
|
r = newstring(lastpat->n);
|
|
|
|
runemove(r->r, lastpat->r, lastpat->n); /* newstring put \0 at end */
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
Addr* simpleaddr(void) {
|
|
|
|
Addr addr;
|
|
|
|
Addr *ap, *nap;
|
|
|
|
|
|
|
|
addr.num = 0;
|
|
|
|
addr.next = 0;
|
|
|
|
addr.u.left = 0;
|
|
|
|
switch (cmdskipbl()) {
|
|
|
|
case '#':
|
|
|
|
addr.type = getch();
|
|
|
|
addr.num = getnum(1);
|
|
|
|
break;
|
|
|
|
case '0':
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
case '5':
|
|
|
|
case '6':
|
|
|
|
case '7':
|
|
|
|
case '8':
|
|
|
|
case '9':
|
|
|
|
addr.num = getnum(1);
|
|
|
|
addr.type = 'l';
|
|
|
|
break;
|
|
|
|
case '/':
|
|
|
|
case '?':
|
|
|
|
case '"':
|
|
|
|
addr.u.re = getregexp(addr.type = getch());
|
|
|
|
break;
|
|
|
|
case '.':
|
|
|
|
case '$':
|
|
|
|
case '+':
|
|
|
|
case '-':
|
|
|
|
case '\'':
|
|
|
|
addr.type = getch();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (addr.next = simpleaddr())
|
|
|
|
switch (addr.next->type) {
|
|
|
|
case '.':
|
|
|
|
case '$':
|
|
|
|
case '\'':
|
|
|
|
if (addr.type != '"')
|
|
|
|
case '"':
|
|
|
|
editerror("bad address syntax");
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
case '#':
|
|
|
|
if (addr.type == '"')
|
|
|
|
break;
|
|
|
|
/* fall through */
|
|
|
|
case '/':
|
|
|
|
case '?':
|
|
|
|
if (addr.type != '+' && addr.type != '-') {
|
|
|
|
/* insert the missing '+' */
|
|
|
|
nap = newaddr();
|
|
|
|
nap->type = '+';
|
|
|
|
nap->next = addr.next;
|
|
|
|
addr.next = nap;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '+':
|
|
|
|
case '-':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error("simpleaddr");
|
|
|
|
}
|
|
|
|
ap = newaddr();
|
|
|
|
*ap = addr;
|
|
|
|
return ap;
|
|
|
|
}
|
|
|
|
|
|
|
|
Addr* compoundaddr(void) {
|
|
|
|
Addr addr;
|
|
|
|
Addr *ap, *next;
|
|
|
|
|
|
|
|
addr.u.left = simpleaddr();
|
|
|
|
if ((addr.type = cmdskipbl()) != ',' && addr.type != ';')
|
|
|
|
return addr.u.left;
|
|
|
|
getch();
|
|
|
|
next = addr.next = compoundaddr();
|
|
|
|
if (next && (next->type == ',' || next->type == ';') && next->u.left == 0)
|
|
|
|
editerror("bad address syntax");
|
|
|
|
ap = newaddr();
|
|
|
|
*ap = addr;
|
|
|
|
return ap;
|
2019-11-14 23:15:48 +00:00
|
|
|
}
|