430 lines
11 KiB
C
430 lines
11 KiB
C
/* $Revision: 1.15 $
|
|
*/
|
|
|
|
/* Scheme primitives that define and query event procedures, and
|
|
* functions to lookup and execute events.
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include "unroff.h"
|
|
|
|
#define NUM_VEC_EVENTS 6
|
|
|
|
static Table *requests, *numregs, *specials, *escapes, *chars, *others;
|
|
static Table *fallbacks;
|
|
static Object Key_Equation, Key_Sentence;
|
|
Object eventsvec;
|
|
|
|
static SYMDESCR event_syms[] = {
|
|
{ "line", EV_LINE },
|
|
{ "prolog", EV_PROLOG },
|
|
{ "epilog", EV_EPILOG },
|
|
{ "option", EV_OPTION },
|
|
{ "start", EV_START },
|
|
{ "exit", EV_EXIT },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
static char *event_names[] = {
|
|
"line event",
|
|
"prolog event",
|
|
"epilog event",
|
|
"option event",
|
|
"start event",
|
|
"exit event",
|
|
"request",
|
|
"macro",
|
|
"string",
|
|
"number register",
|
|
"special character",
|
|
"inline equation",
|
|
"sentence event",
|
|
"escape sequence",
|
|
"character event",
|
|
};
|
|
|
|
int debug_flag = 0;
|
|
|
|
void debug_printf(const char *format, ...) {
|
|
va_list ap;
|
|
|
|
if (!debug_flag) return;
|
|
va_start(ap, format);
|
|
vfprintf(stderr, format, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
void debug_string(const char *bytes, int nbytes) {
|
|
char *string;
|
|
|
|
if (!debug_flag) return;
|
|
string = safe_malloc(nbytes + 1);
|
|
memcpy(string, bytes, nbytes);
|
|
string[nbytes] = '\0';
|
|
debug_printf("%s", string);
|
|
free(string);
|
|
}
|
|
|
|
static Object make_event_object(Object x) {
|
|
switch (TYPE(x)) {
|
|
case T_Null:
|
|
case T_Compound:
|
|
case T_String:
|
|
case T_Character:
|
|
case T_Symbol:
|
|
return x;
|
|
case T_Primitive:
|
|
Primitive_Error("event function must be a compound procedure");
|
|
default:
|
|
if (EQ(x, False))
|
|
return x;
|
|
Primitive_Error("invalid event value argument");
|
|
}
|
|
}
|
|
|
|
static Object store_event(Table *tp, char *key, int size, Object obj,
|
|
char flags) {
|
|
Elem *oldp;
|
|
Object ret = False;
|
|
|
|
debug_printf("store_event ");
|
|
debug_string(key, size);
|
|
debug_printf("\n");
|
|
if ((oldp = table_lookup(tp, key, size)) != 0)
|
|
ret = get_object(oldp->obj);
|
|
if (!Nullp(obj)) {
|
|
if (EQ(obj, False))
|
|
table_remove(tp, key, size);
|
|
else
|
|
table_store(tp, key, size, obj, (unsigned long)flags);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static Object def_indexed(Table *tp, Object key, Object obj, char flags,
|
|
char *code) {
|
|
if (TYPE(key) == T_Symbol)
|
|
key = SYMBOL(key)->name;
|
|
else if (TYPE(key) != T_String)
|
|
Wrong_Type_Combination(key, "string or symbol");
|
|
if (STRING(key)->size == 0) {
|
|
if (*code != 'r' && *code != 'p')
|
|
Primitive_Error("event key must be of non-zero length");
|
|
return store_event(fallbacks, code, 1, make_event_object(obj), flags);
|
|
}
|
|
return store_event(tp, STRING(key)->data, STRING(key)->size,
|
|
make_event_object(obj), flags);
|
|
}
|
|
|
|
static Object p_defrequest(Object key, Object obj) {
|
|
return def_indexed(requests, key, obj, 0, "r");
|
|
}
|
|
|
|
static Object p_defmacro(Object key, Object obj) {
|
|
return def_indexed(requests, key, obj, RQ_MACRO, "m");
|
|
}
|
|
|
|
static Object p_defstring(Object key, Object obj) {
|
|
return def_indexed(requests, key, obj, RQ_STRING, "s");
|
|
}
|
|
|
|
static Object p_defnumreg(Object key, Object obj) {
|
|
return def_indexed(numregs, key, obj, 0, "n");
|
|
}
|
|
|
|
static Object p_defspecial(Object key, Object obj) {
|
|
return def_indexed(specials, key, obj, 0, "p");
|
|
}
|
|
|
|
static Object p_defequation(Object obj) {
|
|
return def_indexed(others, Key_Equation, obj, 0, 0);
|
|
}
|
|
|
|
static Object p_defsentence(Object obj) {
|
|
return def_indexed(others, Key_Sentence, obj, 0, 0);
|
|
}
|
|
|
|
static Object p_requestdef(Object key) {
|
|
return def_indexed(requests, key, Null, 0, "r");
|
|
}
|
|
|
|
static Object p_macrodef(Object key) {
|
|
return def_indexed(requests, key, Null, 0, "m");
|
|
}
|
|
|
|
static Object p_stringdef(Object key) {
|
|
return def_indexed(requests, key, Null, 0, "s");
|
|
}
|
|
|
|
static Object p_numregdef(Object key) {
|
|
return def_indexed(numregs, key, Null, 0, "n");
|
|
}
|
|
|
|
static Object p_specialdef(Object key) {
|
|
return def_indexed(specials, key, Null, 0, "p");
|
|
}
|
|
|
|
static Object p_equationdef(void) {
|
|
return def_indexed(others, Key_Equation, Null, 0, 0);
|
|
}
|
|
|
|
static Object p_sentencedef(void) {
|
|
return def_indexed(others, Key_Sentence, Null, 0, 0);
|
|
}
|
|
|
|
static Object def_char_event(Table *tp, Object key, Object obj) {
|
|
char c;
|
|
|
|
switch (TYPE(key)) {
|
|
case T_Character:
|
|
c = CHAR(key); break;
|
|
case T_Symbol:
|
|
key = SYMBOL(key)->name;
|
|
/* fall through */
|
|
case T_String:
|
|
if (tp == escapes && STRING(key)->size == 0)
|
|
return store_event(fallbacks, "e", 1, make_event_object(obj), 0);
|
|
if (STRING(key)->size != 1)
|
|
goto err;
|
|
c = STRING(key)->data[0];
|
|
break;
|
|
default: err:
|
|
Primitive_Error("cannot coerce argument to character");
|
|
}
|
|
return store_event(tp, &c, 1, make_event_object(obj), 0);
|
|
}
|
|
|
|
Object p_defescape(Object key, Object obj) {
|
|
return def_char_event(escapes, key, obj);
|
|
}
|
|
|
|
Object p_defchar(Object key, Object obj) {
|
|
return def_char_event(chars, key, obj);
|
|
}
|
|
|
|
Object p_escapedef(Object key) {
|
|
return def_char_event(escapes, key, Null);
|
|
}
|
|
|
|
Object p_chardef(Object key) {
|
|
return def_char_event(chars, key, Null);
|
|
}
|
|
|
|
const char *event_name(Event e) {
|
|
switch (e) {
|
|
#define ENAME(x) case x: return #x;
|
|
ENAME(EV_REQUEST);
|
|
ENAME(EV_MACRO);
|
|
ENAME(EV_STRING);
|
|
ENAME(EV_NUMREG);
|
|
ENAME(EV_SPECIAL);
|
|
ENAME(EV_ESCAPE);
|
|
ENAME(EV_CHAR);
|
|
ENAME(EV_EQUATION);
|
|
ENAME(EV_SENTENCE);
|
|
ENAME(EV_LINE);
|
|
ENAME(EV_PROLOG);
|
|
ENAME(EV_EPILOG);
|
|
ENAME(EV_OPTION);
|
|
ENAME(EV_START);
|
|
ENAME(EV_EXIT);
|
|
#undef ENAME
|
|
}
|
|
return "???";
|
|
}
|
|
|
|
Elem *event_lookup(Event e, char *key, int size) {
|
|
Elem *p;
|
|
|
|
debug_printf("event_lookup %s ", event_name(e));
|
|
debug_string(key, size);
|
|
debug_printf("\n");
|
|
if (key && size == 0) {
|
|
switch(e) {
|
|
case EV_REQUEST:
|
|
return table_lookup(fallbacks, "r", 1); /* not yet */
|
|
case EV_MACRO:
|
|
return table_lookup(fallbacks, "m", 1); /* not yet */
|
|
case EV_SPECIAL:
|
|
return table_lookup(fallbacks, "p", 1);
|
|
case EV_ESCAPE:
|
|
return table_lookup(fallbacks, "e", 1);
|
|
default:
|
|
assert(0);
|
|
}
|
|
} else {
|
|
switch (e) {
|
|
case EV_REQUEST:
|
|
return ((p = table_lookup(requests, key, size)) &&
|
|
!(p->flags & RQ_MACRO)) ? p : 0;
|
|
case EV_MACRO:
|
|
return ((p = table_lookup(requests, key, size)) &&
|
|
(p->flags & RQ_MACRO)) ? p : 0;
|
|
case EV_STRING:
|
|
return table_lookup(requests, key, size);
|
|
case EV_NUMREG:
|
|
return table_lookup(numregs, key, size);
|
|
case EV_SPECIAL:
|
|
return table_lookup(specials, key, size);
|
|
case EV_ESCAPE:
|
|
return table_lookup(escapes, key, size);
|
|
case EV_CHAR:
|
|
assert(size == 1);
|
|
return table_lookup(chars, key, size);
|
|
case EV_EQUATION:
|
|
return table_lookup(others, "e", 1);
|
|
case EV_SENTENCE:
|
|
return table_lookup(others, "s", 1);
|
|
case EV_LINE:
|
|
case EV_PROLOG:
|
|
case EV_EPILOG:
|
|
case EV_OPTION:
|
|
case EV_START:
|
|
case EV_EXIT:
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
return 0; /* shut up gcc -W */
|
|
}
|
|
|
|
static int check_num_args(Object fun, int num, char *evnam) {
|
|
char *s = 0;
|
|
struct S_Compound *comp = COMPOUND(fun);
|
|
|
|
if (num < comp->min_args)
|
|
s = "few";
|
|
else if (comp->max_args >= 0 && num > comp->max_args)
|
|
s = "many";
|
|
if (s) warn("too %s arguments for %s function", s, evnam);
|
|
return !s;
|
|
}
|
|
|
|
char *event_exec(Event e, char *key, int size, int *size_ret, int complain) {
|
|
Elem *p = event_lookup(e, key, size);
|
|
Object ret;
|
|
char *name = event_names[e];
|
|
static char c;
|
|
|
|
if (p) {
|
|
if ((e == EV_REQUEST || e == EV_MACRO) && (p->flags & RQ_STRING)) {
|
|
warn("cannot execute string as request or macro");
|
|
return 0;
|
|
}
|
|
if (e == EV_STRING && !(p->flags & RQ_STRING)) {
|
|
warn("cannot execute request or macro as string");
|
|
return 0;
|
|
}
|
|
ret = get_object(p->obj);
|
|
if (TYPE(ret) == T_Compound) {
|
|
if (!check_num_args(ret, args_num(), name))
|
|
return 0;
|
|
ret = Funcall(ret, args_get(), 0);
|
|
}
|
|
assert(size_ret != 0);
|
|
switch (TYPE(ret)) {
|
|
case T_Character:
|
|
c = CHAR(ret);
|
|
*size_ret = 1;
|
|
return &c;
|
|
case T_Symbol:
|
|
ret = SYMBOL(ret)->name; /* fall through */
|
|
case T_String:
|
|
*size_ret = STRING(ret)->size;
|
|
return STRING(ret)->data;
|
|
default:
|
|
warn("cannot coerce result of %s function to string", name);
|
|
return 0;
|
|
}
|
|
}
|
|
if (complain)
|
|
warn("no event value for %s `%s'", name, printable_string(key, size));
|
|
return 0;
|
|
}
|
|
|
|
char *event_exec_fallback(Event e, char *key, int size, int *size_ret) {
|
|
if (!event_lookup(e, key, size) && event_lookup(e, "", 0))
|
|
return event_exec(e, "", 0, size_ret, 1);
|
|
else
|
|
return event_exec(e, key, size, size_ret, 1);
|
|
}
|
|
|
|
static Object store_vec_event(int e, Object pri, Object obj) {
|
|
Object ret, v = VECTOR(eventsvec)->data[e];
|
|
int p = Get_Integer(pri);
|
|
|
|
if (p < 0 || p > 99)
|
|
Range_Error(pri);
|
|
ret = VECTOR(v)->data[p];
|
|
if (!Nullp(obj))
|
|
VECTOR(v)->data[p] = obj;
|
|
return ret;
|
|
}
|
|
|
|
static Object p_defevent(Object event, Object pri, Object obj) {
|
|
char e = Symbols_To_Bits(event, 0, event_syms);
|
|
|
|
if (Truep(obj) && TYPE(obj) != T_Compound)
|
|
Wrong_Type_Combination(obj, "compound procedure or #f");
|
|
return store_vec_event(e, pri, obj);
|
|
}
|
|
|
|
static Object p_eventdef(Object event, Object pri) {
|
|
char e = Symbols_To_Bits(event, 0, event_syms);
|
|
|
|
return store_vec_event(e, pri, Null);
|
|
}
|
|
|
|
void events_vec_exec(Event e) {
|
|
Object func, v;
|
|
int i;
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
v = VECTOR(eventsvec)->data[e];
|
|
func = VECTOR(v)->data[i];
|
|
if (Truep(func) && check_num_args(func, args_num(), event_names[e]))
|
|
(void)Funcall(func, args_get(), 0);
|
|
}
|
|
}
|
|
|
|
void init_event(void) {
|
|
int i;
|
|
|
|
requests = table_new(256*256);
|
|
numregs = table_new(256*256);
|
|
specials = table_new(256*256);
|
|
escapes = table_new(256);
|
|
chars = table_new(256);
|
|
others = table_new(10);
|
|
fallbacks = table_new(256);
|
|
Key_Equation = Make_String("e", 1);
|
|
Global_GC_Link(Key_Equation);
|
|
Key_Sentence = Make_String("s", 1);
|
|
Global_GC_Link(Key_Sentence);
|
|
eventsvec = Make_Vector(NUM_VEC_EVENTS, Null);
|
|
Global_GC_Link(eventsvec);
|
|
for (i = 0; i < NUM_VEC_EVENTS; i++)
|
|
VECTOR(eventsvec)->data[i] = Make_Vector(100, False);
|
|
Define_Primitive(p_defrequest, "defrequest", 2, 2, EVAL);
|
|
Define_Primitive(p_defmacro, "defmacro", 2, 2, EVAL);
|
|
Define_Primitive(p_defstring, "defstring", 2, 2, EVAL);
|
|
Define_Primitive(p_defnumreg, "defnumreg", 2, 2, EVAL);
|
|
Define_Primitive(p_defspecial, "defspecial", 2, 2, EVAL);
|
|
Define_Primitive(p_defequation, "defequation", 1, 1, EVAL);
|
|
Define_Primitive(p_defsentence, "defsentence", 1, 1, EVAL);
|
|
Define_Primitive(p_defescape, "defescape", 2, 2, EVAL);
|
|
Define_Primitive(p_defchar, "defchar", 2, 2, EVAL);
|
|
Define_Primitive(p_defevent, "defevent", 3, 3, EVAL);
|
|
Define_Primitive(p_requestdef, "requestdef", 1, 1, EVAL);
|
|
Define_Primitive(p_macrodef, "macrodef", 1, 1, EVAL);
|
|
Define_Primitive(p_stringdef, "stringdef", 1, 1, EVAL);
|
|
Define_Primitive(p_numregdef, "numregdef", 1, 1, EVAL);
|
|
Define_Primitive(p_specialdef, "specialdef", 1, 1, EVAL);
|
|
Define_Primitive(p_equationdef, "equationdef", 0, 0, EVAL);
|
|
Define_Primitive(p_sentencedef, "sentencedef", 0, 0, EVAL);
|
|
Define_Primitive(p_escapedef, "escapedef", 1, 1, EVAL);
|
|
Define_Primitive(p_chardef, "chardef", 1, 1, EVAL);
|
|
Define_Primitive(p_eventdef, "eventdef", 2, 2, EVAL);
|
|
}
|