TclInterp.m
上传用户:shenzhenrh
上传日期:2013-05-12
资源大小:2904k
文件大小:13k
源码类别:

信息检索与抽取

开发平台:

Unix_Linux

  1. /* Implementation for Objective-C Tcl interpreter object
  2.    Copyright (C) 1993,1994  R. Andrew McCallum
  3.    Written by:  R. Andrew McCallum <mccallum@cs.rochester.edu>
  4.    Dept. of Computer Science, U. of Rochester, Rochester, NY  14627
  5.    This file is part of the Tcl/Objective-C interface library.
  6.    This library is free software; you can redistribute it and/or
  7.    modify it under the terms of the GNU Library General Public
  8.    License as published by the Free Software Foundation; either
  9.    version 2 of the License, or (at your option) any later version.
  10.    
  11.    This library is distributed in the hope that it will be useful,
  12.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.    Library General Public License for more details.
  15.    You should have received a copy of the GNU Library General Public
  16.    License along with this library; if not, write to the Free
  17.    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. */ 
  19. #ifdef NeXT
  20. #include "objc-gnu2next.h"
  21. #include <objc/List.h>
  22. #include <objc/HashTable.h>
  23. #else
  24. #include "List.h"
  25. #include "HashTable.h"
  26. #endif /* NeXT */
  27. #include "TclInterp.h"
  28. #include "tclObjc.h"
  29. #include <misc.h>
  30. #if HAVE_READLINE
  31. #include <readline/readline.h>
  32. #include <readline/history.h>
  33. #endif /* HAVE_READLINE */
  34. #define DEFAULT_PROMPT "Tcl% "
  35. #define DEFAULT_PARTIAL_PROMPT "Tcl> "
  36. #if (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION == 4)
  37. #define SUBDIR "tcl8.4"
  38. #elif (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION == 3)
  39. #define SUBDIR "tcl8.3"
  40. #elif (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION == 2)
  41. #define SUBDIR "tcl8.2"
  42. #elif (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION == 1)
  43. #define SUBDIR "tcl8.1"
  44. #else
  45. #define SUBDIR "tcl8.0"
  46. #endif
  47. #ifdef __CYGWIN__
  48. #include <unistd.h> // MAXPATHLEN
  49. #include <sys/cygwin.h> // cygwin_conv_to_win32_path
  50. #endif
  51. /* If we're using tcl 7.4 or greater, must make tcl_RcFileName reference
  52.  * be extern or else we get a linker conflict. In tcl 7.3, you must
  53.  * declare it here or else it links in the wrong file.
  54.  *
  55.  * And the variable was changed for tcl 7.5 from a C variable to a 
  56.  * Tcl variable, so it doesn't have to be declared here anymore.  
  57.  * These defines were moved to TclInterp.h. 
  58.  */
  59. #if (TCLVERSIONLT74 == 0)
  60. #   if (TCLVERSIONGT74 == 0)
  61. extern char* tcl_RcFileName;
  62. #   endif
  63. #else
  64. char *tcl_RcFileName = NULL; /* Name of a user-specific startup script
  65.  * to source if the application is being run
  66.  * interactively (e.g. "~/.wishrc").  Set
  67.  * by Tcl_AppInit.  NULL means don't source
  68.  * anything ever. */
  69. #endif
  70. List* tclList;
  71. @implementation TclInterp
  72. + initialize
  73. {
  74.   if (self == [TclInterp class])
  75.     {
  76.       tclList = [[List alloc] init];
  77.     }
  78.   return self;
  79. }
  80. + firstTcl
  81. {
  82.   if ([tclList count])
  83.     return [tclList objectAt:0];
  84.   else
  85.     {
  86.       fprintf(stderr, "no firstTcln");
  87.       return nil;
  88.     }
  89. }
  90. + tclAtIndex: (unsigned) index
  91. {
  92.   if (index < [tclList count])
  93.     return [tclList objectAt:index];
  94.   return nil;
  95. }
  96. + (unsigned) tclCount
  97. {
  98.   return [tclList count];
  99. }
  100. - setSecondaryLibraryPath: (const char *)path
  101. {
  102.   secondaryPath = path;
  103.   return self;
  104. }
  105. - (const char *)checkPath: (const char *)path
  106.              subdirectory: (const char *)subdir
  107.                      file: (const char *)file
  108. {
  109.   if (path == NULL)
  110.     return NULL;
  111.   else
  112.     {
  113.       size_t path_len = strlen (path);
  114.       size_t subdir_len = subdir ? strlen (subdir) + 1 : 0;
  115.       size_t len = path_len + 1 + subdir_len + strlen (file) + 1;
  116.       char *dirbuf = malloc (len);
  117.       char filebuf[len + strlen (file) + 1];
  118.       if (dirbuf == NULL)
  119.         abort ();
  120.       strcpy (dirbuf, path);
  121.       if (path[path_len - 1] != '/')
  122.         strcat (dirbuf, "/");
  123.       if (subdir)
  124.         {
  125.           strcat (dirbuf, subdir);
  126.           strcat (dirbuf, "/");
  127.         }
  128.       strcpy (filebuf, dirbuf);
  129.       strcat (filebuf, file);
  130.       if (access (filebuf, R_OK) != -1)
  131.         return dirbuf;
  132.       else 
  133.         {
  134.           free (dirbuf);
  135.           return NULL;
  136.         }
  137.     }
  138. }
  139. const char *
  140. fix_tcl_path (const char *path)
  141. {
  142. #ifndef __CYGWIN__
  143.   return path;
  144. #else
  145.   char buf[MAXPATHLEN + 1], *p;
  146.   
  147.   cygwin_conv_to_win32_path (path, buf);
  148.   
  149.   p = buf;
  150.   while (*p)
  151.     {
  152.       if (*p == '\')
  153. *p = '/';
  154.       p++;
  155.       }
  156.   return xstrdup (buf);
  157. #endif
  158. }
  159. - (const char *)checkTclLibrary
  160. {
  161.   const char *path;
  162.   if ([self checkPath: TCL_LIBRARY subdirectory: NULL file: "init.tcl"])
  163.     path = TCL_LIBRARY;
  164.   else
  165.     path =
  166.       [self checkPath: secondaryPath subdirectory: SUBDIR file: "init.tcl"];
  167.   return fix_tcl_path (path);
  168. }
  169. - (const char *)preInitWithArgc: (int)argc argv: (const char **)argv
  170. {
  171.   const char *args;
  172.   const char *fileName;
  173.   /* tell class object about us */
  174.   [tclList addObject: self];
  175.   /* Create objc name and id hashtables */
  176.   namesToObjects = [[HashTable alloc] initKeyDesc:"*"
  177.     valueDesc:"@"];
  178.   objectsToNames = [[HashTable alloc] initKeyDesc:"@"
  179.     valueDesc:"*"];
  180.   /* This calls Tcl_FindEncoding which ends up
  181.      calling TclWinSetInterfaces which sets getFileAttributesExProc 
  182.      which will be used by TclpObjNormalizePath. */
  183.   Tcl_FindExecutable (argv[0]);
  184.   /* Create and init tcl interpreter */
  185.   interp = Tcl_CreateInterp ();
  186.   {
  187.     const char *path = [self checkTclLibrary];
  188.     
  189.     if (path)
  190.       {
  191. #if (TCL_MAJOR_VERSION >= 8) && (TCL_MINOR_VERSION >= 1)
  192.         extern void TclSetLibraryPath (Tcl_Obj *pathPtr);
  193.         Tcl_Obj *pathObj = Tcl_NewStringObj (path, -1);
  194.         TclSetLibraryPath (Tcl_NewListObj (1, &pathObj));
  195. #else
  196.         Tcl_SetVar (interp, "tcl_library", (char *) path, TCL_GLOBAL_ONLY);
  197. #endif
  198.       }
  199.     else
  200.       {
  201.         char *msg = Tcl_GetVar (interp, "errorInfo", TCL_GLOBAL_ONLY);
  202.         if (msg == NULL)
  203.           msg = interp->result;
  204.         [self error:msg];
  205.         abort ();
  206.       }
  207.   }
  208.   /*
  209.    * Make command-line arguments available in the Tcl variables "argc"
  210.    * and "argv".
  211.    */
  212.   fileName = NULL;
  213.   if ((argc > 1) && (argv[1][0] != '-'))
  214.     {
  215.       fileName = argv[1];
  216.       argc--;
  217.       argv++;
  218.     }
  219.   args = Tcl_Merge (argc-1, (char **)argv+1);
  220.   Tcl_SetVar (interp, "argv", (char *)args, TCL_GLOBAL_ONLY);
  221.   ckfree ((void *)args);
  222.   {
  223.     char buffer[20];
  224.     
  225.     sprintf (buffer, "%d", argc-1);
  226.     Tcl_SetVar (interp, "argc", buffer, TCL_GLOBAL_ONLY);
  227.   }
  228.   Tcl_SetVar (interp, "argv0",
  229.               (char *)((fileName != NULL) ? fileName : argv[0]),
  230.               TCL_GLOBAL_ONLY);
  231.   
  232.   Tcl_SetVar (interp, "tcl_interactive", "1", TCL_GLOBAL_ONLY);
  233.   Tcl_SetVar (interp, "tclObjc", tclObjc_objectToName(self), TCL_GLOBAL_ONLY);
  234.   
  235.   if (Tcl_Init (interp) == TCL_ERROR || TclObjc_Init(interp) == TCL_ERROR)
  236.     {
  237.       char *msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
  238.       if (msg == NULL)
  239. msg = interp->result;
  240.       [self error:msg];
  241.       return NULL; /* shouldn't get here anyway */
  242.     }
  243.   /* Specify a user-specific startup file to invoke if the application
  244.      is run interactively.  Typically the startup file is "~/.apprc"
  245.      where "app" is the name of the application.  If this line is
  246.      deleted then no user-specific startup file will be run under any
  247.      conditions. */
  248. #if (TCLVERSIONGT74 == 1)
  249.   Tcl_SetVar(interp, "tcl_rcFileName", "~/.wishrc", TCL_GLOBAL_ONLY);
  250. #else
  251.   tcl_RcFileName = "~/.wishrc";
  252. #endif
  253.   return fileName;
  254. }
  255. - initWithArgc: (int)argc argv: (const char **)argv
  256. {
  257.   const char *fileName, *msg;
  258.   [super init];
  259. #if HAVE_READLINE
  260.   if (argc)
  261.     rl_readline_name = argv[0];
  262. #endif
  263.   fileName = [self preInitWithArgc:argc argv:argv];
  264.   tclObjc_registerObjectWithName(interp, self, "objcTcl");
  265.   /* If a script file was specified then source that file. */
  266.   if (fileName) 
  267.     if (Tcl_EvalFile(interp, (char *)fileName) != TCL_OK) 
  268.       goto error;
  269.   return self;
  270.  error:
  271.   msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
  272.   if (msg == NULL) {
  273.     msg = interp->result;
  274.   }
  275.   [self error:msg];
  276.   [self free];
  277.   return nil;
  278. }
  279. - init
  280. {
  281.   return [self initWithArgc:0 argv:NULL];
  282. }
  283. - free
  284. {
  285.   [tclList removeObject:self];
  286.   Tcl_DeleteInterp(interp);
  287.   [namesToObjects free];
  288.   [objectsToNames free];
  289.   return [super free];
  290. }
  291. - setEvalDebugPrint: (BOOL) value
  292. {
  293.   evalDebugPrint = value;
  294.   return self;
  295. }
  296. - eval: (const char *)fmt, ...
  297. {
  298.   char cmd[32768];   /* Ugly constant.  Get rid of this */
  299.   va_list ap;
  300.   va_start(ap,fmt);
  301.   vsprintf(cmd, fmt, ap);
  302.   if (evalDebugPrint)
  303.     fprintf(stderr, "%sn", cmd);
  304.   code = Tcl_Eval(interp, cmd);
  305.   if (code != TCL_OK)
  306.     {
  307.       char *msg;
  308.       msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
  309.       if (msg == NULL) {
  310. msg = interp->result;
  311.       }
  312.       fprintf(stderr, "(Tcl -eval:) %sn", msg);
  313.       fprintf(stderr, "while evaluating: %sn", cmd);
  314.     }
  315.   va_end(ap);
  316.   return self;
  317. }
  318. - globalEval: (const char *)fmt, ...
  319. {
  320.   char cmd[4096];
  321.   va_list ap;
  322.   va_start(ap,fmt);
  323.   vsprintf(cmd, fmt, ap);
  324.   if (evalDebugPrint)
  325.     fprintf(stderr, "(global) %sn", cmd);
  326.   code = Tcl_GlobalEval(interp, cmd);
  327.   if (code != TCL_OK)
  328.     {
  329.       char *msg;
  330.       msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
  331.       if (msg == NULL) {
  332. msg = interp->result;
  333.       }
  334.       fprintf(stderr, "(Tcl -eval:) %sn", msg);
  335.       fprintf(stderr, "while evaluating: %sn", cmd);
  336.     }
  337.   va_end(ap);
  338.   return self;
  339. }
  340. - evalFile: (const char *)filename
  341. {
  342.   if ((code = Tcl_EvalFile(interp, (char*)filename)) != TCL_OK) 
  343.     {
  344.       char *msg;
  345.       msg = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
  346.       if (msg == NULL) {
  347. msg = interp->result;
  348.       }
  349.       fprintf(stderr, "(Tcl -evalFile:) %sn", msg);
  350.       fprintf(stderr, "while evaluating contents of file %sn", filename);
  351.     }
  352.   return self;
  353. }
  354. - (BOOL) variableExists: (const char *)varName
  355. {
  356.   return (Tcl_GetVar(interp, (char*)varName, 0) != NULL);
  357. }
  358. - (BOOL) globalVariableExists: (const char *)varName
  359. {
  360.   return (Tcl_GetVar(interp, (char*)varName, TCL_GLOBAL_ONLY) != NULL);
  361. }
  362. - (const char *) variableValue: (const char *)varName
  363. {
  364.   const char *v = Tcl_GetVar(interp, (char*)varName, 0);
  365.   if (!v)
  366.     fprintf(stderr, "(Tcl variableValue:) %s isn't a variablen", varName);
  367.   return v;
  368. }
  369. - (const char *) globalVariableValue: (const char *)varName
  370. {
  371.   const char *v = Tcl_GetVar(interp, (char*)varName, TCL_GLOBAL_ONLY);
  372.   if (!v)
  373.     fprintf(stderr, "(Tcl variableValue:) %s isn't a variablen", varName);
  374.   return v;
  375. }
  376. - (int) code
  377. {
  378.   return code;
  379. }
  380. - (const char *) result
  381. {
  382.   return interp->result;
  383. }
  384. - (Tcl_Interp *) interp
  385. {
  386.   return interp;
  387. }
  388. /* I should make all these rely on the intepretter's hash tables instead.
  389.    That way we can get names defined by Tcl "set" commands too. */
  390. - registerObject: (id)anObject withName: (const char *)aName
  391. {
  392.   [namesToObjects insertKey:aName value:anObject];
  393.   [objectsToNames insertKey:anObject value: (char *)aName];
  394.   tclObjc_registerObjectWithName (interp, anObject, aName);
  395.   return self;
  396. }
  397. - unregisterObject: (id)anObject
  398. {
  399.   char *name = (char *)[objectsToNames valueForKey:anObject];
  400.   tclObjc_unregisterObjectNamed(interp, name);
  401.   [objectsToNames removeKey:anObject];
  402.   [namesToObjects removeKey:name];
  403.   return self;
  404. }
  405. - unregisterObjectNamed:(const char *)aName
  406. {
  407.   return [self unregisterObject:
  408.        (id)[namesToObjects valueForKey:(char *)aName]];
  409. }
  410. - (const char *) nameForObject:anObject
  411. {
  412.   return (char *)[objectsToNames valueForKey:anObject];
  413. }
  414.     
  415. - objectNamed:(const char *)aName
  416. {
  417.   id theObject = nil;
  418.   if ((theObject = tclObjc_nameToObject(aName)) != (id)-1)
  419.     return theObject;
  420.   else if ((theObject = [namesToObjects valueForKey:aName]))
  421.     return theObject;
  422.   return (id)-1;
  423. }
  424. - (BOOL) objectIsRegistered: anObject
  425. {
  426.   return [objectsToNames isKey:anObject];
  427. }
  428. - (BOOL) nameIsRegistered: (const char *)aName
  429. {
  430.   return [namesToObjects isKey:aName];
  431. }
  432. - promptAndEval
  433. #if HAVE_READLINE
  434. {
  435.   Tcl_DString command;
  436.   const char *cmd;
  437.   const char *line;
  438.   int result;
  439.   int gotPartial = 0;
  440.       
  441.   Tcl_DStringInit(&command);
  442.   while (1)
  443.     {
  444.       /* I could add code to do something like tcl_prompt1 */
  445.       if (gotPartial)
  446. line = readline(DEFAULT_PARTIAL_PROMPT);
  447.       else
  448. line = readline(DEFAULT_PROMPT);
  449.       if (!line)
  450. {
  451.   printf("n");
  452.   return self;
  453. }
  454.       add_history(line);
  455.       cmd = Tcl_DStringAppend(&command, line, -1);
  456.       free(line);
  457.       if (!Tcl_CommandComplete(cmd))
  458. {
  459.   gotPartial = 1;
  460.   continue;
  461. }
  462.       gotPartial = 0;
  463.       result = Tcl_RecordAndEval(interp, cmd, 0);
  464.       Tcl_DStringFree(&command);
  465.       if (result != TCL_OK)
  466. fprintf(stderr, "%sn", interp->result);
  467.       else
  468. printf("%sn", interp->result);
  469.     }
  470.   return self;
  471. }
  472. #else /* HAVE_READLINE */
  473. {
  474.     char buffer[1000];
  475.     const char *cmd;
  476.     int result, gotPartial;
  477.     static Tcl_DString command; /* Used to buffer incomplete commands being
  478.  * read from stdin. */
  479.     gotPartial = 0;
  480.     Tcl_DStringInit(&command);
  481.     for (;;) {
  482. clearerr(stdin);
  483. /* I could add code to do something like tcl_prompt1 */
  484. if (gotPartial)
  485.   fputs (DEFAULT_PARTIAL_PROMPT, stdout);
  486. else
  487.   fputs (DEFAULT_PROMPT, stdout);
  488. fflush (stdout);
  489. if (fgets (buffer, 1000, stdin) == NULL) {
  490.     if (!gotPartial) {
  491.       printf("n");
  492.       return self;
  493.     }
  494.     buffer[0] = 0;
  495. }
  496. cmd = Tcl_DStringAppend(&command, buffer, -1);
  497. if ((buffer[0] != 0) && !Tcl_CommandComplete ((char *)cmd)) {
  498.     gotPartial = 1;
  499.     continue;
  500. }
  501. gotPartial = 0;
  502. result = Tcl_RecordAndEval (interp, (char *)cmd, 0);
  503. Tcl_DStringFree(&command);
  504. if (result != TCL_OK) {
  505.     fprintf(stderr, "%sn", interp->result);
  506. } else if (*interp->result != 0) {
  507.     printf("%sn", interp->result);
  508. }
  509.     }
  510.     return self;
  511. }
  512. #endif /* HAVE_READLINE */
  513. @end