unroff/src/event.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);
}