summaryrefslogtreecommitdiff
path: root/dlperl/dlperl.c
diff options
context:
space:
mode:
Diffstat (limited to 'dlperl/dlperl.c')
-rw-r--r--dlperl/dlperl.c1037
1 files changed, 1037 insertions, 0 deletions
diff --git a/dlperl/dlperl.c b/dlperl/dlperl.c
new file mode 100644
index 0000000000..49d48bb197
--- /dev/null
+++ b/dlperl/dlperl.c
@@ -0,0 +1,1037 @@
+static char sccsid[] = "@(#)dlperl.c 1.2 10/12/92 (DLPERL)";
+
+/*
+ * name: dlperl.c
+ * synopsis: dlperl - perl interface to dynamically linked usubs
+ * sccsid: @(#)dlperl.c 1.2 10/12/92
+ */
+
+/*
+ * NOTE: this code is *not* portable
+ * - uses SPARC assembler with gcc asm extensions
+ * - is SPARC ABI specific
+ * - uses SunOS 4.x dlopen
+ *
+ * NOTE: not all types are currently implemented
+ * - multiple indirections (pointers to pointers, etc.)
+ * - structures
+ * - quad-precison (long double)
+ */
+
+#include <dlfcn.h>
+#include <alloca.h>
+#include <ctype.h>
+
+/* perl */
+#include "EXTERN.h"
+#include "perl.h"
+
+/* globals */
+int Dl_warn = 1;
+int Dl_errno;
+#define DL_ERRSTR_SIZ 256
+char Dl_errstr[DL_ERRSTR_SIZ];
+#define WORD_SIZE (sizeof(int))
+
+static int userval();
+static int userset();
+static int usersub();
+
+
+/*
+ * glue perl subroutines and variables to dlperl functions
+ */
+enum usersubs {
+ US_dl_open,
+ US_dl_sym,
+ US_dl_call,
+ US_dl_close,
+};
+
+enum uservars {
+ UV_DL_VERSION,
+ UV_DL_WARN,
+ UV_dl_errno,
+ UV_dl_errstr,
+};
+
+
+int
+dlperl_init()
+{
+ struct ufuncs uf;
+ char *file = "dlperl.c";
+
+ uf.uf_val = userval;
+ uf.uf_set = userset;
+
+#define MAGICVAR(name, ix) uf.uf_index = ix, magicname(name, &uf, sizeof uf)
+
+ /* subroutines */
+ make_usub("dl_open", US_dl_open, usersub, file);
+ make_usub("dl_sym", US_dl_sym, usersub, file);
+ make_usub("dl_call", US_dl_call, usersub, file);
+ make_usub("dl_close", US_dl_close, usersub, file);
+
+ /* variables */
+ MAGICVAR("DL_VERSION", (int) UV_DL_VERSION);
+ MAGICVAR("DL_WARN", (int) UV_DL_WARN);
+ MAGICVAR("dl_errno", (int) UV_dl_errno);
+ MAGICVAR("dl_errstr", (int) UV_dl_errstr);
+
+ return 0;
+}
+
+
+/*
+ * USERVAL AND USERSET
+ */
+
+/*
+ * assign dlperl variables to perl variables
+ */
+/*ARGSUSED*/
+static int
+userval(ix, str)
+int ix;
+STR *str;
+{
+ switch(ix) {
+ case UV_DL_VERSION:
+ str_set(str, sccsid);
+ break;
+ case UV_DL_WARN:
+ str_numset(str, (double) Dl_warn);
+ break;
+ case UV_dl_errno:
+ str_numset(str, (double) Dl_errno);
+ break;
+ case UV_dl_errstr:
+ str_set(str, Dl_errstr);
+ break;
+ default:
+ fatal("dlperl: unimplemented userval");
+ break;
+ }
+ return 0;
+}
+
+/*
+ * assign perl variables to dlperl variables
+ */
+static int
+userset(ix, str)
+int ix;
+STR *str;
+{
+ switch(ix) {
+ case UV_DL_WARN:
+ Dl_warn = (int) str_gnum(str);
+ break;
+ default:
+ fatal("dlperl: unimplemented userset");
+ break;
+ }
+ return 0;
+}
+
+
+/*
+ * USERSUBS
+ */
+static int
+usersub(ix, sp, items)
+int ix;
+register int sp;
+register int items;
+{
+ int oldsp = sp;
+ STR **st = stack->ary_array + sp;
+ register STR *Str; /* used in str_get and str_gnum macros */
+
+ Dl_errno = 0;
+ *Dl_errstr = '\0';
+
+ switch(ix) {
+ case US_dl_open:
+ {
+ char *file;
+ void *dl_so;
+
+ if(items != 1) {
+ fatal("Usage: $dl_so = &dl_open($file)");
+ return oldsp;
+ }
+
+ file = str_get(st[1]);
+ dl_so = dlopen(file, 1);
+
+ --sp;
+ if(dl_so == NULL) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr, "&dl_open: %s", dlerror());
+ if(Dl_warn) warn(Dl_errstr);
+
+ astore(stack, ++sp, str_mortal(&str_undef));
+ } else {
+ astore(stack, ++sp, str_2mortal(str_make(
+ (char *) &dl_so, sizeof(void *))));
+ }
+ break;
+ }
+ case US_dl_sym:
+ {
+ void *dl_so;
+ char *symbol;
+ void *dl_func;
+
+ if(items != 2) {
+ fatal("Usage: $dl_func = &dl_sym($dl_so, $symbol)");
+ return oldsp;
+ }
+
+ dl_so = *(void **) str_get(st[1]);
+ symbol = str_get(st[2]);
+ dl_func = dlsym(dl_so, symbol);
+
+ --sp;
+ if(dl_func == NULL) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr, "&dl_sym: %s", dlerror());
+ if(Dl_warn) warn(Dl_errstr);
+
+ astore(stack, ++sp, str_mortal(&str_undef));
+ } else {
+ astore(stack, ++sp, str_2mortal(str_make(
+ (char *) &dl_func, sizeof(void *))));
+ }
+ break;
+ }
+ case US_dl_call:
+ {
+ void *dl_func;
+ char *parms_desc, *return_desc;
+ int nstack, nparm, narr, nlen, nrep;
+ int f_indirect, f_no_parm, f_result;
+ char c, *c_p; int c_pn = 0;
+ unsigned char C, *C_p; int C_pn = 0;
+ short s, *s_p; int s_pn = 0;
+ unsigned short S, *S_p; int S_pn = 0;
+ int i, *i_p; int i_pn = 0;
+ unsigned int I, *I_p; int I_pn = 0;
+ long l, *l_p; int l_pn = 0;
+ unsigned long L, *L_p; int L_pn = 0;
+ float f, *f_p; int f_pn = 0;
+ double d, *d_p; int d_pn = 0;
+ char *a, **a_p; int a_pn = 0;
+ char *p, **p_p; int p_pn = 0;
+ unsigned int *stack_base, *stack_p;
+ unsigned int *xp;
+ void (*func)();
+ unsigned int ret_o;
+ double ret_fd;
+ float ret_f;
+ char *c1;
+ int n1, n2;
+
+ if(items < 3) {
+fatal("Usage: @vals = &dl_call($dl_func, $parms_desc, $return_desc, @parms)");
+ return oldsp;
+ }
+ dl_func = *(void **) str_get(st[1]);
+ parms_desc = str_get(st[2]);
+ return_desc = str_get(st[3]);
+
+ /* determine size of stack and temporaries */
+# define CNT_STK_TMP(PN, SN) \
+ n2 = 0; do { \
+ if(f_indirect) { \
+ PN += narr; \
+ ++nstack; \
+ if(!f_no_parm) \
+ nparm += narr; \
+ } else { \
+ nstack += SN; \
+ if(!f_no_parm) \
+ ++nparm; \
+ } \
+ } while(++n2 < nrep); \
+ f_indirect = f_no_parm = narr = nrep = 0;
+
+ nstack = 0;
+ nparm = 0;
+ f_indirect = f_no_parm = narr = nrep = 0;
+ for(c1 = parms_desc;*c1;++c1) {
+ switch(*c1) {
+ case ' ':
+ case '\t':
+ break;
+
+ case 'c': /* signed char */
+ CNT_STK_TMP(c_pn, 1);
+ break;
+ case 'C': /* unsigned char */
+ CNT_STK_TMP(C_pn, 1);
+ break;
+ case 's': /* signed short */
+ CNT_STK_TMP(s_pn, 1);
+ break;
+ case 'S': /* unsigned short */
+ CNT_STK_TMP(S_pn, 1);
+ break;
+ case 'i': /* signed int */
+ CNT_STK_TMP(i_pn, 1);
+ break;
+ case 'I': /* unsigned int */
+ CNT_STK_TMP(I_pn, 1);
+ break;
+ case 'l': /* signed long */
+ CNT_STK_TMP(l_pn, 1);
+ break;
+ case 'L': /* unsigned long */
+ CNT_STK_TMP(L_pn, 1);
+ break;
+ case 'f': /* float */
+ CNT_STK_TMP(f_pn, 1);
+ break;
+ case 'd': /* double */
+ CNT_STK_TMP(d_pn, 2);
+ break;
+ case 'a': /* ascii (null-terminated) string */
+ CNT_STK_TMP(a_pn, 1);
+ break;
+ case 'p': /* pointer to <nlen> buffer */
+ CNT_STK_TMP(p_pn, 1);
+ break;
+
+ case '&': /* pointer = [1] */
+ if(f_indirect) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: too many indirections, with char %c",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ f_indirect = 1;
+ narr = 1;
+ break;
+ case '[': /* array */
+ if(f_indirect) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: too many indirections, with char %c",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ f_indirect = 1;
+ ++c1;
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ while(isdigit(*c1)) {
+ narr = narr * 10 + (*c1 - '0');
+ ++c1;
+ }
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ if(*c1 != ']') {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: bad char %c, expected ]",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ break;
+ case '<': /* length */
+ ++c1;
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ while(isdigit(*c1))
+ ++c1;
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ if(*c1 != '>') {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: bad char %c, expected >",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ break;
+ case '+':
+ break;
+ case '-':
+ f_no_parm = 1;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if(nrep) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: too many repeats");
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ while(isdigit(*c1)) {
+ nrep = nrep * 10 + (*c1 - '0');
+ ++c1;
+ }
+ --c1;
+ break;
+ default:
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: bad char %c",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ }
+ /* trailing &[]<>+-0-9 is ignored */
+ if(nparm != items - 3) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: bad parameter count %d, expected %d",
+ items - 3, nparm);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ nparm = 4;
+
+ /* allocate temporaries */
+ if((c_pn && (c_p = (char *)
+ alloca(c_pn * sizeof(char))) == NULL) ||
+ (C_pn && (C_p = (unsigned char *)
+ alloca(C_pn * sizeof(unsigned char))) == NULL) ||
+ (s_pn && (s_p = (short *)
+ alloca(s_pn * sizeof(short))) == NULL) ||
+ (S_pn && (S_p = (unsigned short *)
+ alloca(S_pn * sizeof(unsigned short))) == NULL) ||
+ (i_pn && (i_p = (int *)
+ alloca(i_pn * sizeof(int))) == NULL) ||
+ (I_pn && (I_p = (unsigned int *)
+ alloca(I_pn * sizeof(unsigned int))) == NULL) ||
+ (l_pn && (l_p = (long *)
+ alloca(l_pn * sizeof(long))) == NULL) ||
+ (L_pn && (L_p = (unsigned long *)
+ alloca(L_pn * sizeof(unsigned long))) == NULL) ||
+ (f_pn && (f_p = (float *)
+ alloca(f_pn * sizeof(float))) == NULL) ||
+ (d_pn && (d_p = (double *)
+ alloca(d_pn * sizeof(double))) == NULL) ||
+ (a_pn && (a_p = (char **)
+ alloca(a_pn * sizeof(char *))) == NULL) ||
+ (p_pn && (p_p = (char **)
+ alloca(p_pn * sizeof(char *))) == NULL)) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr, "&dl_call: bad alloca");
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+
+ /* grow stack - maintains stack alignment (double word) */
+ /* NOTE: no functions should be called otherwise the stack */
+ /* that is being built will be corrupted */
+ /* NOTE: some of the stack is pre-allocated, but is not */
+ /* reused here */
+ if(alloca(nstack * WORD_SIZE) == NULL) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr, "&dl_call: bad alloca");
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+
+ /* stack base */
+#if !defined(lint)
+ asm("add %%sp,68,%%o0;st %%o0,%0" :
+ "=g" (stack_base) : /* input */ : "%%o0");
+#else
+ stack_base = 0;
+#endif
+ stack_p = stack_base;
+
+ /* layout stack */
+# define LAY_STK_NUM(T, P, PN) \
+ n2 = 0; do { \
+ if(f_indirect) { \
+ *stack_p++ = (unsigned int) &P[PN]; \
+ if(f_no_parm) { \
+ PN += narr; \
+ } else { \
+ for(n1 = 0;n1 < narr;++n1) { \
+ P[PN++] = (T) \
+ str_gnum(st[nparm++]); \
+ } \
+ } \
+ } else { \
+ if(f_no_parm) { \
+ ++stack_p; \
+ } else { \
+ *stack_p++ = (T) \
+ str_gnum(st[nparm++]); \
+ } \
+ } \
+ } while(++n2 < nrep); \
+ f_indirect = f_no_parm = narr = nrep = 0;
+
+# define LAY_STK_DOUBLE(T, P, PN) \
+ n2 = 0; do { \
+ if(f_indirect) { \
+ *stack_p++ = (unsigned int) &P[PN]; \
+ if(f_no_parm) { \
+ PN += narr; \
+ } else { \
+ for(n1 = 0;n1 < narr;++n1) { \
+ P[PN++] = (T) \
+ str_gnum(st[nparm++]); \
+ } \
+ } \
+ } else { \
+ if(f_no_parm) { \
+ stack_p += 2; \
+ } else { \
+ d = (T) str_gnum(st[nparm++]); \
+ xp = (unsigned int *) &d; \
+ *stack_p++ = *xp++; \
+ *stack_p++ = *xp; \
+ } \
+ } \
+ } while(++n2 < nrep); \
+ f_indirect = f_no_parm = narr = nrep = 0;
+
+# define LAY_STK_STR(P, PN) \
+ n2 = 0; do { \
+ if(f_indirect) { \
+ *stack_p++ = (unsigned int) &P[PN]; \
+ if(f_no_parm) { \
+ PN += narr; \
+ } else { \
+ for(n1 = 0;n1 < narr;++n1) { \
+ P[PN++] = \
+ str_get(st[nparm++]); \
+ } \
+ } \
+ } else { \
+ if(f_no_parm) { \
+ ++stack_p; \
+ } else { \
+ *stack_p++ = (unsigned int) \
+ str_get(st[nparm++]); \
+ } \
+ } \
+ } while(++n2 < nrep); \
+ f_indirect = f_no_parm = narr = nrep = 0;
+
+ c_pn = C_pn = s_pn = S_pn = i_pn = I_pn = l_pn = L_pn = 0;
+ f_pn = d_pn = a_pn = p_pn = 0;
+ f_indirect = f_no_parm = narr = nrep = 0;
+ for(c1 = parms_desc;*c1;++c1) {
+ switch(*c1) {
+ case ' ':
+ case '\t':
+ break;
+
+ case 'c': /* signed char */
+ LAY_STK_NUM(char, c_p, c_pn);
+ break;
+ case 'C': /* unsigned char */
+ LAY_STK_NUM(unsigned char, C_p, C_pn);
+ break;
+ case 's': /* signed short */
+ LAY_STK_NUM(short, s_p, s_pn);
+ break;
+ case 'S': /* unsigned short */
+ LAY_STK_NUM(unsigned short, S_p, S_pn);
+ break;
+ case 'i': /* signed int */
+ LAY_STK_NUM(int, i_p, i_pn);
+ break;
+ case 'I': /* unsigned int */
+ LAY_STK_NUM(unsigned int, I_p, I_pn);
+ break;
+ case 'l': /* signed long */
+ LAY_STK_NUM(long, l_p, l_pn);
+ break;
+ case 'L': /* unsigned long */
+ LAY_STK_NUM(unsigned long, L_p, L_pn);
+ break;
+ case 'f': /* float */
+ LAY_STK_NUM(float, f_p, f_pn);
+ break;
+ case 'd': /* double */
+ LAY_STK_DOUBLE(double, d_p, d_pn);
+ break;
+ case 'a': /* ascii (null-terminated) string */
+ LAY_STK_STR(a_p, a_pn);
+ break;
+ case 'p': /* pointer to <nlen> buffer */
+ LAY_STK_STR(p_p, p_pn);
+ break;
+
+ case '&': /* pointer = [1] */
+ if(f_indirect) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: too many indirections, with char %c",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ f_indirect = 1;
+ narr = 1;
+ break;
+ case '[': /* array */
+ if(f_indirect) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: too many indirections, with char %c",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ f_indirect = 1;
+ ++c1;
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ while(isdigit(*c1)) {
+ narr = narr * 10 + (*c1 - '0');
+ ++c1;
+ }
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ if(*c1 != ']') {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: bad char %c, expected ]",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ break;
+ case '<': /* length */
+ ++c1;
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ while(isdigit(*c1))
+ ++c1;
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ if(*c1 != '>') {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: bad char %c, expected >",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ break;
+ case '+':
+ break;
+ case '-':
+ f_no_parm = 1;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if(nrep) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: too many repeats");
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ while(isdigit(*c1)) {
+ nrep = nrep * 10 + (*c1 - '0');
+ ++c1;
+ }
+ --c1;
+ break;
+ default:
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: bad char %c",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ }
+ /* trailing &[]<>+-0-9 is ignored */
+
+ /* call function */
+ /* NOTE: the first 6 words are passed in registers %o0 - %o5 */
+ /* %sp+68 to %sp+92 are vacant, but allocated */
+ /* and shadow %o0 - %o5 */
+ /* above stack_base starts at %sp+68 and the function */
+ /* call below sets up %o0 - %o5 from stack_base */
+ func = (void (*)()) dl_func;
+ (*func)(stack_base[0], stack_base[1], stack_base[2],
+ stack_base[3], stack_base[4], stack_base[5]);
+
+ /* save return value */
+ /* NOTE: return values are either in %o0 or %f0 */
+#if !defined(lint)
+ asm("st %%o0,%0" : "=g" (ret_o) : /* input */);
+ asm("std %%f0,%0" : "=g" (ret_fd) : /* input */);
+ asm("st %%f0,%0" : "=g" (ret_f) : /* input */);
+#else
+ ret_o = 0; ret_fd = 0.0; ret_f = 0.0;
+#endif
+
+ /* parameter results */
+# define RES_NUM(P, PN, SN) \
+ n2 = 0; do { \
+ if(f_indirect) { \
+ ++nstack; \
+ if(f_result) { \
+ for(n1 = 0;n1 < narr;++n1) { \
+ astore(stack, ++sp, str_2mortal( \
+ str_nmake((double) P[PN++]))); \
+ } \
+ } else { \
+ PN += narr; \
+ } \
+ } else { \
+ nstack += SN; \
+ if(f_result) { \
+ astore(stack, ++sp, \
+ str_mortal(&str_undef));\
+ } \
+ } \
+ } while(++n2 < nrep); \
+ f_indirect = f_result = narr = nlen = nrep = 0;
+
+# define RES_STR(P, PN, L, SN) \
+ n2 = 0; do { \
+ if(f_indirect) { \
+ ++nstack; \
+ if(f_result) { \
+ for(n1 = 0;n1 < narr;++n1) { \
+ astore(stack, ++sp, str_2mortal( \
+ str_make(P[PN++], L))); \
+ } \
+ } else { \
+ PN += narr; \
+ } \
+ } else { \
+ if(f_result) { \
+ astore(stack, ++sp, str_2mortal(\
+ str_make((char *) \
+ stack_base[nstack], L))); \
+ } \
+ nstack += SN; \
+ } \
+ } while(++n2 < nrep); \
+ f_indirect = f_result = narr = nlen = nrep = 0;
+
+ --sp;
+ nstack = 0;
+ c_pn = C_pn = s_pn = S_pn = i_pn = I_pn = l_pn = L_pn = 0;
+ f_pn = d_pn = a_pn = p_pn = 0;
+ f_indirect = f_result = narr = nlen = nrep = 0;
+ for(c1 = parms_desc;*c1;++c1) {
+ switch(*c1) {
+ case ' ':
+ case '\t':
+ break;
+
+ case 'c': /* signed char */
+ RES_NUM(c_p, c_pn, 1);
+ break;
+ case 'C': /* unsigned char */
+ RES_NUM(C_p, C_pn, 1);
+ break;
+ case 's': /* signed short */
+ RES_NUM(s_p, s_pn, 1);
+ break;
+ case 'S': /* unsigned short */
+ RES_NUM(S_p, S_pn, 1);
+ break;
+ case 'i': /* signed int */
+ RES_NUM(i_p, i_pn, 1);
+ break;
+ case 'I': /* unsigned int */
+ RES_NUM(I_p, I_pn, 1);
+ break;
+ case 'l': /* signed long */
+ RES_NUM(l_p, l_pn, 1);
+ break;
+ case 'L': /* unsigned long */
+ RES_NUM(L_p, L_pn, 1);
+ break;
+ case 'f': /* float */
+ RES_NUM(f_p, f_pn, 1);
+ break;
+ case 'd': /* double */
+ RES_NUM(d_p, d_pn, 2);
+ break;
+ case 'a': /* ascii (null-terminated) string */
+ RES_STR(a_p, a_pn, 0, 1);
+ break;
+ case 'p': /* pointer to <nlen> buffer */
+ RES_STR(p_p, p_pn, nlen, 1);
+ break;
+
+ case '&': /* pointer = [1] */
+ if(f_indirect) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: too many indirections, with char %c",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ f_indirect = 1;
+ narr = 1;
+ break;
+ case '[': /* array */
+ if(f_indirect) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: too many indirections, with char %c",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ f_indirect = 1;
+ ++c1;
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ while(isdigit(*c1)) {
+ narr = narr * 10 + (*c1 - '0');
+ ++c1;
+ }
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ if(*c1 != ']') {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: bad char %c, expected ]",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ break;
+ case '<': /* length */
+ ++c1;
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ while(isdigit(*c1)) {
+ nlen = nlen * 10 + (*c1 - '0');
+ ++c1;
+ }
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ if(*c1 != '>') {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: bad char %c, expected >",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ break;
+ case '+':
+ f_result = 1;
+ break;
+ case '-':
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if(nrep) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: too many repeats");
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ while(isdigit(*c1)) {
+ nrep = nrep * 10 + (*c1 - '0');
+ ++c1;
+ }
+ --c1;
+ break;
+ default:
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: parms_desc %s: bad char %c",
+ parms_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ }
+ /* trailing &[]<>+-0-9 is ignored */
+
+ /* return value */
+# define RET_NUM(T, S, P, R) \
+ if(f_indirect) { \
+ P = (T *) ret_o; \
+ for(n1 = 0;n1 < narr;++n1) { \
+ S = *P++; \
+ astore(stack, ++sp, str_2mortal( \
+ str_nmake((double) S))); \
+ } \
+ } else { \
+ S = (T) R; \
+ astore(stack, ++sp, str_2mortal( \
+ str_nmake((double) S))); \
+ }
+
+# define RET_STR(S, P, L) \
+ if(f_indirect) { \
+ P = (char **) ret_o; \
+ for(n1 = 0;n1 < narr;++n1) { \
+ S = *P++; \
+ astore(stack, ++sp, str_2mortal( \
+ str_make((char *) S, L))); \
+ } \
+ } else { \
+ S = (char *) ret_o; \
+ astore(stack, ++sp, str_2mortal( \
+ str_make((char *) S, L))); \
+ }
+
+ f_indirect = nlen = narr = 0;
+ for(c1 = return_desc;*c1;++c1) {
+ switch(*c1) {
+ case ' ':
+ case '\t':
+ break;
+
+ case 'c': /* signed char */
+ RET_NUM(char, c, c_p, ret_o);
+ goto ret_exit;
+ case 'C': /* unsigned char */
+ RET_NUM(unsigned char, C, C_p, ret_o);
+ goto ret_exit;
+ case 's': /* signed short */
+ RET_NUM(short, s, s_p, ret_o);
+ goto ret_exit;
+ case 'S': /* unsigned short */
+ RET_NUM(unsigned short, S, S_p, ret_o);
+ goto ret_exit;
+ case 'i': /* signed int */
+ RET_NUM(int, i, i_p, ret_o);
+ goto ret_exit;
+ case 'I': /* unsigned int */
+ RET_NUM(unsigned int, I, I_p, ret_o);
+ goto ret_exit;
+ case 'l': /* signed long */
+ RET_NUM(long, l, l_p, ret_o);
+ goto ret_exit;
+ case 'L': /* unsigned long */
+ RET_NUM(unsigned long, L, L_p, ret_o);
+ goto ret_exit;
+ case 'f': /* float */
+ RET_NUM(float, f, f_p, ret_f);
+ break;
+ case 'd': /* double */
+ RET_NUM(double, d, d_p, ret_fd);
+ goto ret_exit;
+ case 'a': /* ascii (null-terminated) string */
+ RET_STR(a, a_p, 0);
+ goto ret_exit;
+ case 'p': /* pointer to <nlen> buffer */
+ RET_STR(p, p_p, nlen);
+ goto ret_exit;
+
+ case '&': /* pointer = [1] */
+ if(f_indirect) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: return_desc %s: too many indirections, with char %c",
+ return_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ f_indirect = 1;
+ narr = 1;
+ break;
+ case '[': /* array */
+ if(f_indirect) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: return_desc %s: too many indirections, with char %c",
+ return_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ f_indirect = 1;
+ ++c1;
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ while(isdigit(*c1)) {
+ narr = narr * 10 + (*c1 - '0');
+ ++c1;
+ }
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ if(*c1 != ']') {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: return_desc %s: bad char %c, expected ]",
+ return_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ break;
+ case '<': /* length */
+ ++c1;
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ while(isdigit(*c1)) {
+ nlen = nlen * 10 + (*c1 - '0');
+ ++c1;
+ }
+ while(*c1 == ' ' && *c1 == '\t')
+ ++c1;
+ if(*c1 != '>') {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: return_desc %s: bad char %c, expected >",
+ return_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ break;
+ default:
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr,
+ "&dl_call: return_desc %s: bad char %c",
+ return_desc, *c1);
+ if(Dl_warn) warn(Dl_errstr);
+ return oldsp;
+ }
+ }
+ret_exit: /* anything beyond first [cCsSiIlLdfap] is ignored */
+ break;
+ }
+ case US_dl_close:
+ {
+ void *dl_so;
+ int dl_err;
+
+ if(items != 1) {
+ fatal("Usage: $dl_err = &dl_close($dl_so)");
+ return oldsp;
+ }
+
+ dl_so = *(void **) str_get(st[1]);
+ dl_err = dlclose(dl_so);
+
+ --sp;
+ if(dl_err) {
+ Dl_errno = 1;
+ (void) sprintf(Dl_errstr, "&dl_close: %s", dlerror());
+ if(Dl_warn) warn(Dl_errstr);
+ }
+ astore(stack, ++sp, str_2mortal(str_nmake((double) dl_err)));
+ break;
+ }
+ default:
+ fatal("dlperl: unimplemented usersub");
+ break;
+ }
+ return sp;
+}