os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/mac/tclMacOSA.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 /* 
     2  * tclMacOSA.c --
     3  *
     4  *	This contains the initialization routines, and the implementation of
     5  *	the OSA and Component commands.  These commands allow you to connect
     6  *	with the AppleScript or any other OSA component to compile and execute
     7  *	scripts.
     8  *
     9  * Copyright (c) 1996 Lucent Technologies and Jim Ingham
    10  * Copyright (c) 1997 Sun Microsystems, Inc.
    11  *
    12  * See the file "License Terms" for information on usage and redistribution
    13  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    14  *
    15  * RCS: @(#) $Id: tclMacOSA.c,v 1.10 2002/10/09 11:54:30 das Exp $
    16  */
    17 
    18 #define MAC_TCL
    19 
    20 #include <Aliases.h>
    21 #include <string.h>
    22 #include <AppleEvents.h>
    23 #include <AppleScript.h>
    24 #include <OSA.h>
    25 #include <OSAGeneric.h>
    26 #include <Script.h>
    27 
    28 #include <FullPath.h>
    29 #include <components.h>
    30 
    31 #include <resources.h>
    32 #include <FSpCompat.h>
    33 /* 
    34  * The following two Includes are from the More Files package.
    35  */
    36 #include <MoreFiles.h>
    37 #include <FullPath.h>
    38 
    39 #include "tcl.h"
    40 #include "tclInt.h"
    41 
    42 /*
    43  * I need this only for the call to FspGetFullPath,
    44  * I'm really not poking my nose where it does not belong!
    45  */
    46 #include "tclMacInt.h"
    47 
    48 /*
    49  * Data structures used by the OSA code.
    50  */
    51 typedef struct tclOSAScript {
    52     OSAID scriptID;
    53     OSType languageID;
    54     long modeFlags;
    55 } tclOSAScript;
    56 
    57 typedef struct tclOSAContext {
    58 	OSAID contextID;
    59 } tclOSAContext;
    60 
    61 typedef struct tclOSAComponent {
    62 	char *theName;
    63 	ComponentInstance theComponent; /* The OSA Component represented */
    64 	long componentFlags;
    65 	OSType languageID;
    66 	char *languageName;
    67 	Tcl_HashTable contextTable;    /* Hash Table linking the context names & ID's */
    68 	Tcl_HashTable scriptTable;
    69 	Tcl_Interp *theInterp;
    70 	OSAActiveUPP defActiveProc;
    71 	long defRefCon;
    72 } tclOSAComponent;
    73 
    74 /*
    75  * Prototypes for static procedures. 
    76  */
    77 
    78 static pascal OSErr	TclOSAActiveProc _ANSI_ARGS_((long refCon));
    79 static int		TclOSACompileCmd _ANSI_ARGS_((Tcl_Interp *interp,
    80 		 	    tclOSAComponent *OSAComponent, int argc,
    81 			    CONST char **argv));
    82 static int 		tclOSADecompileCmd _ANSI_ARGS_((Tcl_Interp * Interp,
    83 			    tclOSAComponent *OSAComponent, int argc,
    84 			    CONST char **argv));
    85 static int 		tclOSADeleteCmd _ANSI_ARGS_((Tcl_Interp *interp,
    86 			    tclOSAComponent *OSAComponent, int argc,
    87 			    CONST char **argv));
    88 static int 		tclOSAExecuteCmd _ANSI_ARGS_((Tcl_Interp *interp,
    89 			    tclOSAComponent *OSAComponent, int argc,
    90 			    CONST char **argv));
    91 static int 		tclOSAInfoCmd _ANSI_ARGS_((Tcl_Interp *interp,
    92 			    tclOSAComponent *OSAComponent, int argc,
    93 			    CONST char **argv));
    94 static int 		tclOSALoadCmd _ANSI_ARGS_((Tcl_Interp *interp,
    95 			    tclOSAComponent *OSAComponent, int argc,
    96 			    CONST char **argv));
    97 static int 		tclOSARunCmd _ANSI_ARGS_((Tcl_Interp *interp,
    98 			    tclOSAComponent *OSAComponent, int argc,
    99 			    CONST char **argv));
   100 static int 		tclOSAStoreCmd _ANSI_ARGS_((Tcl_Interp *interp,
   101 			    tclOSAComponent *OSAComponent, int argc,
   102 			    CONST char **argv));
   103 static void		GetRawDataFromDescriptor _ANSI_ARGS_((AEDesc *theDesc,
   104 			    Ptr destPtr, Size destMaxSize, Size *actSize));
   105 static OSErr 		GetCStringFromDescriptor _ANSI_ARGS_((
   106 			    AEDesc *sourceDesc, char *resultStr,
   107 			    Size resultMaxSize,Size *resultSize));
   108 static int 		Tcl_OSAComponentCmd _ANSI_ARGS_((ClientData clientData,
   109 			    Tcl_Interp *interp, int argc, CONST char **argv)); 
   110 static void 		getSortedHashKeys _ANSI_ARGS_((Tcl_HashTable *theTable,
   111 			    CONST char *pattern, Tcl_DString *theResult));
   112 static int 		ASCIICompareProc _ANSI_ARGS_((const void *first,
   113 			    const void *second));
   114 static int 		Tcl_OSACmd _ANSI_ARGS_((ClientData clientData,
   115 			    Tcl_Interp *interp, int argc, CONST char **argv)); 
   116 static void 		tclOSAClose _ANSI_ARGS_((ClientData clientData));
   117 /*static void 		tclOSACloseAll _ANSI_ARGS_((ClientData clientData));*/
   118 static tclOSAComponent *tclOSAMakeNewComponent _ANSI_ARGS_((Tcl_Interp *interp,
   119 			    char *cmdName, char *languageName,
   120 			    OSType scriptSubtype, long componentFlags));  
   121 static int 		prepareScriptData _ANSI_ARGS_((int argc, CONST char **argv,
   122 			    Tcl_DString *scrptData ,AEDesc *scrptDesc)); 
   123 static void 		tclOSAResultFromID _ANSI_ARGS_((Tcl_Interp *interp,
   124 			    ComponentInstance theComponent, OSAID resultID));
   125 static void 		tclOSAASError _ANSI_ARGS_((Tcl_Interp * interp,
   126 			    ComponentInstance theComponent, char *scriptSource));
   127 static int 		tclOSAGetContextID _ANSI_ARGS_((tclOSAComponent *theComponent, 
   128 			    CONST char *contextName, OSAID *theContext));
   129 static void 		tclOSAAddContext _ANSI_ARGS_((tclOSAComponent *theComponent, 
   130 			    char *contextName, const OSAID theContext));						
   131 static int 		tclOSAMakeContext _ANSI_ARGS_((tclOSAComponent *theComponent, 
   132 			    CONST char *contextName, OSAID *theContext));						
   133 static int 		tclOSADeleteContext _ANSI_ARGS_((tclOSAComponent *theComponent,
   134 			    CONST char *contextName)); 
   135 static int 		tclOSALoad _ANSI_ARGS_((Tcl_Interp *interp, 
   136 			    tclOSAComponent *theComponent, CONST char *resourceName, 
   137 			    int resourceNumber, CONST char *fileName,OSAID *resultID));
   138 static int 		tclOSAStore _ANSI_ARGS_((Tcl_Interp *interp, 
   139 			    tclOSAComponent *theComponent, CONST char *resourceName, 
   140 			    int resourceNumber, CONST char *scriptName, CONST char *fileName));
   141 static int 		tclOSAAddScript _ANSI_ARGS_((tclOSAComponent *theComponent,
   142 			    char *scriptName, long modeFlags, OSAID scriptID)); 		
   143 static int 		tclOSAGetScriptID _ANSI_ARGS_((tclOSAComponent *theComponent,
   144 			    CONST char *scriptName, OSAID *scriptID)); 
   145 static tclOSAScript *	tclOSAGetScript _ANSI_ARGS_((tclOSAComponent *theComponent,
   146 			    CONST char *scriptName)); 
   147 static int 		tclOSADeleteScript _ANSI_ARGS_((tclOSAComponent *theComponent,
   148 			    CONST char *scriptName,char *errMsg));
   149 
   150 /*
   151  * "export" is a MetroWerks specific pragma.  It flags the linker that  
   152  * any symbols that are defined when this pragma is on will be exported 
   153  * to shared libraries that link with this library.
   154  */
   155  
   156 
   157 #pragma export on
   158 int Tclapplescript_Init( Tcl_Interp *interp );
   159 #pragma export reset
   160 
   161 /*
   162  *----------------------------------------------------------------------
   163  *
   164  * Tclapplescript_Init --
   165  *
   166  *	Initializes the the OSA command which opens connections to
   167  *	OSA components, creates the AppleScript command, which opens an 
   168  *	instance of the AppleScript component,and constructs the table of
   169  *	available languages.
   170  *
   171  * Results:
   172  *	A standard Tcl result.
   173  *
   174  * Side Effects:
   175  *	Opens one connection to the AppleScript component, if 
   176  *	available.  Also builds up a table of available OSA languages,
   177  *	and creates the OSA command.
   178  *
   179  *----------------------------------------------------------------------
   180  */
   181 
   182 int 
   183 Tclapplescript_Init(
   184     Tcl_Interp *interp)		/* Tcl interpreter. */
   185 {
   186     char *errMsg = NULL;
   187     OSErr myErr = noErr;
   188     Boolean gotAppleScript = false;
   189     Boolean GotOneOSALanguage = false;
   190     ComponentDescription compDescr = {
   191 	kOSAComponentType,
   192 	(OSType) 0,
   193 	(OSType) 0,
   194 	(long) 0,
   195 	(long) 0
   196     }, *foundComp;
   197     Component curComponent = (Component) 0;
   198     ComponentInstance curOpenComponent;
   199     Tcl_HashTable *ComponentTable;
   200     Tcl_HashTable *LanguagesTable;
   201     Tcl_HashEntry *hashEntry;
   202     int newPtr;
   203     AEDesc componentName = { typeNull, NULL };
   204     char nameStr[32];			
   205     Size nameLen;
   206     long appleScriptFlags;
   207 	
   208     /* 
   209      * Perform the required stubs magic...
   210      */
   211      	
   212     if (!Tcl_InitStubs(interp, "8.2", 0)) {
   213 	return TCL_ERROR;
   214     }
   215 
   216     /* 
   217      * Here We Will Get The Available Osa Languages, Since They Can Only Be 
   218      * Registered At Startup...  If You Dynamically Load Components, This
   219      * Will Fail, But This Is Not A Common Thing To Do.
   220      */
   221 	 
   222     LanguagesTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
   223 	
   224     if (LanguagesTable == NULL) {
   225 	panic("Memory Error Allocating Languages Hash Table");
   226     }
   227 	
   228     Tcl_SetAssocData(interp, "OSAScript_LangTable", NULL, LanguagesTable);
   229     Tcl_InitHashTable(LanguagesTable, TCL_STRING_KEYS);
   230 	
   231 			
   232     while ((curComponent = FindNextComponent(curComponent, &compDescr)) != 0) {
   233 	int nbytes = sizeof(ComponentDescription);
   234 	foundComp = (ComponentDescription *)
   235 	    ckalloc(sizeof(ComponentDescription));
   236 	myErr = GetComponentInfo(curComponent, foundComp, NULL, NULL, NULL);
   237 	if (foundComp->componentSubType ==
   238 		kOSAGenericScriptingComponentSubtype) {
   239 	    /* Skip the generic component */
   240 	    ckfree((char *) foundComp);
   241 	} else {
   242 	    GotOneOSALanguage = true;
   243 
   244 	    /*
   245 	     * This is gross: looks like I have to open the component just  
   246 	     * to get its name!!! GetComponentInfo is supposed to return
   247 	     * the name, but AppleScript always returns an empty string.
   248 	     */
   249 		 	
   250 	    curOpenComponent = OpenComponent(curComponent);
   251 	    if (curOpenComponent == NULL) {
   252 		Tcl_AppendResult(interp,"Error opening component",
   253 			(char *) NULL);
   254 		return TCL_ERROR;
   255 	    }
   256 			 
   257 	    myErr = OSAScriptingComponentName(curOpenComponent,&componentName);
   258 	    if (myErr == noErr) {
   259 		myErr = GetCStringFromDescriptor(&componentName,
   260 			nameStr, 31, &nameLen);
   261 		AEDisposeDesc(&componentName);
   262 	    }
   263 	    CloseComponent(curOpenComponent);
   264 
   265 	    if (myErr == noErr) {
   266 		hashEntry = Tcl_CreateHashEntry(LanguagesTable,
   267 			nameStr, &newPtr);
   268 		Tcl_SetHashValue(hashEntry, (ClientData) foundComp);
   269 	    } else {
   270 		Tcl_AppendResult(interp,"Error getting componentName.",
   271 			(char *) NULL);
   272 		return TCL_ERROR;
   273 	    }
   274 			
   275 	    /*
   276 	     * Make sure AppleScript is loaded, otherwise we will
   277 	     * not bother to make the AppleScript command.
   278 	     */
   279 	    if (foundComp->componentSubType == kAppleScriptSubtype) {
   280 		appleScriptFlags = foundComp->componentFlags;
   281 		gotAppleScript = true;
   282 	    }			
   283 	}
   284     }				
   285 
   286     /*
   287      * Create the OSA command.
   288      */
   289 	
   290     if (!GotOneOSALanguage) {
   291 	Tcl_AppendResult(interp,"Could not find any OSA languages",
   292 		(char *) NULL);
   293 	return TCL_ERROR;
   294     }
   295 	
   296     /*
   297      * Create the Component Assoc Data & put it in the interpreter.
   298      */
   299 	
   300     ComponentTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
   301 	
   302     if (ComponentTable == NULL) {
   303 	panic("Memory Error Allocating Hash Table");
   304     }
   305 	
   306     Tcl_SetAssocData(interp, "OSAScript_CompTable", NULL, ComponentTable);
   307 			
   308     Tcl_InitHashTable(ComponentTable, TCL_STRING_KEYS);
   309 
   310     /*
   311      * The OSA command is not currently supported.	 
   312     Tcl_CreateCommand(interp, "OSA", Tcl_OSACmd, (ClientData) NULL,
   313 	    (Tcl_CmdDeleteProc *) NULL);
   314      */
   315      
   316     /* 
   317      * Open up one AppleScript component, with a default context
   318      * and tie it to the AppleScript command.
   319      * If the user just wants single-threaded AppleScript execution
   320      * this should be enough.
   321      *
   322      */
   323 	 
   324     if (gotAppleScript) {
   325 	if (tclOSAMakeNewComponent(interp, "AppleScript",
   326 		"AppleScript English", kAppleScriptSubtype,
   327 		appleScriptFlags) == NULL ) {
   328 	    return TCL_ERROR;
   329 	}
   330     }
   331 
   332     return Tcl_PkgProvide(interp, "OSAConnect", "1.0");
   333 }
   334 
   335 /*
   336  *---------------------------------------------------------------------- 
   337  *
   338  * Tcl_OSACmd --
   339  *
   340  *	This is the command that provides the interface to the OSA
   341  *	component manager.  The subcommands are: close: close a component, 
   342  *	info: get info on components open, and open: get a new connection
   343  *	with the Scripting Component
   344  *
   345  * Results:
   346  *  	A standard Tcl result.
   347  *
   348  * Side effects:
   349  *  	Depends on the subcommand, see the user documentation
   350  *	for more details.
   351  *
   352  *----------------------------------------------------------------------
   353  */
   354  
   355 int 
   356 Tcl_OSACmd(
   357     ClientData clientData,
   358     Tcl_Interp *interp,
   359     int argc,
   360     CONST char **argv)
   361 {
   362     static unsigned short componentCmdIndex = 0;
   363     char autoName[32];
   364     char c;
   365     int length;
   366     Tcl_HashTable *ComponentTable = NULL;
   367 	
   368 
   369     if (argc == 1) {
   370 	Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
   371 		argv[0], " option\"", (char *) NULL);
   372 	return TCL_ERROR;
   373     }
   374 	
   375     c = *argv[1];
   376     length = strlen(argv[1]);
   377 	
   378     /*
   379      * Query out the Component Table, since most of these commands use it...
   380      */
   381 	
   382     ComponentTable = (Tcl_HashTable *) Tcl_GetAssocData(interp,
   383 	    "OSAScript_CompTable", (Tcl_InterpDeleteProc **) NULL);
   384 	
   385     if (ComponentTable == NULL) {
   386 	Tcl_AppendResult(interp, "Error, could not get the Component Table",
   387 		" from the Associated data.", (char *) NULL);
   388 	return TCL_ERROR;
   389     }
   390 	
   391     if (c == 'c' && strncmp(argv[1],"close",length) == 0) {
   392 	Tcl_HashEntry *hashEntry;
   393 	if (argc != 3) {
   394 	    Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
   395 		    argv[0], " ",argv[1], " componentName\"",
   396 		    (char *) NULL);
   397 	    return TCL_ERROR;
   398 	}
   399 		
   400 	if ((hashEntry = Tcl_FindHashEntry(ComponentTable,argv[2])) == NULL) {
   401 	    Tcl_AppendResult(interp, "Component \"", argv[2], "\" not found",
   402 		    (char *) NULL);
   403 	    return TCL_ERROR;
   404 	} else {
   405 	    Tcl_DeleteCommand(interp,argv[2]);
   406 	    return TCL_OK;
   407 	}
   408     } else if (c == 'o' && strncmp(argv[1],"open",length) == 0) {
   409 	/*
   410 	 * Default language is AppleScript.
   411 	 */
   412 	OSType scriptSubtype = kAppleScriptSubtype;
   413 	char *languageName = "AppleScript English";
   414 	char *errMsg = NULL;
   415 	ComponentDescription *theCD;
   416 
   417 	argv += 2;
   418 	argc -= 2;
   419 		 
   420 	while (argc > 0 ) {
   421 	    if (*argv[0] == '-') {
   422 		c = *(argv[0] + 1);
   423 		if (c == 'l' && strcmp(argv[0] + 1, "language") == 0) {
   424 		    if (argc == 1) {
   425 			Tcl_AppendResult(interp,
   426 				"Error - no language provided for the -language switch",
   427 				(char *) NULL);
   428 			return TCL_ERROR;
   429 		    } else {
   430 			Tcl_HashEntry *hashEntry;
   431 			Tcl_HashSearch search;
   432 			Boolean gotIt = false;
   433 			Tcl_HashTable *LanguagesTable;
   434 						
   435 			/*
   436 			 * Look up the language in the languages table
   437 			 * Do a simple strstr match, so AppleScript
   438 			 * will match "AppleScript English"...
   439 			 */
   440 						
   441 			LanguagesTable = Tcl_GetAssocData(interp,
   442 				"OSAScript_LangTable",
   443 				(Tcl_InterpDeleteProc **) NULL);
   444 							
   445 			for (hashEntry =
   446 				 Tcl_FirstHashEntry(LanguagesTable, &search);
   447 			     hashEntry != NULL;
   448 			     hashEntry = Tcl_NextHashEntry(&search)) {
   449 			    languageName = Tcl_GetHashKey(LanguagesTable,
   450 				    hashEntry);
   451 			    if (strstr(languageName,argv[1]) != NULL) {
   452 				theCD = (ComponentDescription *)
   453 				    Tcl_GetHashValue(hashEntry);
   454 				gotIt = true;
   455 				break;
   456 			    }
   457 			}
   458 			if (!gotIt) {
   459 			    Tcl_AppendResult(interp,
   460 				    "Error, could not find the language \"",
   461 				    argv[1],
   462 				    "\" in the list of known languages.",
   463 				    (char *) NULL);
   464 			    return TCL_ERROR;
   465 			}
   466 		    }
   467 		}
   468 		argc -= 2;
   469 		argv += 2;				
   470 	    } else {
   471 		Tcl_AppendResult(interp, "Expected a flag, but got ",
   472 			argv[0], (char *) NULL);
   473 		return TCL_ERROR;
   474 	    }
   475 	}
   476 			
   477 	sprintf(autoName, "OSAComponent%-d", componentCmdIndex++);
   478 	if (tclOSAMakeNewComponent(interp, autoName, languageName,
   479 		theCD->componentSubType, theCD->componentFlags) == NULL ) {
   480 	    return TCL_ERROR;
   481 	} else {
   482 	    Tcl_SetResult(interp,autoName,TCL_VOLATILE);
   483 	    return TCL_OK;	
   484 	}
   485 		
   486     } else if (c == 'i' && strncmp(argv[1],"info",length) == 0) {
   487 	if (argc == 2) {
   488 	    Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
   489 		    argv[0], " ", argv[1], " what\"",
   490 		    (char *) NULL);
   491 	    return TCL_ERROR;
   492 	}
   493 		 	
   494 	c = *argv[2];
   495 	length = strlen(argv[2]);
   496 		
   497 	if (c == 'c' && strncmp(argv[2], "components", length) == 0) {
   498 	    Tcl_DString theResult;
   499 			
   500 	    Tcl_DStringInit(&theResult);
   501 			
   502 	    if (argc == 3) {
   503 		getSortedHashKeys(ComponentTable,(char *) NULL, &theResult);
   504 	    } else if (argc == 4) {
   505 		getSortedHashKeys(ComponentTable, argv[3], &theResult);
   506 	    } else {
   507 		Tcl_AppendResult(interp, "Error: wrong # of arguments",
   508 			", should be \"", argv[0], " ", argv[1], " ",
   509 			argv[2], " ?pattern?\".", (char *) NULL);
   510 		return TCL_ERROR;
   511 	    }
   512 	    Tcl_DStringResult(interp, &theResult);
   513 	    return TCL_OK;			
   514 	} else if (c == 'l' && strncmp(argv[2],"languages",length) == 0) {
   515 	    Tcl_DString theResult;
   516 	    Tcl_HashTable *LanguagesTable;
   517 			
   518 	    Tcl_DStringInit(&theResult);
   519 	    LanguagesTable = Tcl_GetAssocData(interp,
   520 		    "OSAScript_LangTable", (Tcl_InterpDeleteProc **) NULL);
   521 							
   522 	    if (argc == 3) {
   523 		getSortedHashKeys(LanguagesTable, (char *) NULL, &theResult);
   524 	    } else if (argc == 4) {
   525 		getSortedHashKeys(LanguagesTable, argv[3], &theResult);
   526 	    } else {
   527 		Tcl_AppendResult(interp, "Error: wrong # of arguments",
   528 			", should be \"", argv[0], " ", argv[1], " ",
   529 			argv[2], " ?pattern?\".", (char *) NULL);
   530 		return TCL_ERROR;
   531 	    }
   532 	    Tcl_DStringResult(interp,&theResult);
   533 	    return TCL_OK;			
   534 	} else {
   535 	    Tcl_AppendResult(interp, "Unknown option: ", argv[2],
   536 		    " for OSA info, should be one of",
   537 		    " \"components\" or \"languages\"",
   538 		    (char *) NULL);
   539 	    return TCL_ERROR;
   540 	}
   541     } else {
   542 	Tcl_AppendResult(interp, "Unknown option: ", argv[1],
   543 		", should be one of \"open\", \"close\" or \"info\".",
   544 		(char *) NULL);
   545 	return TCL_ERROR;
   546     }
   547     return TCL_OK;
   548 }
   549 
   550 /* 
   551  *----------------------------------------------------------------------
   552  *
   553  * Tcl_OSAComponentCmd --
   554  *
   555  *	This is the command that provides the interface with an OSA
   556  *	component.  The sub commands are:
   557  *	- compile ? -context context? scriptData
   558  *		compiles the script data, returns the ScriptID
   559  *	- decompile ? -context context? scriptData
   560  *		decompiles the script data, source code
   561  *	- execute ?-context context? scriptData
   562  *		compiles and runs script data
   563  *	- info what: get component info
   564  *	- load ?-flags values? fileName
   565  *		loads & compiles script data from fileName
   566  *	- run scriptId ?options?
   567  *		executes the compiled script 
   568  *
   569  * Results:
   570  *	A standard Tcl result
   571  *
   572  * Side Effects:
   573  *	Depends on the subcommand, see the user documentation
   574  *	for more details.
   575  *
   576  *----------------------------------------------------------------------
   577  */
   578  
   579 int 
   580 Tcl_OSAComponentCmd(
   581     ClientData clientData,
   582     Tcl_Interp *interp, 
   583     int argc,
   584     CONST char **argv)
   585 {
   586     int length;
   587     char c;
   588 	
   589     tclOSAComponent *OSAComponent = (tclOSAComponent *) clientData;
   590 	
   591     if (argc == 1) {
   592 	Tcl_AppendResult(interp, "wrong # args: should be \"",
   593 		argv[0], " option ?arg ...?\"",
   594 		(char *) NULL);
   595 	return TCL_ERROR;
   596     }
   597 	
   598     c = *argv[1];
   599     length = strlen(argv[1]);
   600     if (c == 'c' && strncmp(argv[1], "compile", length) == 0) {
   601 	return TclOSACompileCmd(interp, OSAComponent, argc, argv);
   602     } else if (c == 'l' && strncmp(argv[1], "load", length) == 0) {
   603 	return tclOSALoadCmd(interp, OSAComponent, argc, argv);
   604     } else if (c == 'e' && strncmp(argv[1], "execute", length) == 0) {
   605 	return tclOSAExecuteCmd(interp, OSAComponent, argc, argv);
   606     } else if (c == 'i' && strncmp(argv[1], "info", length) == 0) {
   607 	return tclOSAInfoCmd(interp, OSAComponent, argc, argv);
   608     } else if (c == 'd' && strncmp(argv[1], "decompile", length) == 0) {
   609 	return tclOSADecompileCmd(interp, OSAComponent, argc, argv);
   610     } else if (c == 'd' && strncmp(argv[1], "delete", length) == 0) {
   611 	return tclOSADeleteCmd(interp, OSAComponent, argc, argv);
   612     } else if (c == 'r' && strncmp(argv[1], "run", length) == 0) {
   613 	return tclOSARunCmd(interp, OSAComponent, argc, argv);
   614     } else if (c == 's' && strncmp(argv[1], "store", length) == 0) {
   615 	return tclOSAStoreCmd(interp, OSAComponent, argc, argv);
   616     } else {
   617 	Tcl_AppendResult(interp,"bad option \"", argv[1],
   618 		"\": should be compile, decompile, delete, ",
   619 		 "execute, info, load, run or store",
   620 		 (char *) NULL);
   621 	return TCL_ERROR;
   622     }
   623 
   624     return TCL_OK;
   625 }
   626  
   627 /*
   628  *----------------------------------------------------------------------
   629  *
   630  * TclOSACompileCmd --
   631  *
   632  *	This is the compile subcommand for the component command.
   633  *
   634  * Results:
   635  *	A standard Tcl result
   636  *
   637  * Side Effects:
   638  *  	Compiles the script data either into a script or a script
   639  *	context.  Adds the script to the component's script or context
   640  *	table.  Sets interp's result to the name of the new script or
   641  *	context.
   642  *
   643  *----------------------------------------------------------------------
   644  */
   645  
   646 static int 
   647 TclOSACompileCmd(
   648     Tcl_Interp *interp,
   649     tclOSAComponent *OSAComponent,
   650     int argc,
   651     CONST char **argv)
   652 {
   653     int  tclError = TCL_OK;
   654     int augment = 1;
   655     int makeContext = 0;
   656     char c;
   657     char autoName[16];
   658     char buffer[32];
   659     char *resultName;
   660     Boolean makeNewContext = false;
   661     Tcl_DString scrptData;
   662     AEDesc scrptDesc = { typeNull, NULL };
   663     long modeFlags = kOSAModeCanInteract;
   664     OSAID resultID = kOSANullScript;
   665     OSAID contextID = kOSANullScript;
   666     OSAID parentID = kOSANullScript;
   667     OSAError osaErr = noErr;
   668 	
   669     if (!(OSAComponent->componentFlags && kOSASupportsCompiling)) {
   670 	Tcl_AppendResult(interp,
   671 		"OSA component does not support compiling",
   672 		(char *) NULL);
   673 	return TCL_ERROR;
   674     }
   675 
   676     /* 
   677      * This signals that we should make up a name, which is the
   678      * default behavior:
   679      */
   680 	 
   681     autoName[0] = '\0';
   682     resultName = NULL;
   683 	
   684     if (argc == 2) {
   685 	numArgs:
   686 	Tcl_AppendResult(interp,
   687 		"wrong # args: should be \"", argv[0], " ", argv[1],
   688 		" ?options? code\"",(char *) NULL);
   689 	return TCL_ERROR;
   690     } 
   691 
   692     argv += 2;
   693     argc -= 2;
   694 
   695     /*
   696      * Do the argument parsing.
   697      */
   698 	
   699     while (argc > 0) {
   700 		
   701 	if (*argv[0] == '-') {
   702 	    c = *(argv[0] + 1);
   703 			
   704 	    /*
   705 	     * "--" is the only switch that has no value, stops processing
   706 	     */
   707 			
   708 	    if (c == '-' && *(argv[0] + 2) == '\0') {
   709 		argv += 1;
   710 		argc--;
   711 		break;
   712 	    }
   713 			
   714 	    /*
   715 	     * So we can check here a switch with no value.
   716 	     */
   717 			
   718 	    if (argc == 1)  {
   719 		Tcl_AppendResult(interp,
   720 			"no value given for switch: ",
   721 			argv[0], (char *) NULL);
   722 		return TCL_ERROR;
   723 	    }
   724 			
   725 	    if (c == 'c' && strcmp(argv[0] + 1, "context") == 0) {
   726 		if (Tcl_GetBoolean(interp, argv[1], &makeContext) != TCL_OK) {
   727 		    return TCL_ERROR;
   728 		}
   729 	    } else if (c == 'a' && strcmp(argv[0] + 1, "augment") == 0) {
   730 		/*
   731 		 * Augment the current context which implies making a context.
   732 		 */
   733 
   734 		if (Tcl_GetBoolean(interp, argv[1], &augment) != TCL_OK) {
   735 		    return TCL_ERROR;
   736 		}
   737 		makeContext = 1;
   738 	    } else if (c == 'n' && strcmp(argv[0] + 1, "name") == 0) {
   739 		strncpy(autoName, argv[1], 15);
   740 		autoName[15] = '\0';
   741 		resultName = autoName;
   742 	    } else if (c == 'p' && strcmp(argv[0] + 1,"parent") == 0) {
   743 		/*
   744 		 * Since this implies we are compiling into a context, 
   745 		 * set makeContext here
   746 		 */
   747 		if (tclOSAGetContextID(OSAComponent,
   748 			argv[1], &parentID) != TCL_OK) {
   749 		    Tcl_AppendResult(interp, "context not found \"",
   750 			    argv[1], "\"", (char *) NULL);
   751 		    return TCL_ERROR;
   752 		}
   753 		makeContext = 1;
   754 	    } else {
   755 		Tcl_AppendResult(interp, "bad option \"", argv[0],
   756 			"\": should be -augment, -context, -name or -parent",
   757 			 (char *) NULL);
   758 		return TCL_ERROR;
   759 	    }
   760 	    argv += 2;
   761 	    argc -= 2;
   762 			
   763 	} else {
   764 	    break;
   765 	}
   766     }
   767 
   768     /*
   769      * Make sure we have some data left...
   770      */
   771     if (argc == 0) {
   772 	goto numArgs;
   773     }
   774 	
   775     /* 
   776      * Now if we are making a context, see if it is a new one... 
   777      * There are three options here:
   778      * 1) There was no name provided, so we autoName it
   779      * 2) There was a name, then check and see if it already exists
   780      *  a) If yes, then makeNewContext is false
   781      *  b) Otherwise we are making a new context
   782      */
   783 
   784     if (makeContext) {
   785 	modeFlags |= kOSAModeCompileIntoContext;
   786 	if (resultName == NULL) {
   787 	    /*
   788 	     * Auto name the new context.
   789 	     */
   790 	    resultName = autoName;
   791 	    resultID = kOSANullScript;
   792 	    makeNewContext = true;
   793 	} else if (tclOSAGetContextID(OSAComponent,
   794 		resultName, &resultID) == TCL_OK) {
   795 	} else { 
   796 	    makeNewContext = true;
   797 	}
   798 		
   799 	/*
   800 	 * Deal with the augment now...
   801 	 */
   802 	if (augment && !makeNewContext) {
   803 	    modeFlags |= kOSAModeAugmentContext;
   804 	}
   805     } else if (resultName == NULL) {
   806 	resultName = autoName; /* Auto name the script */
   807     }
   808 	
   809     /*
   810      * Ok, now we have the options, so we can compile the script data.
   811      */
   812 			
   813     if (prepareScriptData(argc, argv, &scrptData, &scrptDesc) == TCL_ERROR) {
   814 	Tcl_DStringResult(interp, &scrptData);
   815 	AEDisposeDesc(&scrptDesc);
   816 	return TCL_ERROR;
   817     }
   818 
   819     /* 
   820      * If we want to use a parent context, we have to make the context 
   821      * by hand. Note, parentID is only specified when you make a new context. 
   822      */
   823 	
   824     if (parentID != kOSANullScript && makeNewContext) {
   825 	AEDesc contextDesc = { typeNull, NULL };
   826 
   827 	osaErr = OSAMakeContext(OSAComponent->theComponent,
   828 		&contextDesc, parentID, &resultID);
   829 	modeFlags |= kOSAModeAugmentContext;
   830     }
   831 	
   832     osaErr = OSACompile(OSAComponent->theComponent, &scrptDesc,
   833 	    modeFlags, &resultID);								
   834     if (osaErr == noErr) {
   835 	 
   836 	if (makeContext) {
   837 	    /* 
   838 	     * For the compiled context to be active, you need to run 
   839 	     * the code that is in the context.
   840 	     */
   841 	    OSAID activateID;
   842 
   843 	    osaErr = OSAExecute(OSAComponent->theComponent, resultID,
   844 		    resultID, kOSAModeCanInteract, &activateID);
   845 	    OSADispose(OSAComponent->theComponent, activateID);
   846 
   847 	    if (osaErr == noErr) {
   848 		if (makeNewContext) {
   849 		    /*
   850 		     * If we have compiled into a context, 
   851 		     * this is added to the context table 
   852 		     */
   853 					 
   854 		    tclOSAAddContext(OSAComponent, resultName, resultID);
   855 		}
   856 				
   857 		Tcl_SetResult(interp, resultName, TCL_VOLATILE);
   858 		tclError = TCL_OK;
   859 	    }
   860 	} else {
   861 	    /*
   862 	     * For a script, we return the script name.
   863 	     */
   864 	    tclOSAAddScript(OSAComponent, resultName, modeFlags, resultID);
   865 	    Tcl_SetResult(interp, resultName, TCL_VOLATILE);
   866 	    tclError = TCL_OK;	
   867 	}
   868     }
   869 	
   870     /* 
   871      * This catches the error either from the original compile, 
   872      * or from the execute in case makeContext == true
   873      */
   874 	 						
   875     if (osaErr == errOSAScriptError) {
   876 	OSADispose(OSAComponent->theComponent, resultID);
   877 	tclOSAASError(interp, OSAComponent->theComponent,
   878 		Tcl_DStringValue(&scrptData));
   879 	tclError = TCL_ERROR;
   880     } else if (osaErr != noErr)  {
   881 	sprintf(buffer, "Error #%-6ld compiling script", osaErr);
   882 	Tcl_AppendResult(interp, buffer, (char *) NULL);
   883 	tclError = TCL_ERROR;		
   884     } 
   885 
   886     Tcl_DStringFree(&scrptData);
   887     AEDisposeDesc(&scrptDesc);
   888 	
   889     return tclError;
   890 }
   891 
   892 /*
   893  *----------------------------------------------------------------------
   894  *
   895  * tclOSADecompileCmd --
   896  *
   897  * 	This implements the Decompile subcommand of the component command
   898  *
   899  * Results:
   900  *	A standard Tcl result.
   901  *
   902  * Side Effects:
   903  *  	Decompiles the script, and sets interp's result to the
   904  *	decompiled script data.
   905  *
   906  *----------------------------------------------------------------------
   907  */
   908  		
   909 static int 
   910 tclOSADecompileCmd(
   911     Tcl_Interp * interp,
   912     tclOSAComponent *OSAComponent,
   913     int argc, 
   914     CONST char **argv)
   915 {
   916     AEDesc resultingSourceData = { typeChar, NULL };
   917     OSAID scriptID;
   918     Boolean isContext;
   919     long result;
   920     OSErr sysErr = noErr;
   921  		
   922     if (argc == 2) {
   923 	Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
   924 		argv[0], " ",argv[1], " scriptName \"", (char *) NULL );
   925 	return TCL_ERROR;
   926     }
   927  	
   928     if (!(OSAComponent->componentFlags && kOSASupportsGetSource)) {
   929 	Tcl_AppendResult(interp,
   930 		"Error, this component does not support get source",
   931 		(char *) NULL);
   932 	return TCL_ERROR;
   933     }
   934  	
   935     if (tclOSAGetScriptID(OSAComponent, argv[2], &scriptID) == TCL_OK) {
   936 	isContext = false;
   937     } else if (tclOSAGetContextID(OSAComponent, argv[2], &scriptID)
   938 	    == TCL_OK ) {
   939 	isContext = true;
   940     } else { 
   941 	Tcl_AppendResult(interp, "Could not find script \"",
   942 		argv[2], "\"", (char *) NULL);
   943 	return TCL_ERROR;
   944     }
   945 	
   946     OSAGetScriptInfo(OSAComponent->theComponent, scriptID,
   947 	    kOSACanGetSource, &result);
   948 						
   949     sysErr = OSAGetSource(OSAComponent->theComponent, 
   950 	    scriptID, typeChar, &resultingSourceData);
   951 	
   952     if (sysErr == noErr) {
   953 	Tcl_DString theResult;
   954 	Tcl_DStringInit(&theResult);
   955 
   956 	Tcl_DStringAppend(&theResult, *resultingSourceData.dataHandle,
   957 		GetHandleSize(resultingSourceData.dataHandle));
   958 	Tcl_DStringResult(interp, &theResult);
   959 	AEDisposeDesc(&resultingSourceData);
   960 	return TCL_OK;
   961     } else {
   962 	Tcl_AppendResult(interp, "Error getting source data", (char *) NULL);
   963 	AEDisposeDesc(&resultingSourceData);
   964 	return TCL_ERROR;
   965     }
   966 }			
   967 	 	
   968 /*
   969  *----------------------------------------------------------------------
   970  *
   971  * tclOSADeleteCmd --
   972  *
   973  *	This implements the Delete subcommand of the Component command.
   974  *
   975  * Results:
   976  *	A standard Tcl result.
   977  *
   978  * Side Effects:
   979  *  	Deletes a script from the script list of the given component.
   980  *	Removes all references to the script, and frees the memory
   981  *	associated with it.
   982  *
   983  *----------------------------------------------------------------------
   984  */
   985  
   986 static int 
   987 tclOSADeleteCmd(
   988     Tcl_Interp *interp,
   989     tclOSAComponent *OSAComponent,
   990     int argc,
   991     CONST char **argv)
   992 {
   993     char c,*errMsg = NULL;
   994     int length;
   995  	
   996     if (argc < 4) {
   997 	Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
   998 		argv[0], " ", argv[1], " what scriptName", (char *) NULL);
   999 	return TCL_ERROR;
  1000     }
  1001  	
  1002     c = *argv[2];
  1003     length = strlen(argv[2]);
  1004     if (c == 'c' && strncmp(argv[2], "context", length) == 0) {
  1005 	if (strcmp(argv[3], "global") == 0) {
  1006 	    Tcl_AppendResult(interp, "You cannot delete the global context",
  1007 		    (char *) NULL);
  1008 	    return TCL_ERROR;
  1009 	} else if (tclOSADeleteContext(OSAComponent, argv[3]) != TCL_OK) {
  1010 	    Tcl_AppendResult(interp, "Error deleting script \"", argv[2],
  1011 		    "\": ", errMsg, (char *) NULL);
  1012 	    ckfree(errMsg);
  1013 	    return TCL_ERROR;
  1014 	}
  1015     } else if (c == 's' && strncmp(argv[2], "script", length) == 0) {
  1016 	if (tclOSADeleteScript(OSAComponent, argv[3], errMsg) != TCL_OK) {
  1017 	    Tcl_AppendResult(interp, "Error deleting script \"", argv[3],
  1018 		    "\": ", errMsg, (char *) NULL);
  1019 	    ckfree(errMsg);
  1020 	    return TCL_ERROR;
  1021 	}
  1022     } else {
  1023 	Tcl_AppendResult(interp,"Unknown value ", argv[2],
  1024 		" should be one of ",
  1025 		"\"context\" or \"script\".",
  1026 		(char *) NULL );
  1027 	return TCL_ERROR;
  1028     }
  1029     return TCL_OK;
  1030 }
  1031 
  1032 /*
  1033  *---------------------------------------------------------------------- 
  1034  *
  1035  * tclOSAExecuteCmd --
  1036  *
  1037  *	This implements the execute subcommand of the component command.
  1038  *
  1039  * Results:
  1040  *	A standard Tcl result.
  1041  *
  1042  * Side effects:
  1043  *	Executes the given script data, and sets interp's result to
  1044  *	the OSA component's return value.
  1045  *
  1046  *---------------------------------------------------------------------- 
  1047  */
  1048  
  1049 static int 
  1050 tclOSAExecuteCmd(
  1051     Tcl_Interp *interp,
  1052     tclOSAComponent *OSAComponent,
  1053     int argc,
  1054     CONST char **argv)
  1055 {
  1056     int tclError = TCL_OK, resID = 128;
  1057     char c,buffer[32],
  1058 	*contextName = NULL,*scriptName = NULL, *resName = NULL;
  1059     Boolean makeNewContext = false,makeContext = false;
  1060     AEDesc scrptDesc = { typeNull, NULL };
  1061     long modeFlags = kOSAModeCanInteract;
  1062     OSAID resultID = kOSANullScript,
  1063 	contextID = kOSANullScript,
  1064 	parentID = kOSANullScript;
  1065     Tcl_DString scrptData;
  1066     OSAError osaErr = noErr;
  1067     OSErr  sysErr = noErr;
  1068 
  1069     if (argc == 2) {
  1070 	Tcl_AppendResult(interp,
  1071 		"Error, no script data for \"", argv[0],
  1072 		" run\"", (char *) NULL);
  1073 	return TCL_ERROR;
  1074     } 
  1075 
  1076     argv += 2;
  1077     argc -= 2;
  1078 
  1079     /*
  1080      * Set the context to the global context by default.
  1081      * Then parse the argument list for switches
  1082      */
  1083     tclOSAGetContextID(OSAComponent, "global", &contextID);
  1084 	
  1085     while (argc > 0) {
  1086 		
  1087 	if (*argv[0] == '-') {
  1088 	    c = *(argv[0] + 1);
  1089 
  1090 	    /*
  1091 	     * "--" is the only switch that has no value.
  1092 	     */
  1093 			
  1094 	    if (c == '-' && *(argv[0] + 2) == '\0') {
  1095 		argv += 1;
  1096 		argc--;
  1097 		break;
  1098 	    }
  1099 			
  1100 	    /*
  1101 	     * So we can check here for a switch with no value.
  1102 	     */
  1103 			
  1104 	    if (argc == 1)  {
  1105 		Tcl_AppendResult(interp,
  1106 			"Error, no value given for switch ",
  1107 			argv[0], (char *) NULL);
  1108 		return TCL_ERROR;
  1109 	    }
  1110 			
  1111 	    if (c == 'c' && strcmp(argv[0] + 1, "context") == 0) {
  1112 		if (tclOSAGetContextID(OSAComponent,
  1113 			argv[1], &contextID) == TCL_OK) {
  1114 		} else {
  1115 		    Tcl_AppendResult(interp, "Script context \"",
  1116 			    argv[1], "\" not found", (char *) NULL);
  1117 		    return TCL_ERROR;
  1118 		}
  1119 	    } else { 
  1120 		Tcl_AppendResult(interp, "Error, invalid switch ", argv[0],
  1121 			" should be \"-context\"", (char *) NULL);
  1122 		return TCL_ERROR;
  1123 	    }
  1124 			
  1125 	    argv += 2;
  1126 	    argc -= 2;
  1127 	} else {
  1128 	    break;
  1129 	}
  1130     }
  1131 	
  1132     if (argc == 0) {
  1133 	Tcl_AppendResult(interp, "Error, no script data", (char *) NULL);
  1134 	return TCL_ERROR;
  1135     }
  1136 		
  1137     if (prepareScriptData(argc, argv, &scrptData, &scrptDesc) == TCL_ERROR) {
  1138 	Tcl_DStringResult(interp, &scrptData);
  1139 	AEDisposeDesc(&scrptDesc);
  1140 	return TCL_ERROR;
  1141     }
  1142     /*
  1143      * Now try to compile and run, but check to make sure the
  1144      * component supports the one shot deal
  1145      */
  1146     if (OSAComponent->componentFlags && kOSASupportsConvenience) {
  1147 	osaErr = OSACompileExecute(OSAComponent->theComponent,
  1148 		&scrptDesc, contextID, modeFlags, &resultID);
  1149     } else {
  1150 	/*
  1151 	 * If not, we have to do this ourselves
  1152 	 */
  1153 	if (OSAComponent->componentFlags && kOSASupportsCompiling) {
  1154 	    OSAID compiledID = kOSANullScript;
  1155 	    osaErr = OSACompile(OSAComponent->theComponent, &scrptDesc,
  1156 		    modeFlags, &compiledID);
  1157 	    if (osaErr == noErr) {
  1158 		osaErr = OSAExecute(OSAComponent->theComponent, compiledID,
  1159 			contextID, modeFlags, &resultID);
  1160 	    }
  1161 	    OSADispose(OSAComponent->theComponent, compiledID);
  1162 	} else {
  1163 	    /*
  1164 	     * The scripting component had better be able to load text data...
  1165 	     */
  1166 	    OSAID loadedID = kOSANullScript;
  1167 			
  1168 	    scrptDesc.descriptorType = OSAComponent->languageID;
  1169 	    osaErr = OSALoad(OSAComponent->theComponent, &scrptDesc,
  1170 		    modeFlags, &loadedID);
  1171 	    if (osaErr == noErr) {
  1172 		OSAExecute(OSAComponent->theComponent, loadedID,
  1173 			contextID, modeFlags, &resultID);
  1174 	    }
  1175 	    OSADispose(OSAComponent->theComponent, loadedID);
  1176 	}
  1177     }
  1178     if (osaErr == errOSAScriptError) {
  1179 	tclOSAASError(interp, OSAComponent->theComponent,
  1180 		Tcl_DStringValue(&scrptData));
  1181 	tclError = TCL_ERROR;
  1182     } else if (osaErr != noErr) {
  1183 	sprintf(buffer, "Error #%-6ld compiling script", osaErr);
  1184 	Tcl_AppendResult(interp, buffer, (char *) NULL);
  1185 	tclError = TCL_ERROR;		
  1186     } else  {
  1187 	tclOSAResultFromID(interp, OSAComponent->theComponent, resultID);
  1188 	osaErr = OSADispose(OSAComponent->theComponent, resultID);
  1189 	tclError = TCL_OK;
  1190     } 
  1191 
  1192     Tcl_DStringFree(&scrptData);
  1193     AEDisposeDesc(&scrptDesc);	
  1194 
  1195     return tclError;	
  1196 } 
  1197 
  1198 /*
  1199  *----------------------------------------------------------------------
  1200  *
  1201  * tclOSAInfoCmd --
  1202  *
  1203  * This implements the Info subcommand of the component command
  1204  *
  1205  * Results:
  1206  *	A standard Tcl result.
  1207  *
  1208  * Side effects:
  1209  *	Info on scripts and contexts.  See the user documentation for details.
  1210  *
  1211  *----------------------------------------------------------------------
  1212  */
  1213 static int 
  1214 tclOSAInfoCmd(
  1215     Tcl_Interp *interp,
  1216     tclOSAComponent *OSAComponent,
  1217     int argc, 
  1218     CONST char **argv)
  1219 {
  1220     char c;
  1221     int length;
  1222     Tcl_DString theResult;
  1223 	
  1224     if (argc == 2) {
  1225 	Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
  1226 		argv[0], " ", argv[1], " what \"", (char *) NULL );
  1227 	return TCL_ERROR;
  1228     }
  1229  	
  1230     c = *argv[2];
  1231     length = strlen(argv[2]);
  1232     if (c == 's' && strncmp(argv[2], "scripts", length) == 0) {
  1233 	Tcl_DStringInit(&theResult);
  1234 	if (argc == 3) {
  1235 	    getSortedHashKeys(&OSAComponent->scriptTable, (char *) NULL,
  1236 		    &theResult);
  1237 	} else if (argc == 4) {
  1238 	    getSortedHashKeys(&OSAComponent->scriptTable, argv[3], &theResult);
  1239 	} else {
  1240 	    Tcl_AppendResult(interp, "Error: wrong # of arguments,",
  1241 		    " should be \"", argv[0], " ", argv[1], " ",
  1242 		    argv[2], " ?pattern?", (char *) NULL);
  1243 	    return TCL_ERROR;
  1244 	}
  1245 	Tcl_DStringResult(interp, &theResult);
  1246 	return TCL_OK;			
  1247     } else if (c == 'c' && strncmp(argv[2], "contexts", length) == 0) {
  1248 	Tcl_DStringInit(&theResult);		
  1249 	if (argc == 3) {
  1250 	    getSortedHashKeys(&OSAComponent->contextTable, (char *) NULL,
  1251 		   &theResult);
  1252 	} else if (argc == 4) {
  1253 	    getSortedHashKeys(&OSAComponent->contextTable,
  1254 		    argv[3], &theResult);
  1255 	} else {
  1256 	    Tcl_AppendResult(interp, "Error: wrong # of arguments for ,",
  1257 		    " should be \"", argv[0], " ", argv[1], " ",
  1258 		    argv[2], " ?pattern?", (char *) NULL);
  1259 	    return TCL_ERROR;
  1260 	}
  1261 	Tcl_DStringResult(interp, &theResult);
  1262 	return TCL_OK;			
  1263     } else if (c == 'l' && strncmp(argv[2], "language", length) == 0) {
  1264 	Tcl_SetResult(interp, OSAComponent->languageName, TCL_STATIC);
  1265 	return TCL_OK;
  1266     } else {
  1267 	Tcl_AppendResult(interp, "Unknown argument \"", argv[2],
  1268 		"\" for \"", argv[0], " info \", should be one of ",
  1269 		"\"scripts\" \"language\", or \"contexts\"",
  1270 		(char *) NULL);
  1271 	return TCL_ERROR;
  1272     } 
  1273 }
  1274 		
  1275 /*
  1276  *----------------------------------------------------------------------
  1277  *
  1278  * tclOSALoadCmd --
  1279  *
  1280  *	This is the load subcommand for the Component Command
  1281  *
  1282  *
  1283  * Results:
  1284  *	A standard Tcl result.
  1285  *
  1286  * Side effects:
  1287  *	Loads script data from the given file, creates a new context
  1288  *	for it, and sets interp's result to the name of the new context.
  1289  *
  1290  *----------------------------------------------------------------------
  1291  */
  1292  
  1293 static int 
  1294 tclOSALoadCmd(
  1295     Tcl_Interp *interp,
  1296     tclOSAComponent *OSAComponent,
  1297     int argc,
  1298     CONST char **argv)
  1299 {
  1300     int tclError = TCL_OK, resID = 128;
  1301     char c, autoName[24],
  1302 	*contextName = NULL, *scriptName = NULL;
  1303     CONST char *resName = NULL;
  1304     Boolean makeNewContext = false, makeContext = false;
  1305     AEDesc scrptDesc = { typeNull, NULL };
  1306     long modeFlags = kOSAModeCanInteract;
  1307     OSAID resultID = kOSANullScript,
  1308 	contextID = kOSANullScript,
  1309 	parentID = kOSANullScript;
  1310     OSAError osaErr = noErr;
  1311     OSErr  sysErr = noErr;
  1312     long scptInfo;
  1313 	
  1314     autoName[0] = '\0';
  1315     scriptName = autoName;
  1316     contextName = autoName;
  1317 	
  1318     if (argc == 2) {
  1319 	Tcl_AppendResult(interp,
  1320 		"Error, no data for \"", argv[0], " ", argv[1],
  1321 		"\"", (char *) NULL);
  1322 	return TCL_ERROR;
  1323     } 
  1324 
  1325     argv += 2;
  1326     argc -= 2;
  1327 
  1328     /*
  1329      * Do the argument parsing.
  1330      */
  1331 	
  1332     while (argc > 0) {
  1333 		
  1334 	if (*argv[0] == '-') {
  1335 	    c = *(argv[0] + 1);
  1336 			
  1337 	    /*
  1338 	     * "--" is the only switch that has no value.
  1339 	     */
  1340 			
  1341 	    if (c == '-' && *(argv[0] + 2) == '\0') {
  1342 		argv += 1;
  1343 		argc--;
  1344 		break;
  1345 	    }
  1346 			
  1347 	    /*
  1348 	     * So we can check here a switch with no value.
  1349 	     */
  1350 			
  1351 	    if (argc == 1)  {
  1352 		Tcl_AppendResult(interp, "Error, no value given for switch ",
  1353 			argv[0], (char *) NULL);
  1354 		return TCL_ERROR;
  1355 	    }
  1356 			
  1357 	    if (c == 'r' && strcmp(argv[0] + 1, "rsrcname") == 0) {
  1358 		resName = argv[1];
  1359 	    } else if (c == 'r' && strcmp(argv[0] + 1, "rsrcid") == 0) {
  1360 		if (Tcl_GetInt(interp, argv[1], &resID) != TCL_OK) {
  1361 		    Tcl_AppendResult(interp,
  1362 			    "Error getting resource ID", (char *) NULL);
  1363 		    return TCL_ERROR;
  1364 		}
  1365 	    } else {
  1366 		Tcl_AppendResult(interp, "Error, invalid switch ", argv[0],
  1367 			" should be \"--\", \"-rsrcname\" or \"-rsrcid\"",
  1368 			(char *) NULL);
  1369 		return TCL_ERROR;
  1370 	    }
  1371 			
  1372 	    argv += 2;
  1373 	    argc -= 2;
  1374 	} else {
  1375 	    break;
  1376 	}
  1377     }
  1378     /*
  1379      * Ok, now we have the options, so we can load the resource,
  1380      */
  1381     if (argc == 0) {
  1382 	Tcl_AppendResult(interp, "Error, no filename given", (char *) NULL);
  1383 	return TCL_ERROR;
  1384     }
  1385 	
  1386     if (tclOSALoad(interp, OSAComponent, resName, resID,
  1387 	    argv[0], &resultID) != TCL_OK) {
  1388 	Tcl_AppendResult(interp, "Error in load command", (char *) NULL);
  1389 	return TCL_ERROR;
  1390     }
  1391 	 
  1392     /*
  1393      *  Now find out whether we have a script, or a script context.
  1394      */
  1395 	 
  1396     OSAGetScriptInfo(OSAComponent->theComponent, resultID,
  1397 	    kOSAScriptIsTypeScriptContext, &scptInfo);
  1398     
  1399     if (scptInfo) {
  1400 	autoName[0] = '\0';
  1401 	tclOSAAddContext(OSAComponent, autoName, resultID);
  1402 		
  1403 	Tcl_SetResult(interp, autoName, TCL_VOLATILE);
  1404     } else {
  1405 	/*
  1406 	 * For a script, we return the script name
  1407 	 */
  1408 	autoName[0] = '\0';
  1409 	tclOSAAddScript(OSAComponent, autoName, kOSAModeCanInteract, resultID);
  1410 	Tcl_SetResult(interp, autoName, TCL_VOLATILE);
  1411     }		 	
  1412     return TCL_OK;
  1413 }
  1414 
  1415 /*
  1416  *----------------------------------------------------------------------
  1417  *
  1418  * tclOSARunCmd --
  1419  *
  1420  *	This implements the run subcommand of the component command
  1421  *
  1422  * Results:
  1423  *	A standard Tcl result.
  1424  *
  1425  * Side effects:
  1426  *	Runs the given compiled script, and returns the OSA
  1427  *	component's result.
  1428  *
  1429  *----------------------------------------------------------------------
  1430  */
  1431  
  1432 static int 
  1433 tclOSARunCmd(
  1434     Tcl_Interp *interp,
  1435     tclOSAComponent *OSAComponent,
  1436     int argc,
  1437     CONST char **argv)
  1438 {
  1439     int tclError = TCL_OK,
  1440 	resID = 128;
  1441     char c, *contextName = NULL,
  1442 	*scriptName = NULL, 
  1443 	*resName = NULL;
  1444     AEDesc scrptDesc = { typeNull, NULL };
  1445     long modeFlags = kOSAModeCanInteract;
  1446     OSAID resultID = kOSANullScript,
  1447 	contextID = kOSANullScript,
  1448 	parentID = kOSANullScript;
  1449     OSAError osaErr = noErr;
  1450     OSErr sysErr = noErr;
  1451     CONST char *componentName = argv[0];
  1452     OSAID scriptID;
  1453 	
  1454     if (argc == 2) {
  1455 	Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
  1456 		argv[0], " ", argv[1], " scriptName", (char *) NULL);
  1457 	return TCL_ERROR;
  1458     }
  1459 	
  1460     /*
  1461      * Set the context to the global context for this component,
  1462      * as a default
  1463      */
  1464     if (tclOSAGetContextID(OSAComponent, "global", &contextID) != TCL_OK) {
  1465 	Tcl_AppendResult(interp,
  1466 		"Could not find the global context for component ",
  1467 		OSAComponent->theName, (char *) NULL );
  1468 	return TCL_ERROR;
  1469     }
  1470 
  1471     /*
  1472      * Now parse the argument list for switches
  1473      */
  1474     argv += 2;
  1475     argc -= 2;
  1476 	
  1477     while (argc > 0) {
  1478 	if (*argv[0] == '-') {
  1479 	    c = *(argv[0] + 1);
  1480 	    /*
  1481 	     * "--" is the only switch that has no value
  1482 	     */
  1483 	    if (c == '-' && *(argv[0] + 2) == '\0') {
  1484 		argv += 1;
  1485 		argc--;
  1486 		break;
  1487 	    }
  1488 			
  1489 	    /*
  1490 	     * So we can check here for a switch with no value.
  1491 	     */
  1492 	    if (argc == 1)  {
  1493 		Tcl_AppendResult(interp, "Error, no value given for switch ",
  1494 			argv[0], (char *) NULL);
  1495 		return TCL_ERROR;
  1496 	    }
  1497 			
  1498 	    if (c == 'c' && strcmp(argv[0] + 1, "context") == 0) {
  1499 		if (argc == 1) {
  1500 		    Tcl_AppendResult(interp,
  1501 			    "Error - no context provided for the -context switch",
  1502 			    (char *) NULL);
  1503 		    return TCL_ERROR;
  1504 		} else if (tclOSAGetContextID(OSAComponent,
  1505 			argv[1], &contextID) == TCL_OK) {
  1506 		} else {
  1507 		    Tcl_AppendResult(interp, "Script context \"", argv[1],
  1508 			    "\" not found", (char *) NULL);
  1509 		    return TCL_ERROR;
  1510 		} 
  1511 	    } else {
  1512 		Tcl_AppendResult(interp, "Error, invalid switch ", argv[0],
  1513 			" for ", componentName,
  1514 			" should be \"-context\"", (char *) NULL);
  1515 		return TCL_ERROR;
  1516 	    }
  1517 	    argv += 2;
  1518 	    argc -= 2;
  1519 	} else {
  1520 	    break;
  1521 	}
  1522     }
  1523 	
  1524     if (tclOSAGetScriptID(OSAComponent, argv[0], &scriptID) != TCL_OK) {
  1525 	if (tclOSAGetContextID(OSAComponent, argv[0], &scriptID) != TCL_OK) {
  1526 	    Tcl_AppendResult(interp, "Could not find script \"",
  1527 		    argv[2], "\"", (char *) NULL);
  1528 	    return TCL_ERROR;
  1529 	}
  1530     }
  1531 	
  1532     sysErr = OSAExecute(OSAComponent->theComponent,
  1533 	    scriptID, contextID, modeFlags, &resultID);
  1534 							
  1535     if (sysErr == errOSAScriptError) {
  1536 	tclOSAASError(interp, OSAComponent->theComponent, (char *) NULL);
  1537 	tclError = TCL_ERROR;
  1538     } else if (sysErr != noErr) {
  1539 	char buffer[32];
  1540 	sprintf(buffer, "Error #%6.6d encountered in run", sysErr);
  1541 	Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  1542 	tclError = TCL_ERROR;
  1543     } else {
  1544 	tclOSAResultFromID(interp, OSAComponent->theComponent, resultID );
  1545     }
  1546     OSADispose(OSAComponent->theComponent, resultID);
  1547 
  1548     return tclError;		
  1549 }
  1550 
  1551 /*
  1552  *----------------------------------------------------------------------
  1553  *
  1554  * tclOSAStoreCmd --
  1555  *
  1556  *	This implements the store subcommand of the component command
  1557  *
  1558  * Results:
  1559  *	A standard Tcl result.
  1560  *
  1561  * Side effects:
  1562  *	Runs the given compiled script, and returns the OSA
  1563  *	component's result.
  1564  *
  1565  *----------------------------------------------------------------------
  1566  */
  1567  
  1568 static int 
  1569 tclOSAStoreCmd(
  1570     Tcl_Interp *interp,
  1571     tclOSAComponent *OSAComponent,
  1572     int argc,
  1573     CONST char **argv)
  1574 {
  1575     int tclError = TCL_OK, resID = 128;
  1576     char c, *contextName = NULL, *scriptName = NULL;
  1577     CONST char *resName = NULL;
  1578     Boolean makeNewContext = false, makeContext = false;
  1579     AEDesc scrptDesc = { typeNull, NULL };
  1580     long modeFlags = kOSAModeCanInteract;
  1581     OSAID resultID = kOSANullScript,
  1582 	contextID = kOSANullScript,
  1583 	parentID = kOSANullScript;
  1584     OSAError osaErr = noErr;
  1585     OSErr  sysErr = noErr;
  1586 		
  1587     if (argc == 2) {
  1588 	Tcl_AppendResult(interp, "Error, no data for \"", argv[0],
  1589 		" ",argv[1], "\"", (char *) NULL);
  1590 	return TCL_ERROR;
  1591     } 
  1592 
  1593     argv += 2;
  1594     argc -= 2;
  1595 
  1596     /*
  1597      * Do the argument parsing
  1598      */
  1599 	
  1600     while (argc > 0) {
  1601 	if (*argv[0] == '-') {
  1602 	    c = *(argv[0] + 1);
  1603 			
  1604 	    /*
  1605 	     * "--" is the only switch that has no value
  1606 	     */
  1607 	    if (c == '-' && *(argv[0] + 2) == '\0') {
  1608 		argv += 1;
  1609 		argc--;
  1610 		break;
  1611 	    }
  1612 			
  1613 	    /*
  1614 	     * So we can check here a switch with no value.
  1615 	     */
  1616 	    if (argc == 1)  {
  1617 		Tcl_AppendResult(interp,
  1618 			"Error, no value given for switch ",
  1619 			argv[0], (char *) NULL);
  1620 		return TCL_ERROR;
  1621 	    }
  1622 			
  1623 	    if (c == 'r' && strcmp(argv[0] + 1, "rsrcname") == 0) {
  1624 		resName = argv[1];
  1625 	    } else if (c == 'r' && strcmp(argv[0] + 1, "rsrcid") == 0) {
  1626 		if (Tcl_GetInt(interp, argv[1], &resID) != TCL_OK) {
  1627 		    Tcl_AppendResult(interp,
  1628 			    "Error getting resource ID", (char *) NULL);
  1629 		    return TCL_ERROR;
  1630 		}
  1631 	    } else {
  1632 		Tcl_AppendResult(interp, "Error, invalid switch ", argv[0],
  1633 			" should be \"--\", \"-rsrcname\" or \"-rsrcid\"",
  1634 			(char *) NULL);
  1635 		return TCL_ERROR;
  1636 	    }
  1637 			
  1638 	    argv += 2;
  1639 	    argc -= 2;
  1640 	} else {
  1641 	    break;
  1642 	}
  1643     }
  1644     /*
  1645      * Ok, now we have the options, so we can load the resource,
  1646      */
  1647     if (argc != 2) {
  1648 	Tcl_AppendResult(interp, "Error, wrong # of arguments, should be ",
  1649 		argv[0], " ", argv[1], "?option flag? scriptName fileName",
  1650 		(char *) NULL);
  1651 	return TCL_ERROR;
  1652     }
  1653 	
  1654     if (tclOSAStore(interp, OSAComponent, resName, resID,
  1655 	    argv[0], argv[1]) != TCL_OK) {
  1656 	Tcl_AppendResult(interp, "Error in load command", (char *) NULL);
  1657 	return TCL_ERROR;
  1658     } else {
  1659 	Tcl_ResetResult(interp);
  1660 	tclError = TCL_OK;
  1661     }
  1662     
  1663     return tclError;
  1664 }
  1665 
  1666 /*
  1667  *----------------------------------------------------------------------
  1668  *
  1669  * tclOSAMakeNewComponent --
  1670  *
  1671  *	Makes a command cmdName to represent a new connection to the
  1672  *	OSA component with componentSubType scriptSubtype.
  1673  *
  1674  * Results: 
  1675  *	Returns the tclOSAComponent structure for the connection.
  1676  *
  1677  * Side Effects: 
  1678  *	Adds a new element to the component table.  If there is an
  1679  *	error, then the result of the Tcl interpreter interp is set
  1680  *	to an appropriate error message.
  1681  *
  1682  *----------------------------------------------------------------------
  1683  */
  1684  
  1685 tclOSAComponent *
  1686 tclOSAMakeNewComponent(
  1687     Tcl_Interp *interp,
  1688     char *cmdName,
  1689     char *languageName, 
  1690     OSType scriptSubtype,
  1691     long componentFlags) 
  1692 {
  1693     char buffer[32];
  1694     AEDesc resultingName = {typeNull, NULL};
  1695     AEDesc nullDesc = {typeNull, NULL };
  1696     OSAID globalContext;
  1697     char global[] = "global";
  1698     int nbytes;
  1699     ComponentDescription requestedComponent = {
  1700 	kOSAComponentType,
  1701 	(OSType) 0,
  1702 	(OSType) 0,
  1703 	(long int) 0,
  1704 	(long int) 0
  1705     };
  1706     Tcl_HashTable *ComponentTable;
  1707     Component foundComponent = NULL;
  1708     OSAActiveUPP myActiveProcUPP;
  1709 			
  1710     tclOSAComponent *newComponent;
  1711     Tcl_HashEntry *hashEntry;
  1712     int newPtr;
  1713 	
  1714     requestedComponent.componentSubType = scriptSubtype;
  1715     nbytes = sizeof(tclOSAComponent);
  1716     newComponent = (tclOSAComponent *) ckalloc(sizeof(tclOSAComponent));
  1717     if (newComponent == NULL) {
  1718 	goto CleanUp;
  1719     }
  1720 	
  1721     foundComponent = FindNextComponent(0, &requestedComponent);
  1722     if (foundComponent == 0) {
  1723 	Tcl_AppendResult(interp,
  1724 		"Could not find component of requested type", (char *) NULL);
  1725 	goto CleanUp;
  1726     } 
  1727 	
  1728     newComponent->theComponent = OpenComponent(foundComponent); 
  1729 	
  1730     if (newComponent->theComponent == NULL) {
  1731 	Tcl_AppendResult(interp,
  1732 		"Could not open component of the requested type",
  1733 		(char *) NULL);
  1734 	goto CleanUp;
  1735     }
  1736 							
  1737     newComponent->languageName = (char *) ckalloc(strlen(languageName) + 1);
  1738     strcpy(newComponent->languageName,languageName);
  1739 	
  1740     newComponent->componentFlags = componentFlags;
  1741 	
  1742     newComponent->theInterp = interp;
  1743 	
  1744     Tcl_InitHashTable(&newComponent->contextTable, TCL_STRING_KEYS);
  1745     Tcl_InitHashTable(&newComponent->scriptTable, TCL_STRING_KEYS);
  1746 		
  1747     if (tclOSAMakeContext(newComponent, global, &globalContext) != TCL_OK) {
  1748 	sprintf(buffer, "%-6.6ld", globalContext);
  1749 	Tcl_AppendResult(interp, "Error ", buffer, " making ", global,
  1750 		" context.", (char *) NULL);
  1751 	goto CleanUp;
  1752     }
  1753     
  1754     newComponent->languageID = scriptSubtype;
  1755 	
  1756     newComponent->theName = (char *) ckalloc(strlen(cmdName) + 1 );
  1757     strcpy(newComponent->theName, cmdName);
  1758 
  1759     Tcl_CreateCommand(interp, newComponent->theName, Tcl_OSAComponentCmd,
  1760 	    (ClientData) newComponent, tclOSAClose);
  1761 					
  1762     /*
  1763      * Register the new component with the component table
  1764      */ 
  1765 
  1766     ComponentTable = (Tcl_HashTable *) Tcl_GetAssocData(interp,
  1767 	    "OSAScript_CompTable", (Tcl_InterpDeleteProc **) NULL);
  1768 	
  1769     if (ComponentTable == NULL) {
  1770 	Tcl_AppendResult(interp, "Error, could not get the Component Table",
  1771 		" from the Associated data.", (char *) NULL);
  1772 	return (tclOSAComponent *) NULL;
  1773     }
  1774 	
  1775     hashEntry = Tcl_CreateHashEntry(ComponentTable,
  1776 	    newComponent->theName, &newPtr);	
  1777     Tcl_SetHashValue(hashEntry, (ClientData) newComponent);
  1778 
  1779     /*
  1780      * Set the active proc to call Tcl_DoOneEvent() while idle
  1781      */
  1782     if (OSAGetActiveProc(newComponent->theComponent,
  1783 	    &newComponent->defActiveProc, &newComponent->defRefCon) != noErr ) {
  1784     	/* TODO -- clean up here... */
  1785     }
  1786 
  1787     myActiveProcUPP = NewOSAActiveUPP(TclOSAActiveProc);
  1788     OSASetActiveProc(newComponent->theComponent,
  1789 	    myActiveProcUPP, (long) newComponent);
  1790     return newComponent;
  1791 	
  1792     CleanUp:
  1793 	
  1794     ckfree((char *) newComponent);
  1795     return (tclOSAComponent *) NULL;
  1796 }
  1797 
  1798 /*
  1799  *----------------------------------------------------------------------
  1800  *
  1801  * tclOSAClose --
  1802  *
  1803  *	This procedure closes the connection to an OSA component, and 
  1804  *	deletes all the script and context data associated with it.
  1805  *	It is the command deletion callback for the component's command.
  1806  *
  1807  * Results:
  1808  *	None
  1809  *
  1810  * Side effects:
  1811  *	Closes the connection, and releases all the script data.
  1812  *
  1813  *----------------------------------------------------------------------
  1814  */
  1815 
  1816 void 
  1817 tclOSAClose(
  1818     ClientData clientData) 
  1819 {
  1820     tclOSAComponent *theComponent = (tclOSAComponent *) clientData;
  1821     Tcl_HashEntry *hashEntry;
  1822     Tcl_HashSearch search;
  1823     tclOSAScript *theScript;
  1824     Tcl_HashTable *ComponentTable;
  1825 	
  1826     /* 
  1827      * Delete the context and script tables 
  1828      * the memory for the language name, and
  1829      * the hash entry.
  1830      */
  1831 	
  1832     for (hashEntry = Tcl_FirstHashEntry(&theComponent->scriptTable, &search);
  1833 	 hashEntry != NULL;
  1834 	 hashEntry = Tcl_NextHashEntry(&search)) {
  1835 
  1836 	theScript = (tclOSAScript *) Tcl_GetHashValue(hashEntry);
  1837 	OSADispose(theComponent->theComponent, theScript->scriptID);	
  1838 	ckfree((char *) theScript);
  1839 	Tcl_DeleteHashEntry(hashEntry);
  1840     }
  1841 	
  1842     for (hashEntry = Tcl_FirstHashEntry(&theComponent->contextTable, &search);
  1843 	 hashEntry != NULL;
  1844 	 hashEntry = Tcl_NextHashEntry(&search)) {
  1845 
  1846 	Tcl_DeleteHashEntry(hashEntry);
  1847     }
  1848 	
  1849     ckfree(theComponent->languageName);
  1850     ckfree(theComponent->theName);
  1851 	
  1852     /*
  1853      * Finally close the component
  1854      */
  1855 	
  1856     CloseComponent(theComponent->theComponent);
  1857 	
  1858     ComponentTable = (Tcl_HashTable *)
  1859 	Tcl_GetAssocData(theComponent->theInterp,
  1860 		"OSAScript_CompTable", (Tcl_InterpDeleteProc **) NULL);
  1861 	
  1862     if (ComponentTable == NULL) {
  1863 	panic("Error, could not get the Component Table from the Associated data.");
  1864     }
  1865 	
  1866     hashEntry = Tcl_FindHashEntry(ComponentTable, theComponent->theName);
  1867     if (hashEntry != NULL) {
  1868 	Tcl_DeleteHashEntry(hashEntry);
  1869     }
  1870     
  1871     ckfree((char *) theComponent);
  1872 }
  1873 
  1874 /*
  1875  *----------------------------------------------------------------------
  1876  *
  1877  * tclOSAGetContextID  --
  1878  *
  1879  *	This returns the context ID, given the component name.
  1880  *
  1881  * Results:
  1882  *	A context ID
  1883  *
  1884  * Side effects:
  1885  *	None
  1886  *
  1887  *----------------------------------------------------------------------
  1888  */
  1889 
  1890 static int 
  1891 tclOSAGetContextID(
  1892     tclOSAComponent *theComponent, 
  1893     CONST char *contextName, 
  1894     OSAID *theContext)
  1895 {
  1896     Tcl_HashEntry *hashEntry;
  1897     tclOSAContext *contextStruct;
  1898 	
  1899     if ((hashEntry = Tcl_FindHashEntry(&theComponent->contextTable,
  1900 	    contextName)) == NULL ) {			
  1901 	return TCL_ERROR;
  1902     } else {
  1903 	contextStruct = (tclOSAContext *) Tcl_GetHashValue(hashEntry);
  1904 	*theContext = contextStruct->contextID;
  1905     }
  1906     return TCL_OK;
  1907 }
  1908 
  1909 /*
  1910  *----------------------------------------------------------------------
  1911  *
  1912  * tclOSAAddContext  --
  1913  *
  1914  *	This adds the context ID, with the name contextName.  If the
  1915  *	name is passed in as a NULL string, space is malloc'ed for the
  1916  *	string and a new name is made up, if the string is empty, you
  1917  *	must have allocated enough space ( 24 characters is fine) for
  1918  *	the name, which is made up and passed out.
  1919  *
  1920  * Results:
  1921  *	Nothing
  1922  *
  1923  * Side effects:
  1924  *	Adds the script context to the component's context table.
  1925  *
  1926  *----------------------------------------------------------------------
  1927  */
  1928 
  1929 static void 
  1930 tclOSAAddContext(
  1931     tclOSAComponent *theComponent, 
  1932     char *contextName,
  1933     const OSAID theContext)
  1934 {
  1935     static unsigned short contextIndex = 0;
  1936     tclOSAContext *contextStruct;
  1937     Tcl_HashEntry *hashEntry;
  1938     int newPtr;
  1939 
  1940     if (contextName == NULL) {
  1941 	contextName = ckalloc(16 + TCL_INTEGER_SPACE);
  1942 	sprintf(contextName, "OSAContext%d", contextIndex++);
  1943     } else if (*contextName == '\0') {
  1944 	sprintf(contextName, "OSAContext%d", contextIndex++);
  1945     }
  1946 	
  1947     hashEntry = Tcl_CreateHashEntry(&theComponent->contextTable,
  1948 	    contextName, &newPtr);	
  1949 
  1950     contextStruct = (tclOSAContext *) ckalloc(sizeof(tclOSAContext));
  1951     contextStruct->contextID = theContext;
  1952     Tcl_SetHashValue(hashEntry,(ClientData) contextStruct);
  1953 }
  1954 
  1955 /*
  1956  *----------------------------------------------------------------------
  1957  *
  1958  * tclOSADeleteContext  --
  1959  *
  1960  *	This deletes the context struct, with the name contextName.  
  1961  *
  1962  * Results:
  1963  *	A normal Tcl result
  1964  *
  1965  * Side effects:
  1966  *	Removes the script context to the component's context table,
  1967  *	and deletes the data associated with it.
  1968  *
  1969  *----------------------------------------------------------------------
  1970  */
  1971 
  1972 static int 
  1973 tclOSADeleteContext(
  1974     tclOSAComponent *theComponent,
  1975     CONST char *contextName) 
  1976 {
  1977     Tcl_HashEntry *hashEntry;
  1978     tclOSAContext *contextStruct;
  1979 	
  1980     hashEntry = Tcl_FindHashEntry(&theComponent->contextTable, contextName);
  1981     if (hashEntry == NULL) {
  1982 	return TCL_ERROR;
  1983     }	
  1984     /*
  1985      * Dispose of the script context data
  1986      */
  1987     contextStruct = (tclOSAContext *) Tcl_GetHashValue(hashEntry);
  1988     OSADispose(theComponent->theComponent,contextStruct->contextID);
  1989     /*
  1990      * Then the hash entry
  1991      */
  1992     ckfree((char *) contextStruct);
  1993     Tcl_DeleteHashEntry(hashEntry);
  1994     return TCL_OK;
  1995 }
  1996 
  1997 /*
  1998  *----------------------------------------------------------------------
  1999  *
  2000  * tclOSAMakeContext  --
  2001  *
  2002  *	This makes the context with name contextName, and returns the ID.
  2003  *
  2004  * Results:
  2005  *	A standard Tcl result
  2006  *
  2007  * Side effects:
  2008  *	Makes a new context, adds it to the context table, and returns 
  2009  *	the new contextID in the variable theContext.
  2010  *
  2011  *----------------------------------------------------------------------
  2012  */
  2013 
  2014 static int 
  2015 tclOSAMakeContext(
  2016     tclOSAComponent *theComponent, 
  2017     CONST char *contextName,
  2018     OSAID *theContext)
  2019 {
  2020     AEDesc contextNameDesc = {typeNull, NULL};
  2021     OSAError osaErr = noErr;
  2022 
  2023     AECreateDesc(typeChar, contextName, strlen(contextName), &contextNameDesc);
  2024     osaErr = OSAMakeContext(theComponent->theComponent, &contextNameDesc,
  2025 	    kOSANullScript, theContext);
  2026 								
  2027     AEDisposeDesc(&contextNameDesc);
  2028 	
  2029     if (osaErr == noErr) {
  2030 	char name[24];
  2031 	strncpy(name, contextName, 23);
  2032 	name[23] = '\0';
  2033 	tclOSAAddContext(theComponent, name, *theContext);
  2034     } else {
  2035 	*theContext = (OSAID) osaErr;
  2036 	return TCL_ERROR;
  2037     }
  2038 	
  2039     return TCL_OK;
  2040 }
  2041 
  2042 /*
  2043  *----------------------------------------------------------------------
  2044  *
  2045  * tclOSAStore --
  2046  *
  2047  *	This stores a script resource from the file named in fileName.
  2048  *
  2049  *	Most of this routine is caged from the Tcl Source, from the
  2050  *	Tcl_MacSourceCmd routine.  This is good, since it ensures this
  2051  *	follows the same convention for looking up files as Tcl.
  2052  *
  2053  * Returns
  2054  *	A standard Tcl result.
  2055  *
  2056  * Side Effects:
  2057  *	The given script data is stored in the file fileName.
  2058  *
  2059  *----------------------------------------------------------------------
  2060  */
  2061  
  2062 int
  2063 tclOSAStore(
  2064     Tcl_Interp *interp,
  2065     tclOSAComponent *theComponent,
  2066     CONST char *resourceName,
  2067     int resourceNumber, 
  2068     CONST char *scriptName,
  2069     CONST char *fileName)
  2070 {
  2071     Handle resHandle;
  2072     Str255 rezName;
  2073     int result = TCL_OK;
  2074     short saveRef, fileRef = -1;
  2075     char idStr[16 + TCL_INTEGER_SPACE];
  2076     FSSpec fileSpec;
  2077     Tcl_DString ds, buffer;
  2078     CONST char *nativeName;
  2079     OSErr myErr = noErr;
  2080     OSAID scriptID;
  2081     Size scriptSize;
  2082     AEDesc scriptData;
  2083 
  2084     /*
  2085      * First extract the script data
  2086      */
  2087 	
  2088     if (tclOSAGetScriptID(theComponent, scriptName, &scriptID) != TCL_OK ) {
  2089 	if (tclOSAGetContextID(theComponent, scriptName, &scriptID)
  2090 		!= TCL_OK) {
  2091 	    Tcl_AppendResult(interp, "Error getting script ",
  2092 		    scriptName, (char *) NULL);
  2093 	    return TCL_ERROR;
  2094 	}
  2095     }
  2096 	
  2097     myErr = OSAStore(theComponent->theComponent, scriptID,
  2098 	    typeOSAGenericStorage, kOSAModeNull, &scriptData);
  2099     if (myErr != noErr) {
  2100 	sprintf(idStr, "%d", myErr);
  2101 	Tcl_AppendResult(interp, "Error #", idStr,
  2102 		" storing script ", scriptName, (char *) NULL);
  2103 	return TCL_ERROR;
  2104     }
  2105 
  2106     /*
  2107      * Now try to open the output file
  2108      */
  2109 	
  2110     saveRef = CurResFile();
  2111 	
  2112     if (fileName != NULL) {
  2113 	OSErr err;
  2114 		
  2115 	if (Tcl_TranslateFileName(interp, fileName, &buffer) == NULL) {
  2116 	    return TCL_ERROR;
  2117 	}
  2118 	nativeName = Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&buffer), 
  2119     	    Tcl_DStringLength(&buffer), &ds);
  2120 	err = FSpLocationFromPath(strlen(nativeName), nativeName, &fileSpec);
  2121 		
  2122 	Tcl_DStringFree(&ds);
  2123 	Tcl_DStringFree(&buffer);
  2124 	if ((err != noErr) && (err != fnfErr)) {
  2125 	    Tcl_AppendResult(interp,
  2126 		    "Error getting a location for the file: \"", 
  2127 		    fileName, "\".", NULL);
  2128 	    return TCL_ERROR;
  2129 	}
  2130 		
  2131 	FSpCreateResFileCompatTcl(&fileSpec,
  2132 		'WiSH', 'osas', smSystemScript);	
  2133 	myErr = ResError();
  2134 	
  2135 	if ((myErr != noErr) && (myErr != dupFNErr)) {
  2136 	    sprintf(idStr, "%d", myErr);
  2137 	    Tcl_AppendResult(interp, "Error #", idStr,
  2138 		    " creating new resource file ", fileName, (char *) NULL);
  2139 	    result = TCL_ERROR;
  2140 	    goto rezEvalCleanUp;
  2141 	}
  2142 		
  2143 	fileRef = FSpOpenResFileCompatTcl(&fileSpec, fsRdWrPerm);
  2144 	if (fileRef == -1) {
  2145 	    Tcl_AppendResult(interp, "Error reading the file: \"", 
  2146 		    fileName, "\".", NULL);
  2147 	    result = TCL_ERROR;
  2148 	    goto rezEvalCleanUp;
  2149 	}
  2150 	UseResFile(fileRef);
  2151     } else {
  2152 	/*
  2153 	 * The default behavior will search through all open resource files.
  2154 	 * This may not be the behavior you desire.  If you want the behavior
  2155 	 * of this call to *only* search the application resource fork, you
  2156 	 * must call UseResFile at this point to set it to the application
  2157 	 * file.  This means you must have already obtained the application's 
  2158 	 * fileRef when the application started up.
  2159 	 */
  2160     }
  2161 	
  2162     /*
  2163      * Load the resource by name 
  2164      */
  2165     if (resourceName != NULL) {
  2166 	strcpy((char *) rezName + 1, resourceName);
  2167 	rezName[0] = strlen(resourceName);
  2168 	resHandle = Get1NamedResource('scpt', rezName);
  2169 	myErr = ResError();
  2170 	if (resHandle == NULL) {
  2171 	    /*
  2172 	     * These signify either the resource or the resource
  2173 	     * type were not found
  2174 	     */
  2175 	    if (myErr == resNotFound || myErr == noErr) {
  2176 		short uniqueID;
  2177 		while ((uniqueID = Unique1ID('scpt') ) < 128) {}
  2178 		AddResource(scriptData.dataHandle, 'scpt', uniqueID, rezName);
  2179 		WriteResource(resHandle);
  2180 		result = TCL_OK;
  2181 		goto rezEvalCleanUp;
  2182 	    } else {
  2183 		/*
  2184 		 * This means there was some other error, for now
  2185 		 * I just bag out.
  2186 		 */
  2187 		sprintf(idStr, "%d", myErr);
  2188 		Tcl_AppendResult(interp, "Error #", idStr,
  2189 			" opening scpt resource named ", resourceName,
  2190 			" in file ", fileName, (char *) NULL);
  2191 		result = TCL_ERROR;
  2192 		goto rezEvalCleanUp;
  2193 	    }
  2194 	}
  2195 	/*
  2196 	 * Or ID
  2197 	 */ 
  2198     } else {
  2199 	resHandle = Get1Resource('scpt', resourceNumber);
  2200 	rezName[0] = 0;
  2201 	rezName[1] = '\0';
  2202 	myErr = ResError();
  2203 	if (resHandle == NULL) {
  2204 	    /*
  2205 	     * These signify either the resource or the resource
  2206 	     * type were not found
  2207 	     */
  2208 	    if (myErr == resNotFound || myErr == noErr) {
  2209 		AddResource(scriptData.dataHandle, 'scpt',
  2210 			resourceNumber, rezName);
  2211 		WriteResource(resHandle);
  2212 		result = TCL_OK;
  2213 		goto rezEvalCleanUp;
  2214 	    } else {
  2215 		/*
  2216 		 * This means there was some other error, for now
  2217 		 * I just bag out */
  2218 		sprintf(idStr, "%d", myErr);
  2219 		Tcl_AppendResult(interp, "Error #", idStr,
  2220 			" opening scpt resource named ", resourceName,
  2221 			" in file ", fileName,(char *) NULL);
  2222 		result = TCL_ERROR;
  2223 		goto rezEvalCleanUp;
  2224 	    }
  2225 	} 
  2226     }
  2227 	
  2228     /* 
  2229      * We get to here if the resource exists 
  2230      * we just copy into it... 
  2231      */
  2232 	 
  2233     scriptSize = GetHandleSize(scriptData.dataHandle);
  2234     SetHandleSize(resHandle, scriptSize);
  2235     HLock(scriptData.dataHandle);
  2236     HLock(resHandle);
  2237     BlockMove(*scriptData.dataHandle, *resHandle,scriptSize);
  2238     HUnlock(scriptData.dataHandle);
  2239     HUnlock(resHandle);
  2240     ChangedResource(resHandle);
  2241     WriteResource(resHandle);
  2242     result = TCL_OK;
  2243     goto rezEvalCleanUp;
  2244 			
  2245     rezEvalError:
  2246     sprintf(idStr, "ID=%d", resourceNumber);
  2247     Tcl_AppendResult(interp, "The resource \"",
  2248 	    (resourceName != NULL ? resourceName : idStr),
  2249 	    "\" could not be loaded from ",
  2250 	    (fileName != NULL ? fileName : "application"),
  2251 	    ".", NULL);
  2252 
  2253     rezEvalCleanUp:
  2254     if (fileRef != -1) {
  2255 	CloseResFile(fileRef);
  2256     }
  2257 
  2258     UseResFile(saveRef);
  2259 	
  2260     return result;
  2261 }
  2262 
  2263 /*----------------------------------------------------------------------
  2264  *
  2265  * tclOSALoad --
  2266  *
  2267  *	This loads a script resource from the file named in fileName.
  2268  *	Most of this routine is caged from the Tcl Source, from the
  2269  *	Tcl_MacSourceCmd routine.  This is good, since it ensures this
  2270  *	follows the same convention for looking up files as Tcl.
  2271  *
  2272  * Returns
  2273  *	A standard Tcl result.
  2274  *
  2275  * Side Effects:
  2276  *	A new script element is created from the data in the file.
  2277  *	The script ID is passed out in the variable resultID.
  2278  *
  2279  *----------------------------------------------------------------------
  2280  */
  2281  
  2282 int
  2283 tclOSALoad(
  2284     Tcl_Interp *interp,
  2285     tclOSAComponent *theComponent,
  2286     CONST char *resourceName,
  2287     int resourceNumber, 
  2288     CONST char *fileName,
  2289     OSAID *resultID)
  2290 {
  2291     Handle sourceData;
  2292     Str255 rezName;
  2293     int result = TCL_OK;
  2294     short saveRef, fileRef = -1;
  2295     char idStr[16 + TCL_INTEGER_SPACE];
  2296     FSSpec fileSpec;
  2297     Tcl_DString ds, buffer;
  2298     CONST char *nativeName;
  2299 
  2300     saveRef = CurResFile();
  2301 	
  2302     if (fileName != NULL) {
  2303 	OSErr err;
  2304 		
  2305 	if (Tcl_TranslateFileName(interp, fileName, &buffer) == NULL) {
  2306 	    return TCL_ERROR;
  2307 	}
  2308 	nativeName = Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&buffer), 
  2309     	    Tcl_DStringLength(&buffer), &ds);
  2310 	err = FSpLocationFromPath(strlen(nativeName), nativeName, &fileSpec);
  2311 	Tcl_DStringFree(&ds);
  2312 	Tcl_DStringFree(&buffer);
  2313 	if (err != noErr) {
  2314 	    Tcl_AppendResult(interp, "Error finding the file: \"", 
  2315 		    fileName, "\".", NULL);
  2316 	    return TCL_ERROR;
  2317 	}
  2318 			
  2319 	fileRef = FSpOpenResFileCompatTcl(&fileSpec, fsRdPerm);
  2320 	if (fileRef == -1) {
  2321 	    Tcl_AppendResult(interp, "Error reading the file: \"", 
  2322 		    fileName, "\".", NULL);
  2323 	    return TCL_ERROR;
  2324 	}
  2325 	UseResFile(fileRef);
  2326     } else {
  2327 	/*
  2328 	 * The default behavior will search through all open resource files.
  2329 	 * This may not be the behavior you desire.  If you want the behavior
  2330 	 * of this call to *only* search the application resource fork, you
  2331 	 * must call UseResFile at this point to set it to the application
  2332 	 * file.  This means you must have already obtained the application's 
  2333 	 * fileRef when the application started up.
  2334 	 */
  2335     }
  2336 	
  2337     /*
  2338      * Load the resource by name or ID
  2339      */
  2340     if (resourceName != NULL) {
  2341 	strcpy((char *) rezName + 1, resourceName);
  2342 	rezName[0] = strlen(resourceName);
  2343 	sourceData = GetNamedResource('scpt', rezName);
  2344     } else {
  2345 	sourceData = GetResource('scpt', (short) resourceNumber);
  2346     }
  2347 	
  2348     if (sourceData == NULL) {
  2349 	result = TCL_ERROR;
  2350     } else {
  2351 	AEDesc scriptDesc;
  2352 	OSAError osaErr;
  2353 		
  2354 	scriptDesc.descriptorType = typeOSAGenericStorage;
  2355 	scriptDesc.dataHandle = sourceData;
  2356 		
  2357 	osaErr = OSALoad(theComponent->theComponent, &scriptDesc,
  2358 		kOSAModeNull, resultID);
  2359 		
  2360 	ReleaseResource(sourceData);
  2361 		
  2362 	if (osaErr != noErr) {
  2363 	    result = TCL_ERROR;
  2364 	    goto rezEvalError;
  2365 	}
  2366 			
  2367 	goto rezEvalCleanUp;
  2368     }
  2369 	
  2370     rezEvalError:
  2371     sprintf(idStr, "ID=%d", resourceNumber);
  2372     Tcl_AppendResult(interp, "The resource \"",
  2373 	    (resourceName != NULL ? resourceName : idStr),
  2374 	    "\" could not be loaded from ",
  2375 	    (fileName != NULL ? fileName : "application"),
  2376 	    ".", NULL);
  2377 
  2378     rezEvalCleanUp:
  2379     if (fileRef != -1) {
  2380 	CloseResFile(fileRef);
  2381     }
  2382 
  2383     UseResFile(saveRef);
  2384 	
  2385     return result;
  2386 }
  2387 
  2388 /*
  2389  *----------------------------------------------------------------------
  2390  *
  2391  * tclOSAGetScriptID  --
  2392  *
  2393  *	This returns the context ID, gibven the component name.
  2394  *
  2395  * Results:
  2396  *	A standard Tcl result
  2397  *
  2398  * Side effects:
  2399  *	Passes out the script ID in the variable scriptID.
  2400  *
  2401  *----------------------------------------------------------------------
  2402  */
  2403 
  2404 static int 
  2405 tclOSAGetScriptID(
  2406     tclOSAComponent *theComponent,
  2407     CONST char *scriptName,
  2408     OSAID *scriptID) 
  2409 {
  2410     tclOSAScript *theScript;
  2411 	
  2412     theScript = tclOSAGetScript(theComponent, scriptName);
  2413     if (theScript == NULL) {
  2414 	return TCL_ERROR;
  2415     }
  2416 	
  2417     *scriptID = theScript->scriptID;
  2418     return TCL_OK;
  2419 }
  2420 
  2421 /*
  2422  *----------------------------------------------------------------------
  2423  *
  2424  * tclOSAAddScript  --
  2425  *
  2426  *	This adds a script to theComponent's script table, with the
  2427  *	given name & ID.
  2428  *
  2429  * Results:
  2430  *	A standard Tcl result
  2431  *
  2432  * Side effects:
  2433  *	Adds an element to the component's script table.
  2434  *
  2435  *----------------------------------------------------------------------
  2436  */
  2437 
  2438 static int 
  2439 tclOSAAddScript(
  2440     tclOSAComponent *theComponent,
  2441     char *scriptName,
  2442     long modeFlags,
  2443     OSAID scriptID) 
  2444 {
  2445     Tcl_HashEntry *hashEntry;
  2446     int newPtr;
  2447     static int scriptIndex = 0;
  2448     tclOSAScript *theScript;
  2449 	
  2450     if (*scriptName == '\0') {
  2451 	sprintf(scriptName, "OSAScript%d", scriptIndex++);
  2452     }
  2453 	
  2454     hashEntry = Tcl_CreateHashEntry(&theComponent->scriptTable,
  2455 	    scriptName, &newPtr);
  2456     if (newPtr == 0) {
  2457 	theScript = (tclOSAScript *) Tcl_GetHashValue(hashEntry);
  2458 	OSADispose(theComponent->theComponent, theScript->scriptID);
  2459     } else {		
  2460 	theScript = (tclOSAScript *) ckalloc(sizeof(tclOSAScript));
  2461 	if (theScript == NULL) {
  2462 	    return TCL_ERROR;
  2463 	}
  2464     }
  2465 		
  2466     theScript->scriptID = scriptID;
  2467     theScript->languageID = theComponent->languageID;
  2468     theScript->modeFlags = modeFlags;
  2469 	
  2470     Tcl_SetHashValue(hashEntry,(ClientData) theScript);
  2471 
  2472     return TCL_OK;
  2473 }
  2474 
  2475 /*
  2476  *----------------------------------------------------------------------
  2477  *
  2478  * tclOSAGetScriptID  --
  2479  *
  2480  *	This returns the script structure, given the component and script name.
  2481  *
  2482  * Results:
  2483  *	A pointer to the script structure.
  2484  *
  2485  * Side effects:
  2486  *	None
  2487  *
  2488  *----------------------------------------------------------------------
  2489  */
  2490  
  2491 static tclOSAScript *
  2492 tclOSAGetScript(
  2493     tclOSAComponent *theComponent,
  2494     CONST char *scriptName)
  2495 {
  2496     Tcl_HashEntry *hashEntry;
  2497 	
  2498     hashEntry = Tcl_FindHashEntry(&theComponent->scriptTable, scriptName);
  2499     if (hashEntry == NULL) {
  2500 	return NULL;
  2501     }
  2502 	
  2503     return (tclOSAScript *) Tcl_GetHashValue(hashEntry);
  2504 }
  2505 
  2506 /*
  2507  *----------------------------------------------------------------------
  2508  *
  2509  * tclOSADeleteScript  --
  2510  *
  2511  *	This deletes the script given by scriptName.
  2512  *
  2513  * Results:
  2514  *	A standard Tcl result
  2515  *
  2516  * Side effects:
  2517  *	Deletes the script from the script table, and frees up the
  2518  *	resources associated with it.  If there is an error, then
  2519  *	space for the error message is malloc'ed, and passed out in
  2520  *	the variable errMsg.
  2521  *
  2522  *----------------------------------------------------------------------
  2523  */
  2524 
  2525 static int
  2526 tclOSADeleteScript(
  2527     tclOSAComponent *theComponent,
  2528     CONST char *scriptName,
  2529     char *errMsg) 
  2530 {
  2531     Tcl_HashEntry *hashEntry;
  2532     tclOSAScript *scriptPtr;
  2533 
  2534     hashEntry = Tcl_FindHashEntry(&theComponent->scriptTable, scriptName);
  2535     if (hashEntry == NULL) {
  2536 	errMsg = ckalloc(17);
  2537 	strcpy(errMsg,"Script not found");
  2538 	return TCL_ERROR;
  2539     }
  2540 	
  2541     scriptPtr = (tclOSAScript *) Tcl_GetHashValue(hashEntry);
  2542     OSADispose(theComponent->theComponent, scriptPtr->scriptID);
  2543     ckfree((char *) scriptPtr);
  2544     Tcl_DeleteHashEntry(hashEntry);
  2545     return TCL_OK;
  2546 }
  2547 
  2548 /*
  2549  *----------------------------------------------------------------------
  2550  *
  2551  * TclOSAActiveProc --
  2552  *
  2553  *	This is passed to each component.  It is run periodically
  2554  *	during script compilation and script execution.  It in turn
  2555  *	calls Tcl_DoOneEvent to process the event queue.  We also call
  2556  *	the default Active proc which will let the user cancel the script
  2557  *	by hitting Command-.
  2558  * 
  2559  * Results:
  2560  *	A standard MacOS system error
  2561  *
  2562  * Side effects:
  2563  *	Any Tcl code may run while calling Tcl_DoOneEvent.
  2564  *
  2565  *----------------------------------------------------------------------
  2566  */
  2567  
  2568 static pascal OSErr 
  2569 TclOSAActiveProc(
  2570     long refCon)
  2571 {
  2572     tclOSAComponent *theComponent = (tclOSAComponent *) refCon;
  2573 	
  2574     Tcl_DoOneEvent(TCL_DONT_WAIT);
  2575     InvokeOSAActiveUPP(theComponent->defRefCon, theComponent->defActiveProc);
  2576 	
  2577     return noErr;
  2578 }
  2579 
  2580 /*
  2581  *----------------------------------------------------------------------
  2582  *
  2583  * ASCIICompareProc --
  2584  *
  2585  *	Trivial ascii compare for use with qsort.	
  2586  *
  2587  * Results:
  2588  *	strcmp of the two input strings
  2589  *
  2590  * Side effects:
  2591  *	None
  2592  *
  2593  *----------------------------------------------------------------------
  2594  */
  2595 static int 
  2596 ASCIICompareProc(const void *first,const void *second)
  2597 {
  2598     int order;
  2599     
  2600     char *firstString = *((char **) first);
  2601     char *secondString = *((char **) second);
  2602 
  2603     order = strcmp(firstString, secondString);
  2604 	
  2605     return order;
  2606 }
  2607 
  2608 #define REALLOC_INCR 30
  2609 /*
  2610  *----------------------------------------------------------------------
  2611  *
  2612  * getSortedHashKeys --
  2613  *
  2614  *	returns an alphabetically sorted list of the keys of the hash
  2615  *	theTable which match the string "pattern" in the DString
  2616  *	theResult. pattern == NULL matches all.
  2617  *
  2618  * Results:
  2619  *	None
  2620  *
  2621  * Side effects:
  2622  *	ReInitializes the DString theResult, then copies the names of
  2623  *	the matching keys into the string as list elements.
  2624  *
  2625  *----------------------------------------------------------------------
  2626  */
  2627  
  2628 static void 
  2629 getSortedHashKeys(
  2630     Tcl_HashTable *theTable,
  2631     CONST char *pattern,
  2632     Tcl_DString *theResult)
  2633 {
  2634     Tcl_HashSearch search;
  2635     Tcl_HashEntry *hPtr;
  2636     Boolean compare = true;
  2637     char *keyPtr;
  2638     static char **resultArgv = NULL;
  2639     static int totSize = 0;
  2640     int totElem = 0, i;
  2641 	
  2642     if (pattern == NULL || *pattern == '\0' || 
  2643 	    (*pattern == '*' && *(pattern + 1) == '\0')) {
  2644 	compare = false;
  2645     }
  2646 	
  2647     for (hPtr = Tcl_FirstHashEntry(theTable,&search), totElem = 0;
  2648 	 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  2649 			
  2650 	keyPtr = (char *) Tcl_GetHashKey(theTable, hPtr);
  2651 	if (!compare || Tcl_StringMatch(keyPtr, pattern)) {
  2652 	    totElem++;		
  2653 	    if (totElem >= totSize) {
  2654 		totSize += REALLOC_INCR;
  2655 		resultArgv = (char **) ckrealloc((char *) resultArgv,
  2656 			totSize * sizeof(char *));
  2657 	    }
  2658 	    resultArgv[totElem - 1] = keyPtr;
  2659 	} 
  2660     }
  2661 		
  2662     Tcl_DStringInit(theResult);
  2663     if (totElem == 1) {
  2664 	Tcl_DStringAppendElement(theResult, resultArgv[0]);
  2665     } else if (totElem > 1) {
  2666 	qsort((VOID *) resultArgv, (size_t) totElem, sizeof (char *),
  2667 		ASCIICompareProc);
  2668 
  2669 	for (i = 0; i < totElem; i++) {
  2670 	    Tcl_DStringAppendElement(theResult, resultArgv[i]);
  2671 	}
  2672     }	
  2673 }
  2674 
  2675 /*
  2676  *----------------------------------------------------------------------
  2677  *
  2678  * prepareScriptData --
  2679  *
  2680  *	Massages the input data in the argv array, concating the 
  2681  *	elements, with a " " between each, and replacing \n with \r,
  2682  *	and \\n with "  ".  Puts the result in the the DString scrptData,
  2683  *	and copies the result to the AEdesc scrptDesc.
  2684  *
  2685  * Results:
  2686  *	Standard Tcl result
  2687  *
  2688  * Side effects:
  2689  *	Creates a new Handle (with AECreateDesc) for the script data.
  2690  *	Stores the script in scrptData, or the error message if there
  2691  *	is an error creating the descriptor.
  2692  *
  2693  *----------------------------------------------------------------------
  2694  */
  2695  
  2696 static int
  2697 prepareScriptData(
  2698     int argc,
  2699     CONST char **argv,
  2700     Tcl_DString *scrptData,
  2701     AEDesc *scrptDesc) 
  2702 {
  2703     char * ptr;
  2704     int i;
  2705     char buffer[7];
  2706     OSErr sysErr = noErr;
  2707     Tcl_DString encodedText;
  2708 
  2709     Tcl_DStringInit(scrptData);
  2710 
  2711     for (i = 0; i < argc; i++) {
  2712 	Tcl_DStringAppend(scrptData, argv[i], -1);
  2713 	Tcl_DStringAppend(scrptData, " ", 1);
  2714     }
  2715 
  2716     /*
  2717      * First replace the \n's with \r's in the script argument
  2718      * Also replace "\\n" with "  ".
  2719      */
  2720 
  2721     for (ptr = scrptData->string; *ptr != '\0'; ptr++) {
  2722 	if (*ptr == '\n') {
  2723 	    *ptr = '\r';
  2724 	} else if (*ptr == '\\') {
  2725 	    if (*(ptr + 1) == '\n') {
  2726 		*ptr = ' ';
  2727 		*(ptr + 1) = ' ';
  2728 	    }
  2729 	}
  2730     }
  2731 
  2732     Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(scrptData),
  2733 	    Tcl_DStringLength(scrptData), &encodedText);
  2734     sysErr = AECreateDesc(typeChar, Tcl_DStringValue(&encodedText),
  2735 	    Tcl_DStringLength(&encodedText), scrptDesc);
  2736     Tcl_DStringFree(&encodedText);
  2737 
  2738     if (sysErr != noErr) {
  2739 	sprintf(buffer, "%6d", sysErr);
  2740 	Tcl_DStringFree(scrptData);
  2741 	Tcl_DStringAppend(scrptData, "Error #", 7);
  2742 	Tcl_DStringAppend(scrptData, buffer, -1);
  2743 	Tcl_DStringAppend(scrptData, " creating Script Data Descriptor.", 33);
  2744 	return TCL_ERROR;					
  2745     }
  2746 
  2747     return TCL_OK;
  2748 }
  2749 
  2750 /*
  2751  *----------------------------------------------------------------------
  2752  *
  2753  * tclOSAResultFromID --
  2754  *
  2755  *	Gets a human readable version of the result from the script ID
  2756  *	and returns it in the result of the interpreter interp
  2757  *
  2758  * Results:
  2759  *	None
  2760  *
  2761  * Side effects:
  2762  *	Sets the result of interp to the human readable version of resultID.
  2763  *  
  2764  *
  2765  *----------------------------------------------------------------------
  2766  */
  2767  
  2768 void 
  2769 tclOSAResultFromID(
  2770     Tcl_Interp *interp,
  2771     ComponentInstance theComponent,
  2772     OSAID resultID )
  2773 {
  2774     OSErr myErr = noErr;
  2775     AEDesc resultDesc;
  2776     Tcl_DString resultStr;
  2777 	
  2778     Tcl_DStringInit(&resultStr);
  2779 	
  2780     myErr = OSADisplay(theComponent, resultID, typeChar,
  2781 	    kOSAModeNull, &resultDesc);
  2782     Tcl_DStringAppend(&resultStr, (char *) *resultDesc.dataHandle,
  2783 	    GetHandleSize(resultDesc.dataHandle));
  2784     Tcl_DStringResult(interp,&resultStr);
  2785 }
  2786 
  2787 /*
  2788  *----------------------------------------------------------------------
  2789  *
  2790  * tclOSAASError --
  2791  *
  2792  *	Gets the error message from the AppleScript component, and adds
  2793  *	it to interp's result. If the script data is known, will point
  2794  *	out the offending bit of code.  This MUST BE A NULL TERMINATED
  2795  *	C-STRING, not a typeChar.
  2796  *
  2797  * Results:
  2798  *	None
  2799  *
  2800  * Side effects:
  2801  *	Sets the result of interp to error, plus the relevant portion
  2802  *	of the script.
  2803  *
  2804  *----------------------------------------------------------------------
  2805  */
  2806  
  2807 void 
  2808 tclOSAASError(
  2809     Tcl_Interp * interp,
  2810     ComponentInstance theComponent,
  2811     char *scriptData )
  2812 {
  2813     OSErr myErr = noErr;
  2814     AEDesc errResult,errLimits;
  2815     Tcl_DString errStr;
  2816     DescType returnType;
  2817     Size returnSize;
  2818     short srcStart,srcEnd;
  2819     char buffer[16];
  2820 	
  2821     Tcl_DStringInit(&errStr);
  2822     Tcl_DStringAppend(&errStr, "An AppleScript error was encountered.\n", -1); 
  2823 	
  2824     OSAScriptError(theComponent, kOSAErrorNumber,
  2825 	    typeShortInteger, &errResult);
  2826 	
  2827     sprintf(buffer, "Error #%-6.6d\n", (short int) **errResult.dataHandle);
  2828 
  2829     AEDisposeDesc(&errResult);
  2830 	
  2831     Tcl_DStringAppend(&errStr,buffer, 15);
  2832 	
  2833     OSAScriptError(theComponent, kOSAErrorMessage, typeChar, &errResult);
  2834     Tcl_DStringAppend(&errStr, (char *) *errResult.dataHandle,
  2835 	    GetHandleSize(errResult.dataHandle));
  2836     AEDisposeDesc(&errResult);
  2837 	
  2838     if (scriptData != NULL) {
  2839 	int lowerB, upperB;
  2840 		
  2841 	myErr = OSAScriptError(theComponent, kOSAErrorRange,
  2842 		typeOSAErrorRange, &errResult);
  2843 		
  2844 	myErr = AECoerceDesc(&errResult, typeAERecord, &errLimits);
  2845 	myErr = AEGetKeyPtr(&errLimits, keyOSASourceStart,
  2846 		typeShortInteger, &returnType, &srcStart,
  2847 		sizeof(short int), &returnSize);
  2848 	myErr = AEGetKeyPtr(&errLimits, keyOSASourceEnd, typeShortInteger,
  2849 		&returnType, &srcEnd, sizeof(short int), &returnSize);
  2850 	AEDisposeDesc(&errResult);
  2851 	AEDisposeDesc(&errLimits);
  2852 
  2853 	Tcl_DStringAppend(&errStr, "\nThe offending bit of code was:\n\t", -1);
  2854 	/*
  2855 	 * Get the full line on which the error occured:
  2856 	 */
  2857 	for (lowerB = srcStart; lowerB > 0; lowerB--) {
  2858 	    if (*(scriptData + lowerB ) == '\r') {
  2859 		lowerB++;
  2860 		break;
  2861 	    }
  2862 	}
  2863 		
  2864 	for (upperB = srcEnd; *(scriptData + upperB) != '\0'; upperB++) {
  2865 	    if (*(scriptData + upperB) == '\r') {
  2866 		break;
  2867 	    }
  2868 	}
  2869 
  2870 	Tcl_DStringAppend(&errStr, scriptData+lowerB, srcStart - lowerB);
  2871 	Tcl_DStringAppend(&errStr, "_", 1);
  2872 	Tcl_DStringAppend(&errStr, scriptData+srcStart, upperB - srcStart);
  2873     }
  2874 	
  2875     Tcl_DStringResult(interp,&errStr);
  2876 }
  2877 
  2878 /*
  2879  *----------------------------------------------------------------------
  2880  *
  2881  * GetRawDataFromDescriptor --
  2882  *
  2883  *	Get the data from a descriptor.
  2884  *
  2885  * Results:
  2886  *	None
  2887  *
  2888  * Side effects:
  2889  *	None.
  2890  *
  2891  *----------------------------------------------------------------------
  2892  */
  2893  
  2894 static void
  2895 GetRawDataFromDescriptor(
  2896     AEDesc *theDesc,
  2897     Ptr destPtr,
  2898     Size destMaxSize,
  2899     Size *actSize)
  2900   {
  2901       Size copySize;
  2902 
  2903       if (theDesc->dataHandle) {
  2904 	  HLock((Handle)theDesc->dataHandle);
  2905 	  *actSize = GetHandleSize((Handle)theDesc->dataHandle);
  2906 	  copySize = *actSize < destMaxSize ? *actSize : destMaxSize;
  2907 	  BlockMove(*theDesc->dataHandle, destPtr, copySize);
  2908 	  HUnlock((Handle)theDesc->dataHandle);
  2909       } else {
  2910 	  *actSize = 0;
  2911       }
  2912       
  2913   }
  2914 
  2915 /*
  2916  *----------------------------------------------------------------------
  2917  *
  2918  * GetRawDataFromDescriptor --
  2919  *
  2920  *	Get the data from a descriptor.  Assume it's a C string.
  2921  *
  2922  * Results:
  2923  *	None
  2924  *
  2925  * Side effects:
  2926  *	None.
  2927  *
  2928  *----------------------------------------------------------------------
  2929  */
  2930  
  2931 static OSErr
  2932 GetCStringFromDescriptor(
  2933     AEDesc *sourceDesc,
  2934     char *resultStr,
  2935     Size resultMaxSize,
  2936     Size *resultSize)
  2937 {
  2938     OSErr err;
  2939     AEDesc resultDesc;
  2940 
  2941     resultDesc.dataHandle = nil;
  2942 				
  2943     err = AECoerceDesc(sourceDesc, typeChar, &resultDesc);
  2944 		
  2945     if (!err) {
  2946 	GetRawDataFromDescriptor(&resultDesc, (Ptr) resultStr,
  2947 		resultMaxSize - 1, resultSize);
  2948 	resultStr[*resultSize] = 0;
  2949     } else {
  2950 	err = errAECoercionFail;
  2951     }
  2952 			
  2953     if (resultDesc.dataHandle) {
  2954 	AEDisposeDesc(&resultDesc);
  2955     }
  2956     
  2957     return err;
  2958 }