Blame support/cltkEval.c

Packit bd2e5d
/***********************************************************************/
Packit bd2e5d
/*                                                                     */
Packit bd2e5d
/*                 MLTk, Tcl/Tk interface of OCaml                     */
Packit bd2e5d
/*                                                                     */
Packit bd2e5d
/*    Francois Rouaix, Francois Pessaux, Jun Furuse and Pierre Weis    */
Packit bd2e5d
/*               projet Cristal, INRIA Rocquencourt                    */
Packit bd2e5d
/*            Jacques Garrigue, Kyoto University RIMS                  */
Packit bd2e5d
/*                                                                     */
Packit bd2e5d
/*  Copyright 2002 Institut National de Recherche en Informatique et   */
Packit bd2e5d
/*  en Automatique and Kyoto University.  All rights reserved.         */
Packit bd2e5d
/*  This file is distributed under the terms of the GNU Library        */
Packit bd2e5d
/*  General Public License, with the special exception on linking      */
Packit bd2e5d
/*  described in file LICENSE found in the OCaml source tree.          */
Packit bd2e5d
/*                                                                     */
Packit bd2e5d
/***********************************************************************/
Packit bd2e5d
Packit bd2e5d
/* $Id$ */
Packit bd2e5d
Packit bd2e5d
#include <stdlib.h>
Packit bd2e5d
#include <string.h>
Packit bd2e5d
Packit bd2e5d
#include <tcl.h>
Packit bd2e5d
#include <tk.h>
Packit bd2e5d
#include <mlvalues.h>
Packit bd2e5d
#include <alloc.h>
Packit bd2e5d
#include <memory.h>
Packit bd2e5d
#ifdef HAS_UNISTD
Packit bd2e5d
#include <unistd.h>
Packit bd2e5d
#endif
Packit bd2e5d
#include "camltk.h"
Packit bd2e5d
Packit bd2e5d
/* The Tcl interpretor */
Packit bd2e5d
Tcl_Interp *cltclinterp = NULL;
Packit bd2e5d
Packit bd2e5d
/* Copy a list of strings from the C heap to OCaml */
Packit bd2e5d
value copy_string_list(int argc, char **argv)
Packit bd2e5d
{
Packit bd2e5d
  CAMLparam0();
Packit bd2e5d
  CAMLlocal3( res, oldres, str );
Packit bd2e5d
  int i;
Packit bd2e5d
  oldres = Val_unit;
Packit bd2e5d
  str = Val_unit;
Packit bd2e5d
Packit bd2e5d
  res = Val_int(0); /* [] */
Packit bd2e5d
  for (i = argc-1; i >= 0; i--) {
Packit bd2e5d
    oldres = res;
Packit bd2e5d
    str = tcl_string_to_caml(argv[i]);
Packit bd2e5d
    res = caml_alloc(2, 0);
Packit bd2e5d
    Field(res, 0) = str;
Packit bd2e5d
    Field(res, 1) = oldres;
Packit bd2e5d
  }
Packit bd2e5d
  CAMLreturn(res);
Packit bd2e5d
}
Packit bd2e5d
Packit bd2e5d
/*
Packit bd2e5d
 * Calling Tcl from OCaml
Packit bd2e5d
 *   this version works on an arbitrary Tcl command,
Packit bd2e5d
 *   and does parsing and substitution
Packit bd2e5d
 */
Packit bd2e5d
CAMLprim value camltk_tcl_eval(value str)
Packit bd2e5d
{
Packit bd2e5d
  int code;
Packit bd2e5d
  char *cmd = NULL;
Packit bd2e5d
Packit bd2e5d
  CheckInit();
Packit bd2e5d
Packit bd2e5d
  /* Tcl_Eval may write to its argument, so we take a copy
Packit bd2e5d
   * If the evaluation raises an OCaml exception, we have a space
Packit bd2e5d
   * leak
Packit bd2e5d
   */
Packit bd2e5d
  Tcl_ResetResult(cltclinterp);
Packit bd2e5d
  cmd = caml_string_to_tcl(str);
Packit bd2e5d
  code = Tcl_Eval(cltclinterp, cmd);
Packit bd2e5d
  caml_stat_free(cmd);
Packit bd2e5d
Packit bd2e5d
  switch (code) {
Packit bd2e5d
  case TCL_OK:
Packit bd2e5d
    return tcl_string_to_caml(Tcl_GetStringResult(cltclinterp));
Packit bd2e5d
  case TCL_ERROR:
Packit bd2e5d
    tk_error(Tcl_GetStringResult(cltclinterp));
Packit bd2e5d
  default:  /* TCL_BREAK, TCL_CONTINUE, TCL_RETURN */
Packit bd2e5d
    tk_error("bad tcl result");
Packit bd2e5d
  }
Packit bd2e5d
}
Packit bd2e5d
Packit bd2e5d
/*
Packit bd2e5d
 * Calling Tcl from OCaml
Packit bd2e5d
 *   direct call, argument is TkArgs vect
Packit bd2e5d
  type TkArgs =
Packit bd2e5d
      TkToken of string
Packit bd2e5d
    | TkTokenList of TkArgs list                (* to be expanded *)
Packit bd2e5d
    | TkQuote of TkArgs                         (* mapped to Tcl list *)
Packit bd2e5d
 * NO PARSING, NO SUBSTITUTION
Packit bd2e5d
 */
Packit bd2e5d
Packit bd2e5d
/*
Packit bd2e5d
 * Compute the size of the argument (of type TkArgs).
Packit bd2e5d
 * TkTokenList must be expanded,
Packit bd2e5d
 * TkQuote count for one.
Packit bd2e5d
 */
Packit bd2e5d
int argv_size(value v)
Packit bd2e5d
{
Packit bd2e5d
  switch (Tag_val(v)) {
Packit bd2e5d
  case 0:                       /* TkToken */
Packit bd2e5d
    return 1;
Packit bd2e5d
  case 1:                       /* TkTokenList */
Packit bd2e5d
    { int n = 0;
Packit bd2e5d
      value l;
Packit bd2e5d
      for (l=Field(v,0), n=0; Is_block(l); l=Field(l,1))
Packit bd2e5d
        n+=argv_size(Field(l,0));
Packit bd2e5d
      return n;
Packit bd2e5d
    }
Packit bd2e5d
  case 2:                       /* TkQuote */
Packit bd2e5d
    return 1;
Packit bd2e5d
  default:
Packit bd2e5d
    tk_error("argv_size: illegal tag");
Packit bd2e5d
  }
Packit bd2e5d
}
Packit bd2e5d
Packit bd2e5d
/* Fill a preallocated vector arguments, doing expansion and all.
Packit bd2e5d
 * Assumes Tcl will
Packit bd2e5d
 *  not tamper with our strings
Packit bd2e5d
 *  make copies if strings are "persistent"
Packit bd2e5d
 */
Packit bd2e5d
int fill_args (char **argv, int where, value v)
Packit bd2e5d
{
Packit bd2e5d
  value l;
Packit bd2e5d
Packit bd2e5d
  switch (Tag_val(v)) {
Packit bd2e5d
  case 0:
Packit bd2e5d
    argv[where] = caml_string_to_tcl(Field(v,0)); /* must free by caml_stat_free */
Packit bd2e5d
    return (where + 1);
Packit bd2e5d
  case 1:
Packit bd2e5d
    for (l=Field(v,0); Is_block(l); l=Field(l,1))
Packit bd2e5d
      where = fill_args(argv,where,Field(l,0));
Packit bd2e5d
    return where;
Packit bd2e5d
  case 2:
Packit bd2e5d
    { char **tmpargv;
Packit bd2e5d
      char *merged;
Packit bd2e5d
      int i;
Packit bd2e5d
      int size = argv_size(Field(v,0));
Packit bd2e5d
      tmpargv = (char **)caml_stat_alloc((size + 1) * sizeof(char *));
Packit bd2e5d
      fill_args(tmpargv,0,Field(v,0));
Packit bd2e5d
      tmpargv[size] = NULL;
Packit bd2e5d
      merged = Tcl_Merge(size,(const char *const*)tmpargv);
Packit bd2e5d
      for(i = 0; i
Packit bd2e5d
      caml_stat_free((char *)tmpargv);
Packit bd2e5d
      /* must be freed by caml_stat_free */
Packit bd2e5d
      argv[where] = (char*)caml_stat_alloc(strlen(merged)+1);
Packit bd2e5d
      strcpy(argv[where], merged);
Packit bd2e5d
      Tcl_Free(merged);
Packit bd2e5d
      return (where + 1);
Packit bd2e5d
    }
Packit bd2e5d
  default:
Packit bd2e5d
    tk_error("fill_args: illegal tag");
Packit bd2e5d
  }
Packit bd2e5d
}
Packit bd2e5d
Packit bd2e5d
/* v is an array of TkArg */
Packit bd2e5d
CAMLprim value camltk_tcl_direct_eval(value v)
Packit bd2e5d
{
Packit bd2e5d
  int i;
Packit bd2e5d
  int size;                     /* size of argv */
Packit bd2e5d
  char **argv, **allocated;
Packit bd2e5d
  int result;
Packit bd2e5d
  Tcl_CmdInfo info;
Packit bd2e5d
Packit bd2e5d
  CheckInit();
Packit bd2e5d
Packit bd2e5d
  /* walk the array to compute final size for Tcl */
Packit bd2e5d
  for(i=0, size=0; i
Packit bd2e5d
    size += argv_size(Field(v,i));
Packit bd2e5d
Packit bd2e5d
  /* +2: one slot for NULL
Packit bd2e5d
         one slot for "unknown" if command not found */
Packit bd2e5d
  argv = (char **)caml_stat_alloc((size + 2) * sizeof(char *));
Packit bd2e5d
  allocated = (char **)caml_stat_alloc(size * sizeof(char *));
Packit bd2e5d
Packit bd2e5d
  /* Copy -- argv[i] must be freed by caml_stat_free */
Packit bd2e5d
  {
Packit bd2e5d
    int where;
Packit bd2e5d
    for(i=0, where=0; i
Packit bd2e5d
      where = fill_args(argv,where,Field(v,i));
Packit bd2e5d
    }
Packit bd2e5d
    if( size != where ){ tk_error("fill_args error!!! Call the CamlTk maintainer!"); }
Packit bd2e5d
    for(i=0; i
Packit bd2e5d
    argv[size] = NULL;
Packit bd2e5d
    argv[size + 1] = NULL;
Packit bd2e5d
  }
Packit bd2e5d
Packit bd2e5d
  /* Eval */
Packit bd2e5d
  Tcl_ResetResult(cltclinterp);
Packit bd2e5d
  if (Tcl_GetCommandInfo(cltclinterp,argv[0],&info)) { /* command found */
Packit bd2e5d
#if (TCL_MAJOR_VERSION >= 8)
Packit bd2e5d
    /* info.proc might be a NULL pointer
Packit bd2e5d
     * We should probably attempt an Obj invocation, but the following quick
Packit bd2e5d
     * hack is easier.
Packit bd2e5d
     */
Packit bd2e5d
    if (info.proc == NULL) {
Packit bd2e5d
      Tcl_DString buf;
Packit bd2e5d
      Tcl_DStringInit(&buf;;
Packit bd2e5d
      Tcl_DStringAppend(&buf, argv[0], -1);
Packit bd2e5d
      for (i=1; i
Packit bd2e5d
        Tcl_DStringAppend(&buf, " ", -1);
Packit bd2e5d
        Tcl_DStringAppend(&buf, argv[i], -1);
Packit bd2e5d
      }
Packit bd2e5d
      result = Tcl_Eval(cltclinterp, Tcl_DStringValue(&buf));
Packit bd2e5d
      Tcl_DStringFree(&buf;;
Packit bd2e5d
    } else {
Packit bd2e5d
      result = (*info.proc)(info.clientData,cltclinterp,size,(const char**)argv);
Packit bd2e5d
    }
Packit bd2e5d
#else
Packit bd2e5d
    result = (*info.proc)(info.clientData,cltclinterp,size,(const char**)argv);
Packit bd2e5d
#endif
Packit bd2e5d
  } else { /* implement the autoload stuff */
Packit bd2e5d
    if (Tcl_GetCommandInfo(cltclinterp,"unknown",&info)) { /* unknown found */
Packit bd2e5d
      for (i = size; i >= 0; i--)
Packit bd2e5d
        argv[i+1] = argv[i];
Packit bd2e5d
      argv[0] = "unknown";
Packit bd2e5d
      result = (*info.proc)(info.clientData,cltclinterp,size+1,(const char**)argv);
Packit bd2e5d
    } else { /* ah, it isn't there at all */
Packit bd2e5d
      result = TCL_ERROR;
Packit bd2e5d
      Tcl_AppendResult(cltclinterp, "Unknown command \"",
Packit bd2e5d
                       argv[0], "\"", NULL);
Packit bd2e5d
    }
Packit bd2e5d
  }
Packit bd2e5d
Packit bd2e5d
  /* Free the various things we allocated */
Packit bd2e5d
  for(i=0; i< size; i ++){
Packit bd2e5d
    caml_stat_free((char *) allocated[i]);
Packit bd2e5d
  }
Packit bd2e5d
  caml_stat_free((char *)argv);
Packit bd2e5d
  caml_stat_free((char *)allocated);
Packit bd2e5d
Packit bd2e5d
  switch (result) {
Packit bd2e5d
  case TCL_OK:
Packit bd2e5d
    return tcl_string_to_caml (Tcl_GetStringResult(cltclinterp));
Packit bd2e5d
  case TCL_ERROR:
Packit bd2e5d
    tk_error(Tcl_GetStringResult(cltclinterp));
Packit bd2e5d
  default:  /* TCL_BREAK, TCL_CONTINUE, TCL_RETURN */
Packit bd2e5d
    tk_error("bad tcl result");
Packit bd2e5d
  }
Packit bd2e5d
}