/*
* This file is part of DGD, http://dgd-osr.sourceforge.net/
* Copyright (C) 1993-2010 Dworkin B.V.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
# define INCLUDE_FILE_IO
# include "kfun.h"
# include "table.h"
/*
* prototypes
*/
# define FUNCDEF(name, func, proto, v) extern int func(); extern char proto[];
# include "builtin.c"
# include "std.c"
# include "file.c"
# include "math.c"
# include "extra.c"
# include "debug.c"
# undef FUNCDEF
/*
* kernel function table
*/
static kfunc kforig[] = {
# define FUNCDEF(name, func, proto, v) { name, proto, func, v, 0 },
# include "builtin.c"
# include "std.c"
# include "file.c"
# include "math.c"
# include "extra.c"
# include "debug.c"
# undef FUNCDEF
};
kfunc kftab[256]; /* kfun tab */
char kfind[256]; /* n -> index */
static char kfx[256]; /* index -> n */
static int nkfun; /* # kfuns */
static extfunc *kfext; /* additional kfun pointers */
/*
* NAME: kfun->clear()
* DESCRIPTION: clear previously added kfuns from the table
*/
void kf_clear()
{
kfext = (extfunc *) NULL;
nkfun = sizeof(kforig) / sizeof(kfunc);
}
/*
* NAME: kfun->callgate()
* DESCRIPTION: extra kfun call gate
*/
static int kf_callgate(f, nargs, kf)
frame *f;
int nargs;
kfunc *kf;
{
value val;
val = nil_value;
(*kfext[kf->num - sizeof(kforig) / sizeof(kfunc)])(f, nargs, &val);
i_pop(f, nargs);
*--f->sp = val;
return 0;
}
/*
* NAME: prototype()
* DESCRIPTION: construct proper prototype for new kfun
*/
static char *prototype(proto)
char *proto;
{
register char *p, *q;
register int nargs, vargs;
int class, type;
bool varargs;
class = C_STATIC;
type = *proto++;
p = proto;
nargs = vargs = 0;
varargs = FALSE;
/* pass 1: check prototype */
if (*p != T_VOID) {
while (*p != '\0') {
if (*p == T_VARARGS) {
/* varargs or ellipsis */
if (p[1] == '\0') {
class |= C_ELLIPSIS;
if (!varargs) {
--nargs;
vargs++;
}
break;
}
varargs = TRUE;
} else {
if (*p != T_MIXED) {
/* non-mixed arguments: typecheck this function */
class |= C_TYPECHECKED;
}
if (varargs) {
nargs++;
} else {
vargs++;
}
}
p++;
}
}
/* allocate new prototype */
p = proto;
q = proto = ALLOC(char, 6 + nargs + vargs);
*q++ = class;
*q++ = nargs;
*q++ = vargs;
*q++ = 0;
*q++ = 6 + nargs + vargs;
*q++ = type;
/* pass 2: fill in new prototype */
if (*p != T_VOID) {
while (*p != '\0') {
if (*p != T_VARARGS) {
*q++ = *p;
}
p++;
}
}
return proto;
}
/*
* NAME: kfun->ext_kfun()
* DESCRIPTION: add new kfuns
*/
void kf_ext_kfun(kfadd, n)
register extkfunc *kfadd;
register int n;
{
register kfunc *kf;
register extfunc *kfe;
kfext = REALLOC(kfext, extfunc, nkfun - sizeof(kforig) / sizeof(kfunc),
nkfun - sizeof(kforig) / sizeof(kfunc) + n);
kfadd += n;
nkfun += n;
kf = kftab + nkfun;
kfe = kfext + nkfun - sizeof(kforig) / sizeof(kfunc);
while (n != 0) {
(--kf)->name = (--kfadd)->name;
kf->proto = prototype(kfadd->proto);
kf->func = (int (*)()) &kf_callgate;
kf->version = 0;
*--kfe = kfadd->func;
--n;
}
}
/*
* NAME: kfun->cmp()
* DESCRIPTION: compare two kftable entries
*/
static int kf_cmp(cv1, cv2)
cvoid *cv1, *cv2;
{
return strcmp(((kfunc *) cv1)->name, ((kfunc *) cv2)->name);
}
/*
* NAME: kfun->init()
* DESCRIPTION: initialize the kfun table
*/
void kf_init()
{
register int i, n;
register char *k1, *k2;
memcpy(kftab, kforig, sizeof(kforig));
for (i = 0; i < nkfun; i++) {
kftab[i].num = i;
}
for (i = 0, k1 = kfind, k2 = kfx; i < KF_BUILTINS; i++) {
*k1++ = i;
*k2++ = i;
}
qsort(kftab + KF_BUILTINS, nkfun - KF_BUILTINS, sizeof(kfunc), kf_cmp);
for (n = 0; kftab[i].name[1] == '.'; n++) {
*k2++ = '\0';
i++;
}
for (k1 = kfind + 128; i < nkfun; i++) {
*k1++ = i;
*k2++ = i + 128 - KF_BUILTINS - n;
}
}
/*
* NAME: kfun->index()
* DESCRIPTION: search for kfun in the kfun table, return raw index or -1
*/
static int kf_index(name)
register char *name;
{
register unsigned int h, l, m;
register int c;
l = KF_BUILTINS;
h = nkfun;
do {
c = strcmp(name, kftab[m = (l + h) >> 1].name);
if (c == 0) {
return m; /* found */
} else if (c < 0) {
h = m; /* search in lower half */
} else {
l = m + 1; /* search in upper half */
}
} while (l < h);
/*
* not found
*/
return -1;
}
/*
* NAME: kfun->func()
* DESCRIPTION: search for kfun in the kfun table, return index or -1
*/
int kf_func(name)
char *name;
{
register int n;
n = kf_index(name);
if (n >= 0) {
n = UCHAR(kfx[n]);
}
return n;
}
/*
* NAME: kfun->reclaim()
* DESCRIPTION: reclaim kfun space
*/
void kf_reclaim()
{
register int i, n, last;
/* skip already-removed kfuns */
for (last = nkfun; kfind[--last + 128 - KF_BUILTINS] == '\0'; ) ;
/* remove duplicates at the end */
for (i = last; i >= KF_BUILTINS; --i) {
n = UCHAR(kfind[i + 128 - KF_BUILTINS]);
if (UCHAR(kfx[n]) == i + 128 - KF_BUILTINS) {
if (i != last) {
message("*** Reclaimed %d kernel function%s\012", last - i,
((last - i > 1) ? "s" : ""));
}
break;
}
kfind[i + 128 - KF_BUILTINS] = '\0';
}
/* copy last to 0.removed_kfuns */
for (i = KF_BUILTINS; i < nkfun && kftab[i].name[1] == '.'; i++) {
if (kfx[i] != '\0') {
message("*** Preparing to reclaim unused kfun %s\012",
kftab[i].name);
n = UCHAR(kfind[last-- + 128 - KF_BUILTINS]);
kfx[n] = UCHAR(kfx[i]);
kfind[UCHAR(kfx[n])] = n;
kfx[i] = '\0';
}
}
}
typedef struct {
short nbuiltin; /* # builtin kfuns */
short nkfun; /* # other kfuns */
short kfnamelen; /* length of all kfun names */
} dump_header;
static char dh_layout[] = "sss";
/*
* NAME: kfun->dump()
* DESCRIPTION: dump the kfun table
*/
bool kf_dump(fd)
int fd;
{
register int i, n;
register unsigned int len, buflen;
register kfunc *kf;
dump_header dh;
char *buffer;
bool flag;
/* prepare header */
dh.nbuiltin = KF_BUILTINS;
dh.nkfun = nkfun - KF_BUILTINS;
dh.kfnamelen = 0;
for (i = KF_BUILTINS; i < nkfun; i++) {
n = UCHAR(kfind[i + 128 - KF_BUILTINS]);
if (kfx[n] != '\0') {
dh.kfnamelen += strlen(kftab[n].name) + 1;
if (kftab[n].name[1] != '.') {
dh.kfnamelen += 2;
}
} else {
--dh.nkfun;
}
}
/* write header */
if (P_write(fd, (char *) &dh, sizeof(dump_header)) < 0) {
return FALSE;
}
/* write kfun names */
buffer = ALLOCA(char, dh.kfnamelen);
buflen = 0;
for (i = KF_BUILTINS; i < nkfun; i++) {
n = UCHAR(kfind[i + 128 - KF_BUILTINS]);
if (kfx[n] != '\0') {
kf = &kftab[n];
if (kf->name[1] != '.') {
buffer[buflen++] = '0' + kf->version;
buffer[buflen++] = '.';
}
len = strlen(kf->name) + 1;
memcpy(buffer + buflen, kf->name, len);
buflen += len;
}
}
flag = (P_write(fd, buffer, buflen) >= 0);
AFREE(buffer);
return flag;
}
/*
* NAME: kfun->restore()
* DESCRIPTION: restore the kfun table
*/
void kf_restore(fd, oldcomp)
int fd, oldcomp;
{
register int i, n, buflen;
dump_header dh;
char *buffer;
/* read header */
conf_dread(fd, (char *) &dh, dh_layout, (Uint) 1);
/* fix kfuns */
buffer = ALLOCA(char, dh.kfnamelen);
if (P_read(fd, buffer, (unsigned int) dh.kfnamelen) < 0) {
fatal("cannot restore kfun names");
}
memset(kfx + KF_BUILTINS, '\0', nkfun);
buflen = 0;
for (i = 0; i < dh.nkfun; i++) {
if (buffer[buflen + 1] == '.') {
n = kf_index(buffer + buflen + 2);
if (n < 0 || kftab[n].version != buffer[buflen] - '0') {
n = kf_index(buffer + buflen);
if (n < 0) {
error("Restored unknown kfun: %s", buffer + buflen);
}
}
} else {
n = kf_index(buffer + buflen);
if (n < 0) {
if (strcmp(buffer + buflen, "(compile_object)") == 0) {
n = kf_index("0.compile_object");
} else if (strcmp(buffer + buflen, "hash_md5") == 0 ||
strcmp(buffer + buflen, "(hash_md5)") == 0) {
n = kf_index("0.hash_md5");
} else if (strcmp(buffer + buflen, "hash_sha1") == 0 ||
strcmp(buffer + buflen, "(hash_sha1)") == 0) {
n = kf_index("0.hash_sha1");
} else {
error("Restored unknown kfun: %s", buffer + buflen);
}
}
if (kftab[n].func == kf_dump_state) {
n = kf_index("0.dump_state");
} else if (kftab[n].func == kf_old_compile_object) {
oldcomp = FALSE;
} else if (kftab[n].func == kf_compile_object && oldcomp) {
/* convert compile_object() */
n = kf_index("0.compile_object");
}
}
kfx[n] = i + 128;
kfind[i + 128] = n;
buflen += strlen(buffer + buflen) + 1;
}
AFREE(buffer);
if (dh.nkfun < nkfun - KF_BUILTINS) {
/*
* There are more kfuns in the current driver than in the driver
* which created the dump file: deal with those new kfuns.
*/
n = dh.nkfun + 128;
for (i = KF_BUILTINS; i < nkfun; i++) {
if (kfx[i] == '\0' && kftab[i].name[1] != '.') {
/* new kfun */
kfind[n] = i;
kfx[i] = n++;
}
}
}
}