os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/mac/tclMacOSA.c
First public contribution.
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
9 * Copyright (c) 1996 Lucent Technologies and Jim Ingham
10 * Copyright (c) 1997 Sun Microsystems, Inc.
12 * See the file "License Terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 * RCS: @(#) $Id: tclMacOSA.c,v 1.10 2002/10/09 11:54:30 das Exp $
22 #include <AppleEvents.h>
23 #include <AppleScript.h>
25 #include <OSAGeneric.h>
29 #include <components.h>
31 #include <resources.h>
32 #include <FSpCompat.h>
34 * The following two Includes are from the More Files package.
36 #include <MoreFiles.h>
43 * I need this only for the call to FspGetFullPath,
44 * I'm really not poking my nose where it does not belong!
46 #include "tclMacInt.h"
49 * Data structures used by the OSA code.
51 typedef struct tclOSAScript {
57 typedef struct tclOSAContext {
61 typedef struct tclOSAComponent {
63 ComponentInstance theComponent; /* The OSA Component represented */
67 Tcl_HashTable contextTable; /* Hash Table linking the context names & ID's */
68 Tcl_HashTable scriptTable;
69 Tcl_Interp *theInterp;
70 OSAActiveUPP defActiveProc;
75 * Prototypes for static procedures.
78 static pascal OSErr TclOSAActiveProc _ANSI_ARGS_((long refCon));
79 static int TclOSACompileCmd _ANSI_ARGS_((Tcl_Interp *interp,
80 tclOSAComponent *OSAComponent, int argc,
82 static int tclOSADecompileCmd _ANSI_ARGS_((Tcl_Interp * Interp,
83 tclOSAComponent *OSAComponent, int argc,
85 static int tclOSADeleteCmd _ANSI_ARGS_((Tcl_Interp *interp,
86 tclOSAComponent *OSAComponent, int argc,
88 static int tclOSAExecuteCmd _ANSI_ARGS_((Tcl_Interp *interp,
89 tclOSAComponent *OSAComponent, int argc,
91 static int tclOSAInfoCmd _ANSI_ARGS_((Tcl_Interp *interp,
92 tclOSAComponent *OSAComponent, int argc,
94 static int tclOSALoadCmd _ANSI_ARGS_((Tcl_Interp *interp,
95 tclOSAComponent *OSAComponent, int argc,
97 static int tclOSARunCmd _ANSI_ARGS_((Tcl_Interp *interp,
98 tclOSAComponent *OSAComponent, int argc,
100 static int tclOSAStoreCmd _ANSI_ARGS_((Tcl_Interp *interp,
101 tclOSAComponent *OSAComponent, int argc,
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));
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.
158 int Tclapplescript_Init( Tcl_Interp *interp );
162 *----------------------------------------------------------------------
164 * Tclapplescript_Init --
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.
172 * A standard Tcl result.
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.
179 *----------------------------------------------------------------------
184 Tcl_Interp *interp) /* Tcl interpreter. */
188 Boolean gotAppleScript = false;
189 Boolean GotOneOSALanguage = false;
190 ComponentDescription compDescr = {
197 Component curComponent = (Component) 0;
198 ComponentInstance curOpenComponent;
199 Tcl_HashTable *ComponentTable;
200 Tcl_HashTable *LanguagesTable;
201 Tcl_HashEntry *hashEntry;
203 AEDesc componentName = { typeNull, NULL };
206 long appleScriptFlags;
209 * Perform the required stubs magic...
212 if (!Tcl_InitStubs(interp, "8.2", 0)) {
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.
222 LanguagesTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
224 if (LanguagesTable == NULL) {
225 panic("Memory Error Allocating Languages Hash Table");
228 Tcl_SetAssocData(interp, "OSAScript_LangTable", NULL, LanguagesTable);
229 Tcl_InitHashTable(LanguagesTable, TCL_STRING_KEYS);
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);
242 GotOneOSALanguage = true;
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.
250 curOpenComponent = OpenComponent(curComponent);
251 if (curOpenComponent == NULL) {
252 Tcl_AppendResult(interp,"Error opening component",
257 myErr = OSAScriptingComponentName(curOpenComponent,&componentName);
258 if (myErr == noErr) {
259 myErr = GetCStringFromDescriptor(&componentName,
260 nameStr, 31, &nameLen);
261 AEDisposeDesc(&componentName);
263 CloseComponent(curOpenComponent);
265 if (myErr == noErr) {
266 hashEntry = Tcl_CreateHashEntry(LanguagesTable,
268 Tcl_SetHashValue(hashEntry, (ClientData) foundComp);
270 Tcl_AppendResult(interp,"Error getting componentName.",
276 * Make sure AppleScript is loaded, otherwise we will
277 * not bother to make the AppleScript command.
279 if (foundComp->componentSubType == kAppleScriptSubtype) {
280 appleScriptFlags = foundComp->componentFlags;
281 gotAppleScript = true;
287 * Create the OSA command.
290 if (!GotOneOSALanguage) {
291 Tcl_AppendResult(interp,"Could not find any OSA languages",
297 * Create the Component Assoc Data & put it in the interpreter.
300 ComponentTable = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
302 if (ComponentTable == NULL) {
303 panic("Memory Error Allocating Hash Table");
306 Tcl_SetAssocData(interp, "OSAScript_CompTable", NULL, ComponentTable);
308 Tcl_InitHashTable(ComponentTable, TCL_STRING_KEYS);
311 * The OSA command is not currently supported.
312 Tcl_CreateCommand(interp, "OSA", Tcl_OSACmd, (ClientData) NULL,
313 (Tcl_CmdDeleteProc *) NULL);
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.
324 if (gotAppleScript) {
325 if (tclOSAMakeNewComponent(interp, "AppleScript",
326 "AppleScript English", kAppleScriptSubtype,
327 appleScriptFlags) == NULL ) {
332 return Tcl_PkgProvide(interp, "OSAConnect", "1.0");
336 *----------------------------------------------------------------------
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
346 * A standard Tcl result.
349 * Depends on the subcommand, see the user documentation
352 *----------------------------------------------------------------------
357 ClientData clientData,
362 static unsigned short componentCmdIndex = 0;
366 Tcl_HashTable *ComponentTable = NULL;
370 Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
371 argv[0], " option\"", (char *) NULL);
376 length = strlen(argv[1]);
379 * Query out the Component Table, since most of these commands use it...
382 ComponentTable = (Tcl_HashTable *) Tcl_GetAssocData(interp,
383 "OSAScript_CompTable", (Tcl_InterpDeleteProc **) NULL);
385 if (ComponentTable == NULL) {
386 Tcl_AppendResult(interp, "Error, could not get the Component Table",
387 " from the Associated data.", (char *) NULL);
391 if (c == 'c' && strncmp(argv[1],"close",length) == 0) {
392 Tcl_HashEntry *hashEntry;
394 Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
395 argv[0], " ",argv[1], " componentName\"",
400 if ((hashEntry = Tcl_FindHashEntry(ComponentTable,argv[2])) == NULL) {
401 Tcl_AppendResult(interp, "Component \"", argv[2], "\" not found",
405 Tcl_DeleteCommand(interp,argv[2]);
408 } else if (c == 'o' && strncmp(argv[1],"open",length) == 0) {
410 * Default language is AppleScript.
412 OSType scriptSubtype = kAppleScriptSubtype;
413 char *languageName = "AppleScript English";
415 ComponentDescription *theCD;
421 if (*argv[0] == '-') {
423 if (c == 'l' && strcmp(argv[0] + 1, "language") == 0) {
425 Tcl_AppendResult(interp,
426 "Error - no language provided for the -language switch",
430 Tcl_HashEntry *hashEntry;
431 Tcl_HashSearch search;
432 Boolean gotIt = false;
433 Tcl_HashTable *LanguagesTable;
436 * Look up the language in the languages table
437 * Do a simple strstr match, so AppleScript
438 * will match "AppleScript English"...
441 LanguagesTable = Tcl_GetAssocData(interp,
442 "OSAScript_LangTable",
443 (Tcl_InterpDeleteProc **) NULL);
446 Tcl_FirstHashEntry(LanguagesTable, &search);
448 hashEntry = Tcl_NextHashEntry(&search)) {
449 languageName = Tcl_GetHashKey(LanguagesTable,
451 if (strstr(languageName,argv[1]) != NULL) {
452 theCD = (ComponentDescription *)
453 Tcl_GetHashValue(hashEntry);
459 Tcl_AppendResult(interp,
460 "Error, could not find the language \"",
462 "\" in the list of known languages.",
471 Tcl_AppendResult(interp, "Expected a flag, but got ",
472 argv[0], (char *) NULL);
477 sprintf(autoName, "OSAComponent%-d", componentCmdIndex++);
478 if (tclOSAMakeNewComponent(interp, autoName, languageName,
479 theCD->componentSubType, theCD->componentFlags) == NULL ) {
482 Tcl_SetResult(interp,autoName,TCL_VOLATILE);
486 } else if (c == 'i' && strncmp(argv[1],"info",length) == 0) {
488 Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
489 argv[0], " ", argv[1], " what\"",
495 length = strlen(argv[2]);
497 if (c == 'c' && strncmp(argv[2], "components", length) == 0) {
498 Tcl_DString theResult;
500 Tcl_DStringInit(&theResult);
503 getSortedHashKeys(ComponentTable,(char *) NULL, &theResult);
504 } else if (argc == 4) {
505 getSortedHashKeys(ComponentTable, argv[3], &theResult);
507 Tcl_AppendResult(interp, "Error: wrong # of arguments",
508 ", should be \"", argv[0], " ", argv[1], " ",
509 argv[2], " ?pattern?\".", (char *) NULL);
512 Tcl_DStringResult(interp, &theResult);
514 } else if (c == 'l' && strncmp(argv[2],"languages",length) == 0) {
515 Tcl_DString theResult;
516 Tcl_HashTable *LanguagesTable;
518 Tcl_DStringInit(&theResult);
519 LanguagesTable = Tcl_GetAssocData(interp,
520 "OSAScript_LangTable", (Tcl_InterpDeleteProc **) NULL);
523 getSortedHashKeys(LanguagesTable, (char *) NULL, &theResult);
524 } else if (argc == 4) {
525 getSortedHashKeys(LanguagesTable, argv[3], &theResult);
527 Tcl_AppendResult(interp, "Error: wrong # of arguments",
528 ", should be \"", argv[0], " ", argv[1], " ",
529 argv[2], " ?pattern?\".", (char *) NULL);
532 Tcl_DStringResult(interp,&theResult);
535 Tcl_AppendResult(interp, "Unknown option: ", argv[2],
536 " for OSA info, should be one of",
537 " \"components\" or \"languages\"",
542 Tcl_AppendResult(interp, "Unknown option: ", argv[1],
543 ", should be one of \"open\", \"close\" or \"info\".",
551 *----------------------------------------------------------------------
553 * Tcl_OSAComponentCmd --
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
570 * A standard Tcl result
573 * Depends on the subcommand, see the user documentation
576 *----------------------------------------------------------------------
581 ClientData clientData,
589 tclOSAComponent *OSAComponent = (tclOSAComponent *) clientData;
592 Tcl_AppendResult(interp, "wrong # args: should be \"",
593 argv[0], " option ?arg ...?\"",
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);
617 Tcl_AppendResult(interp,"bad option \"", argv[1],
618 "\": should be compile, decompile, delete, ",
619 "execute, info, load, run or store",
628 *----------------------------------------------------------------------
630 * TclOSACompileCmd --
632 * This is the compile subcommand for the component command.
635 * A standard Tcl result
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
643 *----------------------------------------------------------------------
649 tclOSAComponent *OSAComponent,
653 int tclError = TCL_OK;
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;
669 if (!(OSAComponent->componentFlags && kOSASupportsCompiling)) {
670 Tcl_AppendResult(interp,
671 "OSA component does not support compiling",
677 * This signals that we should make up a name, which is the
686 Tcl_AppendResult(interp,
687 "wrong # args: should be \"", argv[0], " ", argv[1],
688 " ?options? code\"",(char *) NULL);
696 * Do the argument parsing.
701 if (*argv[0] == '-') {
705 * "--" is the only switch that has no value, stops processing
708 if (c == '-' && *(argv[0] + 2) == '\0') {
715 * So we can check here a switch with no value.
719 Tcl_AppendResult(interp,
720 "no value given for switch: ",
721 argv[0], (char *) NULL);
725 if (c == 'c' && strcmp(argv[0] + 1, "context") == 0) {
726 if (Tcl_GetBoolean(interp, argv[1], &makeContext) != TCL_OK) {
729 } else if (c == 'a' && strcmp(argv[0] + 1, "augment") == 0) {
731 * Augment the current context which implies making a context.
734 if (Tcl_GetBoolean(interp, argv[1], &augment) != TCL_OK) {
738 } else if (c == 'n' && strcmp(argv[0] + 1, "name") == 0) {
739 strncpy(autoName, argv[1], 15);
741 resultName = autoName;
742 } else if (c == 'p' && strcmp(argv[0] + 1,"parent") == 0) {
744 * Since this implies we are compiling into a context,
745 * set makeContext here
747 if (tclOSAGetContextID(OSAComponent,
748 argv[1], &parentID) != TCL_OK) {
749 Tcl_AppendResult(interp, "context not found \"",
750 argv[1], "\"", (char *) NULL);
755 Tcl_AppendResult(interp, "bad option \"", argv[0],
756 "\": should be -augment, -context, -name or -parent",
769 * Make sure we have some data left...
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
785 modeFlags |= kOSAModeCompileIntoContext;
786 if (resultName == NULL) {
788 * Auto name the new context.
790 resultName = autoName;
791 resultID = kOSANullScript;
792 makeNewContext = true;
793 } else if (tclOSAGetContextID(OSAComponent,
794 resultName, &resultID) == TCL_OK) {
796 makeNewContext = true;
800 * Deal with the augment now...
802 if (augment && !makeNewContext) {
803 modeFlags |= kOSAModeAugmentContext;
805 } else if (resultName == NULL) {
806 resultName = autoName; /* Auto name the script */
810 * Ok, now we have the options, so we can compile the script data.
813 if (prepareScriptData(argc, argv, &scrptData, &scrptDesc) == TCL_ERROR) {
814 Tcl_DStringResult(interp, &scrptData);
815 AEDisposeDesc(&scrptDesc);
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.
824 if (parentID != kOSANullScript && makeNewContext) {
825 AEDesc contextDesc = { typeNull, NULL };
827 osaErr = OSAMakeContext(OSAComponent->theComponent,
828 &contextDesc, parentID, &resultID);
829 modeFlags |= kOSAModeAugmentContext;
832 osaErr = OSACompile(OSAComponent->theComponent, &scrptDesc,
833 modeFlags, &resultID);
834 if (osaErr == noErr) {
838 * For the compiled context to be active, you need to run
839 * the code that is in the context.
843 osaErr = OSAExecute(OSAComponent->theComponent, resultID,
844 resultID, kOSAModeCanInteract, &activateID);
845 OSADispose(OSAComponent->theComponent, activateID);
847 if (osaErr == noErr) {
848 if (makeNewContext) {
850 * If we have compiled into a context,
851 * this is added to the context table
854 tclOSAAddContext(OSAComponent, resultName, resultID);
857 Tcl_SetResult(interp, resultName, TCL_VOLATILE);
862 * For a script, we return the script name.
864 tclOSAAddScript(OSAComponent, resultName, modeFlags, resultID);
865 Tcl_SetResult(interp, resultName, TCL_VOLATILE);
871 * This catches the error either from the original compile,
872 * or from the execute in case makeContext == true
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;
886 Tcl_DStringFree(&scrptData);
887 AEDisposeDesc(&scrptDesc);
893 *----------------------------------------------------------------------
895 * tclOSADecompileCmd --
897 * This implements the Decompile subcommand of the component command
900 * A standard Tcl result.
903 * Decompiles the script, and sets interp's result to the
904 * decompiled script data.
906 *----------------------------------------------------------------------
912 tclOSAComponent *OSAComponent,
916 AEDesc resultingSourceData = { typeChar, NULL };
920 OSErr sysErr = noErr;
923 Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
924 argv[0], " ",argv[1], " scriptName \"", (char *) NULL );
928 if (!(OSAComponent->componentFlags && kOSASupportsGetSource)) {
929 Tcl_AppendResult(interp,
930 "Error, this component does not support get source",
935 if (tclOSAGetScriptID(OSAComponent, argv[2], &scriptID) == TCL_OK) {
937 } else if (tclOSAGetContextID(OSAComponent, argv[2], &scriptID)
941 Tcl_AppendResult(interp, "Could not find script \"",
942 argv[2], "\"", (char *) NULL);
946 OSAGetScriptInfo(OSAComponent->theComponent, scriptID,
947 kOSACanGetSource, &result);
949 sysErr = OSAGetSource(OSAComponent->theComponent,
950 scriptID, typeChar, &resultingSourceData);
952 if (sysErr == noErr) {
953 Tcl_DString theResult;
954 Tcl_DStringInit(&theResult);
956 Tcl_DStringAppend(&theResult, *resultingSourceData.dataHandle,
957 GetHandleSize(resultingSourceData.dataHandle));
958 Tcl_DStringResult(interp, &theResult);
959 AEDisposeDesc(&resultingSourceData);
962 Tcl_AppendResult(interp, "Error getting source data", (char *) NULL);
963 AEDisposeDesc(&resultingSourceData);
969 *----------------------------------------------------------------------
973 * This implements the Delete subcommand of the Component command.
976 * A standard Tcl result.
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.
983 *----------------------------------------------------------------------
989 tclOSAComponent *OSAComponent,
993 char c,*errMsg = NULL;
997 Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
998 argv[0], " ", argv[1], " what scriptName", (char *) NULL);
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",
1009 } else if (tclOSADeleteContext(OSAComponent, argv[3]) != TCL_OK) {
1010 Tcl_AppendResult(interp, "Error deleting script \"", argv[2],
1011 "\": ", errMsg, (char *) NULL);
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);
1023 Tcl_AppendResult(interp,"Unknown value ", argv[2],
1024 " should be one of ",
1025 "\"context\" or \"script\".",
1033 *----------------------------------------------------------------------
1035 * tclOSAExecuteCmd --
1037 * This implements the execute subcommand of the component command.
1040 * A standard Tcl result.
1043 * Executes the given script data, and sets interp's result to
1044 * the OSA component's return value.
1046 *----------------------------------------------------------------------
1052 tclOSAComponent *OSAComponent,
1056 int tclError = TCL_OK, resID = 128;
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;
1070 Tcl_AppendResult(interp,
1071 "Error, no script data for \"", argv[0],
1072 " run\"", (char *) NULL);
1080 * Set the context to the global context by default.
1081 * Then parse the argument list for switches
1083 tclOSAGetContextID(OSAComponent, "global", &contextID);
1087 if (*argv[0] == '-') {
1091 * "--" is the only switch that has no value.
1094 if (c == '-' && *(argv[0] + 2) == '\0') {
1101 * So we can check here for a switch with no value.
1105 Tcl_AppendResult(interp,
1106 "Error, no value given for switch ",
1107 argv[0], (char *) NULL);
1111 if (c == 'c' && strcmp(argv[0] + 1, "context") == 0) {
1112 if (tclOSAGetContextID(OSAComponent,
1113 argv[1], &contextID) == TCL_OK) {
1115 Tcl_AppendResult(interp, "Script context \"",
1116 argv[1], "\" not found", (char *) NULL);
1120 Tcl_AppendResult(interp, "Error, invalid switch ", argv[0],
1121 " should be \"-context\"", (char *) NULL);
1133 Tcl_AppendResult(interp, "Error, no script data", (char *) NULL);
1137 if (prepareScriptData(argc, argv, &scrptData, &scrptDesc) == TCL_ERROR) {
1138 Tcl_DStringResult(interp, &scrptData);
1139 AEDisposeDesc(&scrptDesc);
1143 * Now try to compile and run, but check to make sure the
1144 * component supports the one shot deal
1146 if (OSAComponent->componentFlags && kOSASupportsConvenience) {
1147 osaErr = OSACompileExecute(OSAComponent->theComponent,
1148 &scrptDesc, contextID, modeFlags, &resultID);
1151 * If not, we have to do this ourselves
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);
1161 OSADispose(OSAComponent->theComponent, compiledID);
1164 * The scripting component had better be able to load text data...
1166 OSAID loadedID = kOSANullScript;
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);
1175 OSADispose(OSAComponent->theComponent, loadedID);
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;
1187 tclOSAResultFromID(interp, OSAComponent->theComponent, resultID);
1188 osaErr = OSADispose(OSAComponent->theComponent, resultID);
1192 Tcl_DStringFree(&scrptData);
1193 AEDisposeDesc(&scrptDesc);
1199 *----------------------------------------------------------------------
1203 * This implements the Info subcommand of the component command
1206 * A standard Tcl result.
1209 * Info on scripts and contexts. See the user documentation for details.
1211 *----------------------------------------------------------------------
1216 tclOSAComponent *OSAComponent,
1222 Tcl_DString theResult;
1225 Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
1226 argv[0], " ", argv[1], " what \"", (char *) NULL );
1231 length = strlen(argv[2]);
1232 if (c == 's' && strncmp(argv[2], "scripts", length) == 0) {
1233 Tcl_DStringInit(&theResult);
1235 getSortedHashKeys(&OSAComponent->scriptTable, (char *) NULL,
1237 } else if (argc == 4) {
1238 getSortedHashKeys(&OSAComponent->scriptTable, argv[3], &theResult);
1240 Tcl_AppendResult(interp, "Error: wrong # of arguments,",
1241 " should be \"", argv[0], " ", argv[1], " ",
1242 argv[2], " ?pattern?", (char *) NULL);
1245 Tcl_DStringResult(interp, &theResult);
1247 } else if (c == 'c' && strncmp(argv[2], "contexts", length) == 0) {
1248 Tcl_DStringInit(&theResult);
1250 getSortedHashKeys(&OSAComponent->contextTable, (char *) NULL,
1252 } else if (argc == 4) {
1253 getSortedHashKeys(&OSAComponent->contextTable,
1254 argv[3], &theResult);
1256 Tcl_AppendResult(interp, "Error: wrong # of arguments for ,",
1257 " should be \"", argv[0], " ", argv[1], " ",
1258 argv[2], " ?pattern?", (char *) NULL);
1261 Tcl_DStringResult(interp, &theResult);
1263 } else if (c == 'l' && strncmp(argv[2], "language", length) == 0) {
1264 Tcl_SetResult(interp, OSAComponent->languageName, TCL_STATIC);
1267 Tcl_AppendResult(interp, "Unknown argument \"", argv[2],
1268 "\" for \"", argv[0], " info \", should be one of ",
1269 "\"scripts\" \"language\", or \"contexts\"",
1276 *----------------------------------------------------------------------
1280 * This is the load subcommand for the Component Command
1284 * A standard Tcl result.
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.
1290 *----------------------------------------------------------------------
1296 tclOSAComponent *OSAComponent,
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;
1315 scriptName = autoName;
1316 contextName = autoName;
1319 Tcl_AppendResult(interp,
1320 "Error, no data for \"", argv[0], " ", argv[1],
1321 "\"", (char *) NULL);
1329 * Do the argument parsing.
1334 if (*argv[0] == '-') {
1338 * "--" is the only switch that has no value.
1341 if (c == '-' && *(argv[0] + 2) == '\0') {
1348 * So we can check here a switch with no value.
1352 Tcl_AppendResult(interp, "Error, no value given for switch ",
1353 argv[0], (char *) NULL);
1357 if (c == 'r' && strcmp(argv[0] + 1, "rsrcname") == 0) {
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);
1366 Tcl_AppendResult(interp, "Error, invalid switch ", argv[0],
1367 " should be \"--\", \"-rsrcname\" or \"-rsrcid\"",
1379 * Ok, now we have the options, so we can load the resource,
1382 Tcl_AppendResult(interp, "Error, no filename given", (char *) NULL);
1386 if (tclOSALoad(interp, OSAComponent, resName, resID,
1387 argv[0], &resultID) != TCL_OK) {
1388 Tcl_AppendResult(interp, "Error in load command", (char *) NULL);
1393 * Now find out whether we have a script, or a script context.
1396 OSAGetScriptInfo(OSAComponent->theComponent, resultID,
1397 kOSAScriptIsTypeScriptContext, &scptInfo);
1401 tclOSAAddContext(OSAComponent, autoName, resultID);
1403 Tcl_SetResult(interp, autoName, TCL_VOLATILE);
1406 * For a script, we return the script name
1409 tclOSAAddScript(OSAComponent, autoName, kOSAModeCanInteract, resultID);
1410 Tcl_SetResult(interp, autoName, TCL_VOLATILE);
1416 *----------------------------------------------------------------------
1420 * This implements the run subcommand of the component command
1423 * A standard Tcl result.
1426 * Runs the given compiled script, and returns the OSA
1427 * component's result.
1429 *----------------------------------------------------------------------
1435 tclOSAComponent *OSAComponent,
1439 int tclError = TCL_OK,
1441 char c, *contextName = 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];
1455 Tcl_AppendResult(interp, "Wrong # of arguments, should be \"",
1456 argv[0], " ", argv[1], " scriptName", (char *) NULL);
1461 * Set the context to the global context for this component,
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 );
1472 * Now parse the argument list for switches
1478 if (*argv[0] == '-') {
1481 * "--" is the only switch that has no value
1483 if (c == '-' && *(argv[0] + 2) == '\0') {
1490 * So we can check here for a switch with no value.
1493 Tcl_AppendResult(interp, "Error, no value given for switch ",
1494 argv[0], (char *) NULL);
1498 if (c == 'c' && strcmp(argv[0] + 1, "context") == 0) {
1500 Tcl_AppendResult(interp,
1501 "Error - no context provided for the -context switch",
1504 } else if (tclOSAGetContextID(OSAComponent,
1505 argv[1], &contextID) == TCL_OK) {
1507 Tcl_AppendResult(interp, "Script context \"", argv[1],
1508 "\" not found", (char *) NULL);
1512 Tcl_AppendResult(interp, "Error, invalid switch ", argv[0],
1513 " for ", componentName,
1514 " should be \"-context\"", (char *) NULL);
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);
1532 sysErr = OSAExecute(OSAComponent->theComponent,
1533 scriptID, contextID, modeFlags, &resultID);
1535 if (sysErr == errOSAScriptError) {
1536 tclOSAASError(interp, OSAComponent->theComponent, (char *) NULL);
1537 tclError = TCL_ERROR;
1538 } else if (sysErr != noErr) {
1540 sprintf(buffer, "Error #%6.6d encountered in run", sysErr);
1541 Tcl_SetResult(interp, buffer, TCL_VOLATILE);
1542 tclError = TCL_ERROR;
1544 tclOSAResultFromID(interp, OSAComponent->theComponent, resultID );
1546 OSADispose(OSAComponent->theComponent, resultID);
1552 *----------------------------------------------------------------------
1556 * This implements the store subcommand of the component command
1559 * A standard Tcl result.
1562 * Runs the given compiled script, and returns the OSA
1563 * component's result.
1565 *----------------------------------------------------------------------
1571 tclOSAComponent *OSAComponent,
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;
1588 Tcl_AppendResult(interp, "Error, no data for \"", argv[0],
1589 " ",argv[1], "\"", (char *) NULL);
1597 * Do the argument parsing
1601 if (*argv[0] == '-') {
1605 * "--" is the only switch that has no value
1607 if (c == '-' && *(argv[0] + 2) == '\0') {
1614 * So we can check here a switch with no value.
1617 Tcl_AppendResult(interp,
1618 "Error, no value given for switch ",
1619 argv[0], (char *) NULL);
1623 if (c == 'r' && strcmp(argv[0] + 1, "rsrcname") == 0) {
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);
1632 Tcl_AppendResult(interp, "Error, invalid switch ", argv[0],
1633 " should be \"--\", \"-rsrcname\" or \"-rsrcid\"",
1645 * Ok, now we have the options, so we can load the resource,
1648 Tcl_AppendResult(interp, "Error, wrong # of arguments, should be ",
1649 argv[0], " ", argv[1], "?option flag? scriptName fileName",
1654 if (tclOSAStore(interp, OSAComponent, resName, resID,
1655 argv[0], argv[1]) != TCL_OK) {
1656 Tcl_AppendResult(interp, "Error in load command", (char *) NULL);
1659 Tcl_ResetResult(interp);
1667 *----------------------------------------------------------------------
1669 * tclOSAMakeNewComponent --
1671 * Makes a command cmdName to represent a new connection to the
1672 * OSA component with componentSubType scriptSubtype.
1675 * Returns the tclOSAComponent structure for the connection.
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.
1682 *----------------------------------------------------------------------
1686 tclOSAMakeNewComponent(
1690 OSType scriptSubtype,
1691 long componentFlags)
1694 AEDesc resultingName = {typeNull, NULL};
1695 AEDesc nullDesc = {typeNull, NULL };
1696 OSAID globalContext;
1697 char global[] = "global";
1699 ComponentDescription requestedComponent = {
1706 Tcl_HashTable *ComponentTable;
1707 Component foundComponent = NULL;
1708 OSAActiveUPP myActiveProcUPP;
1710 tclOSAComponent *newComponent;
1711 Tcl_HashEntry *hashEntry;
1714 requestedComponent.componentSubType = scriptSubtype;
1715 nbytes = sizeof(tclOSAComponent);
1716 newComponent = (tclOSAComponent *) ckalloc(sizeof(tclOSAComponent));
1717 if (newComponent == NULL) {
1721 foundComponent = FindNextComponent(0, &requestedComponent);
1722 if (foundComponent == 0) {
1723 Tcl_AppendResult(interp,
1724 "Could not find component of requested type", (char *) NULL);
1728 newComponent->theComponent = OpenComponent(foundComponent);
1730 if (newComponent->theComponent == NULL) {
1731 Tcl_AppendResult(interp,
1732 "Could not open component of the requested type",
1737 newComponent->languageName = (char *) ckalloc(strlen(languageName) + 1);
1738 strcpy(newComponent->languageName,languageName);
1740 newComponent->componentFlags = componentFlags;
1742 newComponent->theInterp = interp;
1744 Tcl_InitHashTable(&newComponent->contextTable, TCL_STRING_KEYS);
1745 Tcl_InitHashTable(&newComponent->scriptTable, TCL_STRING_KEYS);
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);
1754 newComponent->languageID = scriptSubtype;
1756 newComponent->theName = (char *) ckalloc(strlen(cmdName) + 1 );
1757 strcpy(newComponent->theName, cmdName);
1759 Tcl_CreateCommand(interp, newComponent->theName, Tcl_OSAComponentCmd,
1760 (ClientData) newComponent, tclOSAClose);
1763 * Register the new component with the component table
1766 ComponentTable = (Tcl_HashTable *) Tcl_GetAssocData(interp,
1767 "OSAScript_CompTable", (Tcl_InterpDeleteProc **) NULL);
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;
1775 hashEntry = Tcl_CreateHashEntry(ComponentTable,
1776 newComponent->theName, &newPtr);
1777 Tcl_SetHashValue(hashEntry, (ClientData) newComponent);
1780 * Set the active proc to call Tcl_DoOneEvent() while idle
1782 if (OSAGetActiveProc(newComponent->theComponent,
1783 &newComponent->defActiveProc, &newComponent->defRefCon) != noErr ) {
1784 /* TODO -- clean up here... */
1787 myActiveProcUPP = NewOSAActiveUPP(TclOSAActiveProc);
1788 OSASetActiveProc(newComponent->theComponent,
1789 myActiveProcUPP, (long) newComponent);
1790 return newComponent;
1794 ckfree((char *) newComponent);
1795 return (tclOSAComponent *) NULL;
1799 *----------------------------------------------------------------------
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.
1811 * Closes the connection, and releases all the script data.
1813 *----------------------------------------------------------------------
1818 ClientData clientData)
1820 tclOSAComponent *theComponent = (tclOSAComponent *) clientData;
1821 Tcl_HashEntry *hashEntry;
1822 Tcl_HashSearch search;
1823 tclOSAScript *theScript;
1824 Tcl_HashTable *ComponentTable;
1827 * Delete the context and script tables
1828 * the memory for the language name, and
1832 for (hashEntry = Tcl_FirstHashEntry(&theComponent->scriptTable, &search);
1834 hashEntry = Tcl_NextHashEntry(&search)) {
1836 theScript = (tclOSAScript *) Tcl_GetHashValue(hashEntry);
1837 OSADispose(theComponent->theComponent, theScript->scriptID);
1838 ckfree((char *) theScript);
1839 Tcl_DeleteHashEntry(hashEntry);
1842 for (hashEntry = Tcl_FirstHashEntry(&theComponent->contextTable, &search);
1844 hashEntry = Tcl_NextHashEntry(&search)) {
1846 Tcl_DeleteHashEntry(hashEntry);
1849 ckfree(theComponent->languageName);
1850 ckfree(theComponent->theName);
1853 * Finally close the component
1856 CloseComponent(theComponent->theComponent);
1858 ComponentTable = (Tcl_HashTable *)
1859 Tcl_GetAssocData(theComponent->theInterp,
1860 "OSAScript_CompTable", (Tcl_InterpDeleteProc **) NULL);
1862 if (ComponentTable == NULL) {
1863 panic("Error, could not get the Component Table from the Associated data.");
1866 hashEntry = Tcl_FindHashEntry(ComponentTable, theComponent->theName);
1867 if (hashEntry != NULL) {
1868 Tcl_DeleteHashEntry(hashEntry);
1871 ckfree((char *) theComponent);
1875 *----------------------------------------------------------------------
1877 * tclOSAGetContextID --
1879 * This returns the context ID, given the component name.
1887 *----------------------------------------------------------------------
1892 tclOSAComponent *theComponent,
1893 CONST char *contextName,
1896 Tcl_HashEntry *hashEntry;
1897 tclOSAContext *contextStruct;
1899 if ((hashEntry = Tcl_FindHashEntry(&theComponent->contextTable,
1900 contextName)) == NULL ) {
1903 contextStruct = (tclOSAContext *) Tcl_GetHashValue(hashEntry);
1904 *theContext = contextStruct->contextID;
1910 *----------------------------------------------------------------------
1912 * tclOSAAddContext --
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.
1924 * Adds the script context to the component's context table.
1926 *----------------------------------------------------------------------
1931 tclOSAComponent *theComponent,
1933 const OSAID theContext)
1935 static unsigned short contextIndex = 0;
1936 tclOSAContext *contextStruct;
1937 Tcl_HashEntry *hashEntry;
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++);
1947 hashEntry = Tcl_CreateHashEntry(&theComponent->contextTable,
1948 contextName, &newPtr);
1950 contextStruct = (tclOSAContext *) ckalloc(sizeof(tclOSAContext));
1951 contextStruct->contextID = theContext;
1952 Tcl_SetHashValue(hashEntry,(ClientData) contextStruct);
1956 *----------------------------------------------------------------------
1958 * tclOSADeleteContext --
1960 * This deletes the context struct, with the name contextName.
1963 * A normal Tcl result
1966 * Removes the script context to the component's context table,
1967 * and deletes the data associated with it.
1969 *----------------------------------------------------------------------
1973 tclOSADeleteContext(
1974 tclOSAComponent *theComponent,
1975 CONST char *contextName)
1977 Tcl_HashEntry *hashEntry;
1978 tclOSAContext *contextStruct;
1980 hashEntry = Tcl_FindHashEntry(&theComponent->contextTable, contextName);
1981 if (hashEntry == NULL) {
1985 * Dispose of the script context data
1987 contextStruct = (tclOSAContext *) Tcl_GetHashValue(hashEntry);
1988 OSADispose(theComponent->theComponent,contextStruct->contextID);
1990 * Then the hash entry
1992 ckfree((char *) contextStruct);
1993 Tcl_DeleteHashEntry(hashEntry);
1998 *----------------------------------------------------------------------
2000 * tclOSAMakeContext --
2002 * This makes the context with name contextName, and returns the ID.
2005 * A standard Tcl result
2008 * Makes a new context, adds it to the context table, and returns
2009 * the new contextID in the variable theContext.
2011 *----------------------------------------------------------------------
2016 tclOSAComponent *theComponent,
2017 CONST char *contextName,
2020 AEDesc contextNameDesc = {typeNull, NULL};
2021 OSAError osaErr = noErr;
2023 AECreateDesc(typeChar, contextName, strlen(contextName), &contextNameDesc);
2024 osaErr = OSAMakeContext(theComponent->theComponent, &contextNameDesc,
2025 kOSANullScript, theContext);
2027 AEDisposeDesc(&contextNameDesc);
2029 if (osaErr == noErr) {
2031 strncpy(name, contextName, 23);
2033 tclOSAAddContext(theComponent, name, *theContext);
2035 *theContext = (OSAID) osaErr;
2043 *----------------------------------------------------------------------
2047 * This stores a script resource from the file named in fileName.
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.
2054 * A standard Tcl result.
2057 * The given script data is stored in the file fileName.
2059 *----------------------------------------------------------------------
2065 tclOSAComponent *theComponent,
2066 CONST char *resourceName,
2068 CONST char *scriptName,
2069 CONST char *fileName)
2073 int result = TCL_OK;
2074 short saveRef, fileRef = -1;
2075 char idStr[16 + TCL_INTEGER_SPACE];
2077 Tcl_DString ds, buffer;
2078 CONST char *nativeName;
2079 OSErr myErr = noErr;
2085 * First extract the script data
2088 if (tclOSAGetScriptID(theComponent, scriptName, &scriptID) != TCL_OK ) {
2089 if (tclOSAGetContextID(theComponent, scriptName, &scriptID)
2091 Tcl_AppendResult(interp, "Error getting script ",
2092 scriptName, (char *) NULL);
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);
2107 * Now try to open the output file
2110 saveRef = CurResFile();
2112 if (fileName != NULL) {
2115 if (Tcl_TranslateFileName(interp, fileName, &buffer) == NULL) {
2118 nativeName = Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&buffer),
2119 Tcl_DStringLength(&buffer), &ds);
2120 err = FSpLocationFromPath(strlen(nativeName), nativeName, &fileSpec);
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);
2131 FSpCreateResFileCompatTcl(&fileSpec,
2132 'WiSH', 'osas', smSystemScript);
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);
2140 goto rezEvalCleanUp;
2143 fileRef = FSpOpenResFileCompatTcl(&fileSpec, fsRdWrPerm);
2144 if (fileRef == -1) {
2145 Tcl_AppendResult(interp, "Error reading the file: \"",
2146 fileName, "\".", NULL);
2148 goto rezEvalCleanUp;
2150 UseResFile(fileRef);
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.
2163 * Load the resource by name
2165 if (resourceName != NULL) {
2166 strcpy((char *) rezName + 1, resourceName);
2167 rezName[0] = strlen(resourceName);
2168 resHandle = Get1NamedResource('scpt', rezName);
2170 if (resHandle == NULL) {
2172 * These signify either the resource or the resource
2173 * type were not found
2175 if (myErr == resNotFound || myErr == noErr) {
2177 while ((uniqueID = Unique1ID('scpt') ) < 128) {}
2178 AddResource(scriptData.dataHandle, 'scpt', uniqueID, rezName);
2179 WriteResource(resHandle);
2181 goto rezEvalCleanUp;
2184 * This means there was some other error, for now
2187 sprintf(idStr, "%d", myErr);
2188 Tcl_AppendResult(interp, "Error #", idStr,
2189 " opening scpt resource named ", resourceName,
2190 " in file ", fileName, (char *) NULL);
2192 goto rezEvalCleanUp;
2199 resHandle = Get1Resource('scpt', resourceNumber);
2203 if (resHandle == NULL) {
2205 * These signify either the resource or the resource
2206 * type were not found
2208 if (myErr == resNotFound || myErr == noErr) {
2209 AddResource(scriptData.dataHandle, 'scpt',
2210 resourceNumber, rezName);
2211 WriteResource(resHandle);
2213 goto rezEvalCleanUp;
2216 * This means there was some other error, for now
2218 sprintf(idStr, "%d", myErr);
2219 Tcl_AppendResult(interp, "Error #", idStr,
2220 " opening scpt resource named ", resourceName,
2221 " in file ", fileName,(char *) NULL);
2223 goto rezEvalCleanUp;
2229 * We get to here if the resource exists
2230 * we just copy into it...
2233 scriptSize = GetHandleSize(scriptData.dataHandle);
2234 SetHandleSize(resHandle, scriptSize);
2235 HLock(scriptData.dataHandle);
2237 BlockMove(*scriptData.dataHandle, *resHandle,scriptSize);
2238 HUnlock(scriptData.dataHandle);
2240 ChangedResource(resHandle);
2241 WriteResource(resHandle);
2243 goto rezEvalCleanUp;
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"),
2254 if (fileRef != -1) {
2255 CloseResFile(fileRef);
2258 UseResFile(saveRef);
2263 /*----------------------------------------------------------------------
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.
2273 * A standard Tcl result.
2276 * A new script element is created from the data in the file.
2277 * The script ID is passed out in the variable resultID.
2279 *----------------------------------------------------------------------
2285 tclOSAComponent *theComponent,
2286 CONST char *resourceName,
2288 CONST char *fileName,
2293 int result = TCL_OK;
2294 short saveRef, fileRef = -1;
2295 char idStr[16 + TCL_INTEGER_SPACE];
2297 Tcl_DString ds, buffer;
2298 CONST char *nativeName;
2300 saveRef = CurResFile();
2302 if (fileName != NULL) {
2305 if (Tcl_TranslateFileName(interp, fileName, &buffer) == NULL) {
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);
2314 Tcl_AppendResult(interp, "Error finding the file: \"",
2315 fileName, "\".", NULL);
2319 fileRef = FSpOpenResFileCompatTcl(&fileSpec, fsRdPerm);
2320 if (fileRef == -1) {
2321 Tcl_AppendResult(interp, "Error reading the file: \"",
2322 fileName, "\".", NULL);
2325 UseResFile(fileRef);
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.
2338 * Load the resource by name or ID
2340 if (resourceName != NULL) {
2341 strcpy((char *) rezName + 1, resourceName);
2342 rezName[0] = strlen(resourceName);
2343 sourceData = GetNamedResource('scpt', rezName);
2345 sourceData = GetResource('scpt', (short) resourceNumber);
2348 if (sourceData == NULL) {
2354 scriptDesc.descriptorType = typeOSAGenericStorage;
2355 scriptDesc.dataHandle = sourceData;
2357 osaErr = OSALoad(theComponent->theComponent, &scriptDesc,
2358 kOSAModeNull, resultID);
2360 ReleaseResource(sourceData);
2362 if (osaErr != noErr) {
2367 goto rezEvalCleanUp;
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"),
2379 if (fileRef != -1) {
2380 CloseResFile(fileRef);
2383 UseResFile(saveRef);
2389 *----------------------------------------------------------------------
2391 * tclOSAGetScriptID --
2393 * This returns the context ID, gibven the component name.
2396 * A standard Tcl result
2399 * Passes out the script ID in the variable scriptID.
2401 *----------------------------------------------------------------------
2406 tclOSAComponent *theComponent,
2407 CONST char *scriptName,
2410 tclOSAScript *theScript;
2412 theScript = tclOSAGetScript(theComponent, scriptName);
2413 if (theScript == NULL) {
2417 *scriptID = theScript->scriptID;
2422 *----------------------------------------------------------------------
2424 * tclOSAAddScript --
2426 * This adds a script to theComponent's script table, with the
2430 * A standard Tcl result
2433 * Adds an element to the component's script table.
2435 *----------------------------------------------------------------------
2440 tclOSAComponent *theComponent,
2445 Tcl_HashEntry *hashEntry;
2447 static int scriptIndex = 0;
2448 tclOSAScript *theScript;
2450 if (*scriptName == '\0') {
2451 sprintf(scriptName, "OSAScript%d", scriptIndex++);
2454 hashEntry = Tcl_CreateHashEntry(&theComponent->scriptTable,
2455 scriptName, &newPtr);
2457 theScript = (tclOSAScript *) Tcl_GetHashValue(hashEntry);
2458 OSADispose(theComponent->theComponent, theScript->scriptID);
2460 theScript = (tclOSAScript *) ckalloc(sizeof(tclOSAScript));
2461 if (theScript == NULL) {
2466 theScript->scriptID = scriptID;
2467 theScript->languageID = theComponent->languageID;
2468 theScript->modeFlags = modeFlags;
2470 Tcl_SetHashValue(hashEntry,(ClientData) theScript);
2476 *----------------------------------------------------------------------
2478 * tclOSAGetScriptID --
2480 * This returns the script structure, given the component and script name.
2483 * A pointer to the script structure.
2488 *----------------------------------------------------------------------
2491 static tclOSAScript *
2493 tclOSAComponent *theComponent,
2494 CONST char *scriptName)
2496 Tcl_HashEntry *hashEntry;
2498 hashEntry = Tcl_FindHashEntry(&theComponent->scriptTable, scriptName);
2499 if (hashEntry == NULL) {
2503 return (tclOSAScript *) Tcl_GetHashValue(hashEntry);
2507 *----------------------------------------------------------------------
2509 * tclOSADeleteScript --
2511 * This deletes the script given by scriptName.
2514 * A standard Tcl result
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.
2522 *----------------------------------------------------------------------
2527 tclOSAComponent *theComponent,
2528 CONST char *scriptName,
2531 Tcl_HashEntry *hashEntry;
2532 tclOSAScript *scriptPtr;
2534 hashEntry = Tcl_FindHashEntry(&theComponent->scriptTable, scriptName);
2535 if (hashEntry == NULL) {
2536 errMsg = ckalloc(17);
2537 strcpy(errMsg,"Script not found");
2541 scriptPtr = (tclOSAScript *) Tcl_GetHashValue(hashEntry);
2542 OSADispose(theComponent->theComponent, scriptPtr->scriptID);
2543 ckfree((char *) scriptPtr);
2544 Tcl_DeleteHashEntry(hashEntry);
2549 *----------------------------------------------------------------------
2551 * TclOSAActiveProc --
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-.
2560 * A standard MacOS system error
2563 * Any Tcl code may run while calling Tcl_DoOneEvent.
2565 *----------------------------------------------------------------------
2572 tclOSAComponent *theComponent = (tclOSAComponent *) refCon;
2574 Tcl_DoOneEvent(TCL_DONT_WAIT);
2575 InvokeOSAActiveUPP(theComponent->defRefCon, theComponent->defActiveProc);
2581 *----------------------------------------------------------------------
2583 * ASCIICompareProc --
2585 * Trivial ascii compare for use with qsort.
2588 * strcmp of the two input strings
2593 *----------------------------------------------------------------------
2596 ASCIICompareProc(const void *first,const void *second)
2600 char *firstString = *((char **) first);
2601 char *secondString = *((char **) second);
2603 order = strcmp(firstString, secondString);
2608 #define REALLOC_INCR 30
2610 *----------------------------------------------------------------------
2612 * getSortedHashKeys --
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.
2622 * ReInitializes the DString theResult, then copies the names of
2623 * the matching keys into the string as list elements.
2625 *----------------------------------------------------------------------
2630 Tcl_HashTable *theTable,
2631 CONST char *pattern,
2632 Tcl_DString *theResult)
2634 Tcl_HashSearch search;
2635 Tcl_HashEntry *hPtr;
2636 Boolean compare = true;
2638 static char **resultArgv = NULL;
2639 static int totSize = 0;
2642 if (pattern == NULL || *pattern == '\0' ||
2643 (*pattern == '*' && *(pattern + 1) == '\0')) {
2647 for (hPtr = Tcl_FirstHashEntry(theTable,&search), totElem = 0;
2648 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
2650 keyPtr = (char *) Tcl_GetHashKey(theTable, hPtr);
2651 if (!compare || Tcl_StringMatch(keyPtr, pattern)) {
2653 if (totElem >= totSize) {
2654 totSize += REALLOC_INCR;
2655 resultArgv = (char **) ckrealloc((char *) resultArgv,
2656 totSize * sizeof(char *));
2658 resultArgv[totElem - 1] = keyPtr;
2662 Tcl_DStringInit(theResult);
2664 Tcl_DStringAppendElement(theResult, resultArgv[0]);
2665 } else if (totElem > 1) {
2666 qsort((VOID *) resultArgv, (size_t) totElem, sizeof (char *),
2669 for (i = 0; i < totElem; i++) {
2670 Tcl_DStringAppendElement(theResult, resultArgv[i]);
2676 *----------------------------------------------------------------------
2678 * prepareScriptData --
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.
2686 * Standard Tcl result
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.
2693 *----------------------------------------------------------------------
2700 Tcl_DString *scrptData,
2706 OSErr sysErr = noErr;
2707 Tcl_DString encodedText;
2709 Tcl_DStringInit(scrptData);
2711 for (i = 0; i < argc; i++) {
2712 Tcl_DStringAppend(scrptData, argv[i], -1);
2713 Tcl_DStringAppend(scrptData, " ", 1);
2717 * First replace the \n's with \r's in the script argument
2718 * Also replace "\\n" with " ".
2721 for (ptr = scrptData->string; *ptr != '\0'; ptr++) {
2724 } else if (*ptr == '\\') {
2725 if (*(ptr + 1) == '\n') {
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);
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);
2751 *----------------------------------------------------------------------
2753 * tclOSAResultFromID --
2755 * Gets a human readable version of the result from the script ID
2756 * and returns it in the result of the interpreter interp
2762 * Sets the result of interp to the human readable version of resultID.
2765 *----------------------------------------------------------------------
2771 ComponentInstance theComponent,
2774 OSErr myErr = noErr;
2776 Tcl_DString resultStr;
2778 Tcl_DStringInit(&resultStr);
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);
2788 *----------------------------------------------------------------------
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.
2801 * Sets the result of interp to error, plus the relevant portion
2804 *----------------------------------------------------------------------
2809 Tcl_Interp * interp,
2810 ComponentInstance theComponent,
2813 OSErr myErr = noErr;
2814 AEDesc errResult,errLimits;
2816 DescType returnType;
2818 short srcStart,srcEnd;
2821 Tcl_DStringInit(&errStr);
2822 Tcl_DStringAppend(&errStr, "An AppleScript error was encountered.\n", -1);
2824 OSAScriptError(theComponent, kOSAErrorNumber,
2825 typeShortInteger, &errResult);
2827 sprintf(buffer, "Error #%-6.6d\n", (short int) **errResult.dataHandle);
2829 AEDisposeDesc(&errResult);
2831 Tcl_DStringAppend(&errStr,buffer, 15);
2833 OSAScriptError(theComponent, kOSAErrorMessage, typeChar, &errResult);
2834 Tcl_DStringAppend(&errStr, (char *) *errResult.dataHandle,
2835 GetHandleSize(errResult.dataHandle));
2836 AEDisposeDesc(&errResult);
2838 if (scriptData != NULL) {
2841 myErr = OSAScriptError(theComponent, kOSAErrorRange,
2842 typeOSAErrorRange, &errResult);
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);
2853 Tcl_DStringAppend(&errStr, "\nThe offending bit of code was:\n\t", -1);
2855 * Get the full line on which the error occured:
2857 for (lowerB = srcStart; lowerB > 0; lowerB--) {
2858 if (*(scriptData + lowerB ) == '\r') {
2864 for (upperB = srcEnd; *(scriptData + upperB) != '\0'; upperB++) {
2865 if (*(scriptData + upperB) == '\r') {
2870 Tcl_DStringAppend(&errStr, scriptData+lowerB, srcStart - lowerB);
2871 Tcl_DStringAppend(&errStr, "_", 1);
2872 Tcl_DStringAppend(&errStr, scriptData+srcStart, upperB - srcStart);
2875 Tcl_DStringResult(interp,&errStr);
2879 *----------------------------------------------------------------------
2881 * GetRawDataFromDescriptor --
2883 * Get the data from a descriptor.
2891 *----------------------------------------------------------------------
2895 GetRawDataFromDescriptor(
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);
2916 *----------------------------------------------------------------------
2918 * GetRawDataFromDescriptor --
2920 * Get the data from a descriptor. Assume it's a C string.
2928 *----------------------------------------------------------------------
2932 GetCStringFromDescriptor(
2941 resultDesc.dataHandle = nil;
2943 err = AECoerceDesc(sourceDesc, typeChar, &resultDesc);
2946 GetRawDataFromDescriptor(&resultDesc, (Ptr) resultStr,
2947 resultMaxSize - 1, resultSize);
2948 resultStr[*resultSize] = 0;
2950 err = errAECoercionFail;
2953 if (resultDesc.dataHandle) {
2954 AEDisposeDesc(&resultDesc);