544 lines
8.4 KiB
C
544 lines
8.4 KiB
C
/*
|
|
* 7. Macros, strings, diversion, and position traps.
|
|
*
|
|
* macros can override builtins
|
|
* builtins can be renamed or removed!
|
|
*/
|
|
|
|
#include "a.h"
|
|
|
|
enum
|
|
{
|
|
MAXARG = 10,
|
|
MAXMSTACK = 40
|
|
};
|
|
|
|
/* macro invocation frame */
|
|
typedef struct Mac Mac;
|
|
struct Mac
|
|
{
|
|
int argc;
|
|
Rune *argv[MAXARG];
|
|
};
|
|
|
|
Mac mstack[MAXMSTACK];
|
|
int nmstack;
|
|
void emitdi(void);
|
|
void flushdi(void);
|
|
|
|
/*
|
|
* Run a user-defined macro.
|
|
*/
|
|
void popmacro(void);
|
|
int
|
|
runmacro(int dot, int argc, Rune **argv)
|
|
{
|
|
Rune *p;
|
|
int i;
|
|
Mac *m;
|
|
|
|
if(verbose && isupperrune(argv[0][0])) fprint(2, "run: %S\n", argv[0]);
|
|
p = getds(argv[0]);
|
|
if(p == nil){
|
|
if(verbose)
|
|
warn("ignoring unknown request %C%S", dot, argv[0]);
|
|
if(verbose > 1){
|
|
for(i=0; i<argc; i++)
|
|
fprint(2, " %S", argv[i]);
|
|
fprint(2, "\n");
|
|
}
|
|
return -1;
|
|
}
|
|
if(nmstack >= nelem(mstack)){
|
|
fprint(2, "%L: macro stack overflow:");
|
|
for(i=0; i<nmstack; i++)
|
|
fprint(2, " %S", mstack[i].argv[0]);
|
|
fprint(2, "\n");
|
|
return -1;
|
|
}
|
|
m = &mstack[nmstack++];
|
|
m->argc = argc;
|
|
for(i=0; i<argc; i++)
|
|
m->argv[i] = erunestrdup(argv[i]);
|
|
pushinputstring(p);
|
|
nr(L(".$"), argc-1);
|
|
inputnotify(popmacro);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
popmacro(void)
|
|
{
|
|
int i;
|
|
Mac *m;
|
|
|
|
if(--nmstack < 0){
|
|
fprint(2, "%L: macro stack underflow\n");
|
|
return;
|
|
}
|
|
m = &mstack[nmstack];
|
|
for(i=0; i<m->argc; i++)
|
|
free(m->argv[i]);
|
|
if(nmstack > 0)
|
|
nr(L(".$"), mstack[nmstack-1].argc-1);
|
|
else
|
|
nr(L(".$"), 0);
|
|
}
|
|
|
|
void popmacro1(void);
|
|
jmp_buf runjb[10];
|
|
int nrunjb;
|
|
|
|
void
|
|
runmacro1(Rune *name)
|
|
{
|
|
Rune *argv[2];
|
|
int obol;
|
|
|
|
if(verbose) fprint(2, "outcb %p\n", outcb);
|
|
obol = bol;
|
|
argv[0] = name;
|
|
argv[1] = nil;
|
|
bol = 1;
|
|
if(runmacro('.', 1, argv) >= 0){
|
|
inputnotify(popmacro1);
|
|
if(!setjmp(runjb[nrunjb++]))
|
|
runinput();
|
|
else
|
|
if(verbose) fprint(2, "finished %S\n", name);
|
|
}
|
|
bol = obol;
|
|
}
|
|
|
|
void
|
|
popmacro1(void)
|
|
{
|
|
popmacro();
|
|
if(nrunjb >= 0)
|
|
longjmp(runjb[--nrunjb], 1);
|
|
}
|
|
|
|
/*
|
|
* macro arguments
|
|
*
|
|
* "" means " inside " "
|
|
* "" empty string
|
|
* \newline can be done
|
|
* argument separator is space (not tab)
|
|
* number register .$ = number of arguments
|
|
* no arguments outside macros or in strings
|
|
*
|
|
* arguments copied in copy mode
|
|
*/
|
|
|
|
/*
|
|
* diversions
|
|
*
|
|
* processed output diverted
|
|
* dn dl registers vertical and horizontal size of last diversion
|
|
* .z - current diversion name
|
|
*/
|
|
|
|
/*
|
|
* traps
|
|
*
|
|
* skip most
|
|
* .t register - distance to next trap
|
|
*/
|
|
static Rune *trap0;
|
|
|
|
void
|
|
outtrap(void)
|
|
{
|
|
Rune *t;
|
|
|
|
if(outcb)
|
|
return;
|
|
if(trap0){
|
|
if(verbose) fprint(2, "trap: %S\n", trap0);
|
|
t = trap0;
|
|
trap0 = nil;
|
|
runmacro1(t);
|
|
free(t);
|
|
}
|
|
}
|
|
|
|
/* .wh - install trap */
|
|
void
|
|
r_wh(int argc, Rune **argv)
|
|
{
|
|
int i;
|
|
|
|
if(argc < 2)
|
|
return;
|
|
|
|
i = eval(argv[1]);
|
|
if(argc == 2){
|
|
if(i == 0){
|
|
free(trap0);
|
|
trap0 = nil;
|
|
}else
|
|
if(verbose)
|
|
warn("not removing trap at %d", i);
|
|
}
|
|
if(argc > 2){
|
|
if(i == 0){
|
|
free(trap0);
|
|
trap0 = erunestrdup(argv[2]);
|
|
}else
|
|
if(verbose)
|
|
warn("not installing %S trap at %d", argv[2], i);
|
|
}
|
|
}
|
|
|
|
void
|
|
r_ch(int argc, Rune **argv)
|
|
{
|
|
int i;
|
|
|
|
if(argc == 2){
|
|
if(trap0 && runestrcmp(argv[1], trap0) == 0){
|
|
free(trap0);
|
|
trap0 = nil;
|
|
}else
|
|
if(verbose)
|
|
warn("not removing %S trap", argv[1]);
|
|
return;
|
|
}
|
|
if(argc >= 3){
|
|
i = eval(argv[2]);
|
|
if(i == 0){
|
|
free(trap0);
|
|
trap0 = erunestrdup(argv[1]);
|
|
}else
|
|
if(verbose)
|
|
warn("not moving %S trap to %d", argv[1], i);
|
|
}
|
|
}
|
|
|
|
void
|
|
r_dt(int argc, Rune **argv)
|
|
{
|
|
USED(argc);
|
|
USED(argv);
|
|
warn("ignoring diversion trap");
|
|
}
|
|
|
|
/* define macro - .de, .am, .ig */
|
|
void
|
|
r_de(int argc, Rune **argv)
|
|
{
|
|
Rune *end, *p;
|
|
Fmt fmt;
|
|
int ignore, len;
|
|
|
|
delreq(argv[1]);
|
|
delraw(argv[1]);
|
|
ignore = runestrcmp(argv[0], L("ig")) == 0;
|
|
if(!ignore)
|
|
runefmtstrinit(&fmt);
|
|
end = L("..");
|
|
if(argc >= 3)
|
|
end = argv[2];
|
|
if(runestrcmp(argv[0], L("am")) == 0 && (p=getds(argv[1])) != nil)
|
|
fmtrunestrcpy(&fmt, p);
|
|
len = runestrlen(end);
|
|
while((p = readline(CopyMode)) != nil){
|
|
if(runestrncmp(p, end, len) == 0
|
|
&& (p[len]==' ' || p[len]==0 || p[len]=='\t'
|
|
|| (p[len]=='\\' && p[len+1]=='}'))){
|
|
free(p);
|
|
goto done;
|
|
}
|
|
if(!ignore)
|
|
fmtprint(&fmt, "%S\n", p);
|
|
free(p);
|
|
}
|
|
warn("eof in %C%S %S - looking for %#Q", dot, argv[0], argv[1], end);
|
|
done:
|
|
if(ignore)
|
|
return;
|
|
p = runefmtstrflush(&fmt);
|
|
if(p == nil)
|
|
sysfatal("out of memory");
|
|
ds(argv[1], p);
|
|
free(p);
|
|
}
|
|
|
|
/* define string .ds .as */
|
|
void
|
|
r_ds(Rune *cmd)
|
|
{
|
|
Rune *name, *line, *p;
|
|
|
|
name = copyarg();
|
|
line = readline(CopyMode);
|
|
if(name == nil || line == nil){
|
|
free(name);
|
|
return;
|
|
}
|
|
p = line;
|
|
if(*p == '"')
|
|
p++;
|
|
if(cmd[0] == 'd')
|
|
ds(name, p);
|
|
else
|
|
as(name, p);
|
|
free(name);
|
|
free(line);
|
|
}
|
|
|
|
/* remove request, macro, or string */
|
|
void
|
|
r_rm(int argc, Rune **argv)
|
|
{
|
|
int i;
|
|
|
|
emitdi();
|
|
for(i=1; i<argc; i++){
|
|
delreq(argv[i]);
|
|
delraw(argv[i]);
|
|
ds(argv[i], nil);
|
|
}
|
|
}
|
|
|
|
/* .rn - rename request, macro, or string */
|
|
void
|
|
r_rn(int argc, Rune **argv)
|
|
{
|
|
USED(argc);
|
|
renreq(argv[1], argv[2]);
|
|
renraw(argv[1], argv[2]);
|
|
ds(argv[2], getds(argv[1]));
|
|
ds(argv[1], nil);
|
|
}
|
|
|
|
/* .di - divert output to macro xx */
|
|
/* .da - divert, appending to macro */
|
|
/* page offsetting is not done! */
|
|
Fmt difmt;
|
|
int difmtinit;
|
|
Rune di[20][100];
|
|
int ndi;
|
|
|
|
void
|
|
emitdi(void)
|
|
{
|
|
flushdi();
|
|
runefmtstrinit(&difmt);
|
|
difmtinit = 1;
|
|
fmtrune(&difmt, Uformatted);
|
|
}
|
|
|
|
void
|
|
flushdi(void)
|
|
{
|
|
int n;
|
|
Rune *p;
|
|
|
|
if(ndi == 0 || difmtinit == 0)
|
|
return;
|
|
fmtrune(&difmt, Uunformatted);
|
|
p = runefmtstrflush(&difmt);
|
|
memset(&difmt, 0, sizeof difmt);
|
|
difmtinit = 0;
|
|
if(p == nil)
|
|
warn("out of memory in diversion %C%S", dot, di[ndi-1]);
|
|
else{
|
|
n = runestrlen(p);
|
|
if(n > 0 && p[n-1] != '\n'){
|
|
p = runerealloc(p, n+2);
|
|
p[n] = '\n';
|
|
p[n+1] = 0;
|
|
}
|
|
}
|
|
as(di[ndi-1], p);
|
|
free(p);
|
|
}
|
|
|
|
void
|
|
outdi(Rune r)
|
|
{
|
|
if(!difmtinit) abort();
|
|
if(r == Uempty)
|
|
return;
|
|
fmtrune(&difmt, r);
|
|
}
|
|
|
|
/* .di, .da */
|
|
void
|
|
r_di(int argc, Rune **argv)
|
|
{
|
|
br();
|
|
if(argc > 2)
|
|
warn("extra arguments to %C%S", dot, argv[0]);
|
|
if(argc == 1){
|
|
/* end diversion */
|
|
if(ndi <= 0){
|
|
// warn("unmatched %C%S", dot, argv[0]);
|
|
return;
|
|
}
|
|
flushdi();
|
|
if(--ndi == 0){
|
|
_nr(L(".z"), nil);
|
|
outcb = nil;
|
|
}else{
|
|
_nr(L(".z"), di[ndi-1]);
|
|
runefmtstrinit(&difmt);
|
|
fmtrune(&difmt, Uformatted);
|
|
difmtinit = 1;
|
|
}
|
|
return;
|
|
}
|
|
/* start diversion */
|
|
/* various register state should be saved, but it's all useless to us */
|
|
flushdi();
|
|
if(ndi >= nelem(di))
|
|
sysfatal("%Cdi overflow", dot);
|
|
if(argv[0][1] == 'i')
|
|
ds(argv[1], nil);
|
|
_nr(L(".z"), argv[1]);
|
|
runestrcpy(di[ndi++], argv[1]);
|
|
runefmtstrinit(&difmt);
|
|
fmtrune(&difmt, Uformatted);
|
|
difmtinit = 1;
|
|
outcb = outdi;
|
|
}
|
|
|
|
/* .wh - install trap */
|
|
/* .ch - change trap */
|
|
/* .dt - install diversion trap */
|
|
|
|
/* set input-line count trap */
|
|
int itrapcount;
|
|
int itrapwaiting;
|
|
Rune *itrapname;
|
|
|
|
void
|
|
r_it(int argc, Rune **argv)
|
|
{
|
|
if(argc < 3){
|
|
itrapcount = 0;
|
|
return;
|
|
}
|
|
itrapcount = eval(argv[1]);
|
|
free(itrapname);
|
|
itrapname = erunestrdup(argv[2]);
|
|
}
|
|
|
|
void
|
|
itrap(void)
|
|
{
|
|
itrapset();
|
|
if(itrapwaiting){
|
|
itrapwaiting = 0;
|
|
runmacro1(itrapname);
|
|
}
|
|
}
|
|
|
|
void
|
|
itrapset(void)
|
|
{
|
|
if(itrapcount > 0 && --itrapcount == 0)
|
|
itrapwaiting = 1;
|
|
}
|
|
|
|
/* .em - invoke macro when all input is over */
|
|
void
|
|
r_em(int argc, Rune **argv)
|
|
{
|
|
Rune buf[20];
|
|
|
|
USED(argc);
|
|
runesnprint(buf, nelem(buf), ".%S\n", argv[1]);
|
|
as(L("eof"), buf);
|
|
}
|
|
|
|
int
|
|
e_star(void)
|
|
{
|
|
Rune *p;
|
|
|
|
p = getds(getname());
|
|
if(p)
|
|
pushinputstring(p);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
e_t(void)
|
|
{
|
|
if(inputmode&CopyMode)
|
|
return '\t';
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
e_a(void)
|
|
{
|
|
if(inputmode&CopyMode)
|
|
return '\a';
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
e_backslash(void)
|
|
{
|
|
if(inputmode&ArgMode)
|
|
ungetrune('\\');
|
|
return backslash;
|
|
}
|
|
|
|
int
|
|
e_dot(void)
|
|
{
|
|
return '.';
|
|
}
|
|
|
|
int
|
|
e_dollar(void)
|
|
{
|
|
int c;
|
|
|
|
c = getnext();
|
|
if(c < '1' || c > '9'){
|
|
ungetnext(c);
|
|
return 0;
|
|
}
|
|
c -= '0';
|
|
if(nmstack <= 0 || mstack[nmstack-1].argc <= c)
|
|
return 0;
|
|
pushinputstring(mstack[nmstack-1].argv[c]);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
t7init(void)
|
|
{
|
|
addreq(L("de"), r_de, -1);
|
|
addreq(L("am"), r_de, -1);
|
|
addreq(L("ig"), r_de, -1);
|
|
addraw(L("ds"), r_ds);
|
|
addraw(L("as"), r_ds);
|
|
addreq(L("rm"), r_rm, -1);
|
|
addreq(L("rn"), r_rn, -1);
|
|
addreq(L("di"), r_di, -1);
|
|
addreq(L("da"), r_di, -1);
|
|
addreq(L("it"), r_it, -1);
|
|
addreq(L("em"), r_em, 1);
|
|
addreq(L("wh"), r_wh, -1);
|
|
addreq(L("ch"), r_ch, -1);
|
|
addreq(L("dt"), r_dt, -1);
|
|
|
|
addesc('$', e_dollar, CopyMode|ArgMode|HtmlMode);
|
|
addesc('*', e_star, CopyMode|ArgMode|HtmlMode);
|
|
addesc('t', e_t, CopyMode|ArgMode);
|
|
addesc('a', e_a, CopyMode|ArgMode);
|
|
addesc('\\', e_backslash, ArgMode|CopyMode);
|
|
addesc('.', e_dot, CopyMode|ArgMode);
|
|
|
|
ds(L("eof"), L(".sp 0.5i\n"));
|
|
ds(L(".."), L(""));
|
|
}
|
|
|