summaryrefslogtreecommitdiff
path: root/otherlibs/labltk/support/cltkEval.c
diff options
context:
space:
mode:
Diffstat (limited to 'otherlibs/labltk/support/cltkEval.c')
-rw-r--r--otherlibs/labltk/support/cltkEval.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/otherlibs/labltk/support/cltkEval.c b/otherlibs/labltk/support/cltkEval.c
new file mode 100644
index 0000000000..ac0d3e15c1
--- /dev/null
+++ b/otherlibs/labltk/support/cltkEval.c
@@ -0,0 +1,222 @@
+#include <stdlib.h>
+
+#include <tcl.h>
+#include <tk.h>
+#include <caml/mlvalues.h>
+#include <caml/alloc.h>
+#include <caml/memory.h>
+#ifdef HAS_UNISTD
+#include <unistd.h>
+#endif
+#include "camltk.h"
+
+/* The Tcl interpretor */
+Tcl_Interp *cltclinterp = NULL;
+
+/* Copy a list of strings from the C heap to Caml */
+value copy_string_list(argc, argv)
+ int argc;
+ char ** argv;
+{
+ value res;
+ int i;
+ value oldres = Val_unit, str = Val_unit;
+
+ Begin_roots2 (oldres, str);
+ res = Val_int(0); /* [] */
+ for (i = argc-1; i >= 0; i--) {
+ oldres = res;
+ str = copy_string(argv[i]);
+ res = alloc(2, 0);
+ Field(res, 0) = str;
+ Field(res, 1) = oldres;
+ }
+ End_roots();
+ return res;
+}
+
+/*
+ * Calling Tcl from Caml
+ * this version works on an arbitrary Tcl command
+ */
+value camltk_tcl_eval(str) /* ML */
+value str;
+{
+ int code;
+ char *cmd = NULL;
+
+ CheckInit();
+
+ /* Tcl_Eval may write to its argument, so we take a copy
+ * If the evaluation raises a Caml exception, we have a space
+ * leak
+ */
+ Tcl_ResetResult(cltclinterp);
+ cmd = string_to_c(str);
+ code = Tcl_Eval(cltclinterp, cmd);
+ stat_free(cmd);
+
+ switch (code) {
+ case TCL_OK:
+ return copy_string(cltclinterp->result);
+ case TCL_ERROR:
+ tk_error(cltclinterp->result);
+ default: /* TCL_BREAK, TCL_CONTINUE, TCL_RETURN */
+ tk_error("bad tcl result");
+ }
+}
+
+
+/*
+ * Calling Tcl from Caml
+ * direct call, argument is TkArgs vect
+ type TkArgs =
+ TkToken of string
+ | TkTokenList of TkArgs list (* to be expanded *)
+ | TkQuote of TkArgs (* mapped to Tcl list *)
+ * NO PARSING, NO SUBSTITUTION
+ */
+
+/*
+ * Compute the size of the argument (of type TkArgs).
+ * TkTokenList must be expanded,
+ * TkQuote count for one.
+ */
+int argv_size(v)
+value v;
+{
+ switch (Tag_val(v)) {
+ case 0: /* TkToken */
+ return 1;
+ case 1: /* TkTokenList */
+ { int n;
+ value l;
+ for (l=Field(v,0), n=0; Is_block(l); l=Field(l,1))
+ n+=argv_size(Field(l,0));
+ return n;
+ }
+ case 2: /* TkQuote */
+ return 1;
+ }
+}
+
+/*
+ * Memory of allocated Tcl lists.
+ * We should not need more than MAX_LIST
+ */
+#define MAX_LIST 256
+static char *tcllists[MAX_LIST];
+
+static int startfree = 0;
+/* If size is lower, do not allocate */
+static char *quotedargv[16];
+
+/* Fill a preallocated vector arguments, doing expansion and all.
+ * Assumes Tcl will
+ * not tamper with our strings
+ * make copies if strings are "persistent"
+ */
+int fill_args (argv, where, v)
+char ** argv;
+int where;
+value v;
+{
+ switch (Tag_val(v)) {
+ case 0:
+ argv[where] = String_val(Field(v,0));
+ return (where + 1);
+ case 1:
+ { value l;
+ for (l=Field(v,0); Is_block(l); l=Field(l,1))
+ where = fill_args(argv,where,Field(l,0));
+ return where;
+ }
+ case 2:
+ { char **tmpargv;
+ int size = argv_size(Field(v,0));
+ if (size < 16)
+ tmpargv = &quotedargv[0];
+ else
+ tmpargv = (char **)stat_alloc((size + 1) * sizeof(char *));
+ fill_args(tmpargv,0,Field(v,0));
+ tmpargv[size] = NULL;
+ argv[where] = Tcl_Merge(size,tmpargv);
+ tcllists[startfree++] = argv[where]; /* so we can free it later */
+ if (size >= 16)
+ stat_free((char *)tmpargv);
+ return (where + 1);
+ }
+ }
+}
+
+/* v is an array of TkArg */
+value camltk_tcl_direct_eval(v) /* ML */
+value v;
+{
+ int i;
+ int size; /* size of argv */
+ char **argv;
+ int result;
+ Tcl_CmdInfo info;
+ int wherewasi,whereami; /* positions in tcllists array */
+
+ CheckInit();
+
+ /* walk the array to compute final size for Tcl */
+ for(i=0,size=0;i<Wosize_val(v);i++)
+ size += argv_size(Field(v,i));
+
+ /* +2: one slot for NULL
+ one slot for "unknown" if command not found */
+ argv = (char **)stat_alloc((size + 2) * sizeof(char *));
+
+ wherewasi = startfree; /* should be zero except when nested calls */
+ Assert(startfree < MAX_LIST);
+
+ /* Copy */
+ {
+ int where;
+ for(i=0, where=0;i<Wosize_val(v);i++)
+ where = fill_args(argv,where,Field(v,i));
+ argv[size] = NULL;
+ argv[size + 1] = NULL;
+ }
+
+ Begin_roots_block ((value *) argv, size + 2);
+
+ whereami = startfree;
+
+ /* Eval */
+ Tcl_ResetResult(cltclinterp);
+ if (Tcl_GetCommandInfo(cltclinterp,argv[0],&info)) { /* command found */
+ result = (*info.proc)(info.clientData,cltclinterp,size,argv);
+ } else {/* implement the autoload stuff */
+ if (Tcl_GetCommandInfo(cltclinterp,"unknown",&info)) { /* unknown found */
+ for (i = size; i >= 0; i--)
+ argv[i+1] = argv[i];
+ argv[0] = "unknown";
+ result = (*info.proc)(info.clientData,cltclinterp,size+1,argv);
+ } else { /* ah, it isn't there at all */
+ result = TCL_ERROR;
+ Tcl_AppendResult(cltclinterp, "Unknown command \"",
+ argv[0], "\"", NULL);
+ }
+ }
+ End_roots ();
+
+ /* Free the various things we allocated */
+ stat_free((char *)argv);
+ for (i=wherewasi; i<whereami; i++)
+ free(tcllists[i]);
+ startfree = wherewasi;
+
+ switch (result) {
+ case TCL_OK:
+ return copy_string (cltclinterp->result);
+ case TCL_ERROR:
+ tk_error(cltclinterp->result);
+ default: /* TCL_BREAK, TCL_CONTINUE, TCL_RETURN */
+ tk_error("bad tcl result");
+ }
+}
+