1 | /* |
---|
2 | * tclNamesp.c -- |
---|
3 | * |
---|
4 | * Contains support for namespaces, which provide a separate context of |
---|
5 | * commands and global variables. The global :: namespace is the |
---|
6 | * traditional Tcl "global" scope. Other namespaces are created as |
---|
7 | * children of the global namespace. These other namespaces contain |
---|
8 | * special-purpose commands and variables for packages. Also includes the |
---|
9 | * TIP#112 ensemble machinery. |
---|
10 | * |
---|
11 | * Copyright (c) 1993-1997 Lucent Technologies. |
---|
12 | * Copyright (c) 1997 Sun Microsystems, Inc. |
---|
13 | * Copyright (c) 1998-1999 by Scriptics Corporation. |
---|
14 | * Copyright (c) 2002-2005 Donal K. Fellows. |
---|
15 | * Copyright (c) 2006 Neil Madden. |
---|
16 | * Contributions from Don Porter, NIST, 2007. (not subject to US copyright) |
---|
17 | * |
---|
18 | * Originally implemented by |
---|
19 | * Michael J. McLennan |
---|
20 | * Bell Labs Innovations for Lucent Technologies |
---|
21 | * mmclennan@lucent.com |
---|
22 | * |
---|
23 | * See the file "license.terms" for information on usage and redistribution of |
---|
24 | * this file, and for a DISCLAIMER OF ALL WARRANTIES. |
---|
25 | * |
---|
26 | * RCS: @(#) $Id: tclNamesp.c,v 1.162 2008/03/02 18:46:39 msofer Exp $ |
---|
27 | */ |
---|
28 | |
---|
29 | #include "tclInt.h" |
---|
30 | |
---|
31 | /* |
---|
32 | * Thread-local storage used to avoid having a global lock on data that is not |
---|
33 | * limited to a single interpreter. |
---|
34 | */ |
---|
35 | |
---|
36 | typedef struct ThreadSpecificData { |
---|
37 | long numNsCreated; /* Count of the number of namespaces created |
---|
38 | * within the thread. This value is used as a |
---|
39 | * unique id for each namespace. Cannot be |
---|
40 | * per-interp because the nsId is used to |
---|
41 | * distinguish objects which can be passed |
---|
42 | * around between interps in the same thread, |
---|
43 | * but does not need to be global because |
---|
44 | * object internal reps are always per-thread |
---|
45 | * anyway. */ |
---|
46 | } ThreadSpecificData; |
---|
47 | |
---|
48 | static Tcl_ThreadDataKey dataKey; |
---|
49 | |
---|
50 | /* |
---|
51 | * This structure contains a cached pointer to a namespace that is the result |
---|
52 | * of resolving the namespace's name in some other namespace. It is the |
---|
53 | * internal representation for a nsName object. It contains the pointer along |
---|
54 | * with some information that is used to check the cached pointer's validity. |
---|
55 | */ |
---|
56 | |
---|
57 | typedef struct ResolvedNsName { |
---|
58 | Namespace *nsPtr; /* A cached pointer to the Namespace that the |
---|
59 | * name resolved to. */ |
---|
60 | Namespace *refNsPtr; /* Points to the namespace context in which the |
---|
61 | * name was resolved. NULL if the name is fully |
---|
62 | * qualified and thus the resolution does not |
---|
63 | * depend on the context. */ |
---|
64 | int refCount; /* Reference count: 1 for each nsName object |
---|
65 | * that has a pointer to this ResolvedNsName |
---|
66 | * structure as its internal rep. This |
---|
67 | * structure can be freed when refCount |
---|
68 | * becomes zero. */ |
---|
69 | } ResolvedNsName; |
---|
70 | |
---|
71 | /* |
---|
72 | * The client data for an ensemble command. This consists of the table of |
---|
73 | * commands that are actually exported by the namespace, and an epoch counter |
---|
74 | * that, combined with the exportLookupEpoch field of the namespace structure, |
---|
75 | * defines whether the table contains valid data or will need to be recomputed |
---|
76 | * next time the ensemble command is called. |
---|
77 | */ |
---|
78 | |
---|
79 | typedef struct EnsembleConfig { |
---|
80 | Namespace *nsPtr; /* The namspace backing this ensemble up. */ |
---|
81 | Tcl_Command token; /* The token for the command that provides |
---|
82 | * ensemble support for the namespace, or NULL |
---|
83 | * if the command has been deleted (or never |
---|
84 | * existed; the global namespace never has an |
---|
85 | * ensemble command.) */ |
---|
86 | int epoch; /* The epoch at which this ensemble's table of |
---|
87 | * exported commands is valid. */ |
---|
88 | char **subcommandArrayPtr; /* Array of ensemble subcommand names. At all |
---|
89 | * consistent points, this will have the same |
---|
90 | * number of entries as there are entries in |
---|
91 | * the subcommandTable hash. */ |
---|
92 | Tcl_HashTable subcommandTable; |
---|
93 | /* Hash table of ensemble subcommand names, |
---|
94 | * which are its keys so this also provides |
---|
95 | * the storage management for those subcommand |
---|
96 | * names. The contents of the entry values are |
---|
97 | * object version the prefix lists to use when |
---|
98 | * substituting for the command/subcommand to |
---|
99 | * build the ensemble implementation command. |
---|
100 | * Has to be stored here as well as in |
---|
101 | * subcommandDict because that field is NULL |
---|
102 | * when we are deriving the ensemble from the |
---|
103 | * namespace exports list. FUTURE WORK: use |
---|
104 | * object hash table here. */ |
---|
105 | struct EnsembleConfig *next;/* The next ensemble in the linked list of |
---|
106 | * ensembles associated with a namespace. If |
---|
107 | * this field points to this ensemble, the |
---|
108 | * structure has already been unlinked from |
---|
109 | * all lists, and cannot be found by scanning |
---|
110 | * the list from the namespace's ensemble |
---|
111 | * field. */ |
---|
112 | int flags; /* ORed combo of TCL_ENSEMBLE_PREFIX, ENS_DEAD |
---|
113 | * and ENSEMBLE_COMPILE. */ |
---|
114 | |
---|
115 | /* OBJECT FIELDS FOR ENSEMBLE CONFIGURATION */ |
---|
116 | |
---|
117 | Tcl_Obj *subcommandDict; /* Dictionary providing mapping from |
---|
118 | * subcommands to their implementing command |
---|
119 | * prefixes, or NULL if we are to build the |
---|
120 | * map automatically from the namespace |
---|
121 | * exports. */ |
---|
122 | Tcl_Obj *subcmdList; /* List of commands that this ensemble |
---|
123 | * actually provides, and whose implementation |
---|
124 | * will be built using the subcommandDict (if |
---|
125 | * present and defined) and by simple mapping |
---|
126 | * to the namespace otherwise. If NULL, |
---|
127 | * indicates that we are using the (dynamic) |
---|
128 | * list of currently exported commands. */ |
---|
129 | Tcl_Obj *unknownHandler; /* Script prefix used to handle the case when |
---|
130 | * no match is found (according to the rule |
---|
131 | * defined by flag bit TCL_ENSEMBLE_PREFIX) or |
---|
132 | * NULL to use the default error-generating |
---|
133 | * behaviour. The script execution gets all |
---|
134 | * the arguments to the ensemble command |
---|
135 | * (including objv[0]) and will have the |
---|
136 | * results passed directly back to the caller |
---|
137 | * (including the error code) unless the code |
---|
138 | * is TCL_CONTINUE in which case the |
---|
139 | * subcommand will be reparsed by the ensemble |
---|
140 | * core, presumably because the ensemble |
---|
141 | * itself has been updated. */ |
---|
142 | } EnsembleConfig; |
---|
143 | |
---|
144 | #define ENS_DEAD 0x1 /* Flag value to say that the ensemble is dead |
---|
145 | * and on its way out. */ |
---|
146 | |
---|
147 | /* |
---|
148 | * Declarations for functions local to this file: |
---|
149 | */ |
---|
150 | |
---|
151 | static void DeleteImportedCmd(ClientData clientData); |
---|
152 | static int DoImport(Tcl_Interp *interp, |
---|
153 | Namespace *nsPtr, Tcl_HashEntry *hPtr, |
---|
154 | const char *cmdName, const char *pattern, |
---|
155 | Namespace *importNsPtr, int allowOverwrite); |
---|
156 | static void DupNsNameInternalRep(Tcl_Obj *objPtr,Tcl_Obj *copyPtr); |
---|
157 | static char * ErrorCodeRead(ClientData clientData,Tcl_Interp *interp, |
---|
158 | const char *name1, const char *name2, int flags); |
---|
159 | static char * ErrorInfoRead(ClientData clientData,Tcl_Interp *interp, |
---|
160 | const char *name1, const char *name2, int flags); |
---|
161 | static char * EstablishErrorCodeTraces(ClientData clientData, |
---|
162 | Tcl_Interp *interp, const char *name1, |
---|
163 | const char *name2, int flags); |
---|
164 | static char * EstablishErrorInfoTraces(ClientData clientData, |
---|
165 | Tcl_Interp *interp, const char *name1, |
---|
166 | const char *name2, int flags); |
---|
167 | static void FreeNsNameInternalRep(Tcl_Obj *objPtr); |
---|
168 | static int GetNamespaceFromObj(Tcl_Interp *interp, |
---|
169 | Tcl_Obj *objPtr, Tcl_Namespace **nsPtrPtr); |
---|
170 | static int InvokeImportedCmd(ClientData clientData, |
---|
171 | Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); |
---|
172 | static int NamespaceChildrenCmd(ClientData dummy, |
---|
173 | Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); |
---|
174 | static int NamespaceCodeCmd(ClientData dummy, Tcl_Interp *interp, |
---|
175 | int objc, Tcl_Obj *const objv[]); |
---|
176 | static int NamespaceCurrentCmd(ClientData dummy, |
---|
177 | Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); |
---|
178 | static int NamespaceDeleteCmd(ClientData dummy,Tcl_Interp *interp, |
---|
179 | int objc, Tcl_Obj *const objv[]); |
---|
180 | static int NamespaceEnsembleCmd(ClientData dummy, |
---|
181 | Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); |
---|
182 | static int NamespaceEvalCmd(ClientData dummy, Tcl_Interp *interp, |
---|
183 | int objc, Tcl_Obj *const objv[]); |
---|
184 | static int NamespaceExistsCmd(ClientData dummy,Tcl_Interp *interp, |
---|
185 | int objc, Tcl_Obj *const objv[]); |
---|
186 | static int NamespaceExportCmd(ClientData dummy,Tcl_Interp *interp, |
---|
187 | int objc, Tcl_Obj *const objv[]); |
---|
188 | static int NamespaceForgetCmd(ClientData dummy,Tcl_Interp *interp, |
---|
189 | int objc, Tcl_Obj *const objv[]); |
---|
190 | static void NamespaceFree(Namespace *nsPtr); |
---|
191 | static int NamespaceImportCmd(ClientData dummy,Tcl_Interp *interp, |
---|
192 | int objc, Tcl_Obj *const objv[]); |
---|
193 | static int NamespaceInscopeCmd(ClientData dummy, |
---|
194 | Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); |
---|
195 | static int NamespaceOriginCmd(ClientData dummy,Tcl_Interp *interp, |
---|
196 | int objc, Tcl_Obj *const objv[]); |
---|
197 | static int NamespaceParentCmd(ClientData dummy,Tcl_Interp *interp, |
---|
198 | int objc, Tcl_Obj *const objv[]); |
---|
199 | static int NamespacePathCmd(ClientData dummy, Tcl_Interp *interp, |
---|
200 | int objc, Tcl_Obj *const objv[]); |
---|
201 | static int NamespaceQualifiersCmd(ClientData dummy, |
---|
202 | Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); |
---|
203 | static int NamespaceTailCmd(ClientData dummy, Tcl_Interp *interp, |
---|
204 | int objc, Tcl_Obj *const objv[]); |
---|
205 | static int NamespaceUpvarCmd(ClientData dummy, Tcl_Interp *interp, |
---|
206 | int objc, Tcl_Obj *const objv[]); |
---|
207 | static int NamespaceUnknownCmd(ClientData dummy, |
---|
208 | Tcl_Interp *interp, int objc, |
---|
209 | Tcl_Obj *const objv[]); |
---|
210 | static int NamespaceWhichCmd(ClientData dummy, Tcl_Interp *interp, |
---|
211 | int objc, Tcl_Obj *const objv[]); |
---|
212 | static int SetNsNameFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); |
---|
213 | static int NsEnsembleImplementationCmd(ClientData clientData, |
---|
214 | Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); |
---|
215 | static void BuildEnsembleConfig(EnsembleConfig *ensemblePtr); |
---|
216 | static int NsEnsembleStringOrder(const void *strPtr1, |
---|
217 | const void *strPtr2); |
---|
218 | static void DeleteEnsembleConfig(ClientData clientData); |
---|
219 | static void MakeCachedEnsembleCommand(Tcl_Obj *objPtr, |
---|
220 | EnsembleConfig *ensemblePtr, |
---|
221 | const char *subcmdName, Tcl_Obj *prefixObjPtr); |
---|
222 | static void FreeEnsembleCmdRep(Tcl_Obj *objPtr); |
---|
223 | static void DupEnsembleCmdRep(Tcl_Obj *objPtr, Tcl_Obj *copyPtr); |
---|
224 | static void StringOfEnsembleCmdRep(Tcl_Obj *objPtr); |
---|
225 | static void UnlinkNsPath(Namespace *nsPtr); |
---|
226 | |
---|
227 | /* |
---|
228 | * This structure defines a Tcl object type that contains a namespace |
---|
229 | * reference. It is used in commands that take the name of a namespace as an |
---|
230 | * argument. The namespace reference is resolved, and the result in cached in |
---|
231 | * the object. |
---|
232 | */ |
---|
233 | |
---|
234 | static Tcl_ObjType nsNameType = { |
---|
235 | "nsName", /* the type's name */ |
---|
236 | FreeNsNameInternalRep, /* freeIntRepProc */ |
---|
237 | DupNsNameInternalRep, /* dupIntRepProc */ |
---|
238 | NULL, /* updateStringProc */ |
---|
239 | SetNsNameFromAny /* setFromAnyProc */ |
---|
240 | }; |
---|
241 | |
---|
242 | /* |
---|
243 | * This structure defines a Tcl object type that contains a reference to an |
---|
244 | * ensemble subcommand (e.g. the "length" in [string length ab]). It is used |
---|
245 | * to cache the mapping between the subcommand itself and the real command |
---|
246 | * that implements it. |
---|
247 | */ |
---|
248 | |
---|
249 | Tcl_ObjType tclEnsembleCmdType = { |
---|
250 | "ensembleCommand", /* the type's name */ |
---|
251 | FreeEnsembleCmdRep, /* freeIntRepProc */ |
---|
252 | DupEnsembleCmdRep, /* dupIntRepProc */ |
---|
253 | StringOfEnsembleCmdRep, /* updateStringProc */ |
---|
254 | NULL /* setFromAnyProc */ |
---|
255 | }; |
---|
256 | |
---|
257 | /* |
---|
258 | *---------------------------------------------------------------------- |
---|
259 | * |
---|
260 | * TclInitNamespaceSubsystem -- |
---|
261 | * |
---|
262 | * This function is called to initialize all the structures that are used |
---|
263 | * by namespaces on a per-process basis. |
---|
264 | * |
---|
265 | * Results: |
---|
266 | * None. |
---|
267 | * |
---|
268 | * Side effects: |
---|
269 | * None. |
---|
270 | * |
---|
271 | *---------------------------------------------------------------------- |
---|
272 | */ |
---|
273 | |
---|
274 | void |
---|
275 | TclInitNamespaceSubsystem(void) |
---|
276 | { |
---|
277 | /* |
---|
278 | * Does nothing for now. |
---|
279 | */ |
---|
280 | } |
---|
281 | |
---|
282 | /* |
---|
283 | *---------------------------------------------------------------------- |
---|
284 | * |
---|
285 | * Tcl_GetCurrentNamespace -- |
---|
286 | * |
---|
287 | * Returns a pointer to an interpreter's currently active namespace. |
---|
288 | * |
---|
289 | * Results: |
---|
290 | * Returns a pointer to the interpreter's current namespace. |
---|
291 | * |
---|
292 | * Side effects: |
---|
293 | * None. |
---|
294 | * |
---|
295 | *---------------------------------------------------------------------- |
---|
296 | */ |
---|
297 | |
---|
298 | Tcl_Namespace * |
---|
299 | Tcl_GetCurrentNamespace( |
---|
300 | register Tcl_Interp *interp)/* Interpreter whose current namespace is |
---|
301 | * being queried. */ |
---|
302 | { |
---|
303 | return TclGetCurrentNamespace(interp); |
---|
304 | } |
---|
305 | |
---|
306 | /* |
---|
307 | *---------------------------------------------------------------------- |
---|
308 | * |
---|
309 | * Tcl_GetGlobalNamespace -- |
---|
310 | * |
---|
311 | * Returns a pointer to an interpreter's global :: namespace. |
---|
312 | * |
---|
313 | * Results: |
---|
314 | * Returns a pointer to the specified interpreter's global namespace. |
---|
315 | * |
---|
316 | * Side effects: |
---|
317 | * None. |
---|
318 | * |
---|
319 | *---------------------------------------------------------------------- |
---|
320 | */ |
---|
321 | |
---|
322 | Tcl_Namespace * |
---|
323 | Tcl_GetGlobalNamespace( |
---|
324 | register Tcl_Interp *interp)/* Interpreter whose global namespace should |
---|
325 | * be returned. */ |
---|
326 | { |
---|
327 | return TclGetGlobalNamespace(interp); |
---|
328 | } |
---|
329 | |
---|
330 | /* |
---|
331 | *---------------------------------------------------------------------- |
---|
332 | * |
---|
333 | * Tcl_PushCallFrame -- |
---|
334 | * |
---|
335 | * Pushes a new call frame onto the interpreter's Tcl call stack. Called |
---|
336 | * when executing a Tcl procedure or a "namespace eval" or "namespace |
---|
337 | * inscope" command. |
---|
338 | * |
---|
339 | * Results: |
---|
340 | * Returns TCL_OK if successful, or TCL_ERROR (along with an error |
---|
341 | * message in the interpreter's result object) if something goes wrong. |
---|
342 | * |
---|
343 | * Side effects: |
---|
344 | * Modifies the interpreter's Tcl call stack. |
---|
345 | * |
---|
346 | *---------------------------------------------------------------------- |
---|
347 | */ |
---|
348 | |
---|
349 | int |
---|
350 | Tcl_PushCallFrame( |
---|
351 | Tcl_Interp *interp, /* Interpreter in which the new call frame is |
---|
352 | * to be pushed. */ |
---|
353 | Tcl_CallFrame *callFramePtr,/* Points to a call frame structure to push. |
---|
354 | * Storage for this has already been allocated |
---|
355 | * by the caller; typically this is the |
---|
356 | * address of a CallFrame structure allocated |
---|
357 | * on the caller's C stack. The call frame |
---|
358 | * will be initialized by this function. The |
---|
359 | * caller can pop the frame later with |
---|
360 | * Tcl_PopCallFrame, and it is responsible for |
---|
361 | * freeing the frame's storage. */ |
---|
362 | Tcl_Namespace *namespacePtr,/* Points to the namespace in which the frame |
---|
363 | * will execute. If NULL, the interpreter's |
---|
364 | * current namespace will be used. */ |
---|
365 | int isProcCallFrame) /* If nonzero, the frame represents a called |
---|
366 | * Tcl procedure and may have local vars. Vars |
---|
367 | * will ordinarily be looked up in the frame. |
---|
368 | * If new variables are created, they will be |
---|
369 | * created in the frame. If 0, the frame is |
---|
370 | * for a "namespace eval" or "namespace |
---|
371 | * inscope" command and var references are |
---|
372 | * treated as references to namespace |
---|
373 | * variables. */ |
---|
374 | { |
---|
375 | Interp *iPtr = (Interp *) interp; |
---|
376 | register CallFrame *framePtr = (CallFrame *) callFramePtr; |
---|
377 | register Namespace *nsPtr; |
---|
378 | |
---|
379 | if (namespacePtr == NULL) { |
---|
380 | nsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
381 | } else { |
---|
382 | nsPtr = (Namespace *) namespacePtr; |
---|
383 | |
---|
384 | /* |
---|
385 | * TODO: Examine whether it would be better to guard based on NS_DYING |
---|
386 | * or NS_KILLED. It appears that these are not tested because they can |
---|
387 | * be set in a global interp that has been [namespace delete]d, but |
---|
388 | * which never really completely goes away because of lingering global |
---|
389 | * things like ::errorInfo and [::unknown] and hidden commands. |
---|
390 | * Review of those designs might permit stricter checking here. |
---|
391 | */ |
---|
392 | |
---|
393 | if (nsPtr->flags & NS_DEAD) { |
---|
394 | Tcl_Panic("Trying to push call frame for dead namespace"); |
---|
395 | /*NOTREACHED*/ |
---|
396 | } |
---|
397 | } |
---|
398 | |
---|
399 | nsPtr->activationCount++; |
---|
400 | framePtr->nsPtr = nsPtr; |
---|
401 | framePtr->isProcCallFrame = isProcCallFrame; |
---|
402 | framePtr->objc = 0; |
---|
403 | framePtr->objv = NULL; |
---|
404 | framePtr->callerPtr = iPtr->framePtr; |
---|
405 | framePtr->callerVarPtr = iPtr->varFramePtr; |
---|
406 | if (iPtr->varFramePtr != NULL) { |
---|
407 | framePtr->level = (iPtr->varFramePtr->level + 1); |
---|
408 | } else { |
---|
409 | framePtr->level = 0; |
---|
410 | } |
---|
411 | framePtr->procPtr = NULL; /* no called procedure */ |
---|
412 | framePtr->varTablePtr = NULL; /* and no local variables */ |
---|
413 | framePtr->numCompiledLocals = 0; |
---|
414 | framePtr->compiledLocals = NULL; |
---|
415 | framePtr->clientData = NULL; |
---|
416 | framePtr->localCachePtr = NULL; |
---|
417 | |
---|
418 | /* |
---|
419 | * Push the new call frame onto the interpreter's stack of procedure call |
---|
420 | * frames making it the current frame. |
---|
421 | */ |
---|
422 | |
---|
423 | iPtr->framePtr = framePtr; |
---|
424 | iPtr->varFramePtr = framePtr; |
---|
425 | return TCL_OK; |
---|
426 | } |
---|
427 | |
---|
428 | /* |
---|
429 | *---------------------------------------------------------------------- |
---|
430 | * |
---|
431 | * Tcl_PopCallFrame -- |
---|
432 | * |
---|
433 | * Removes a call frame from the Tcl call stack for the interpreter. |
---|
434 | * Called to remove a frame previously pushed by Tcl_PushCallFrame. |
---|
435 | * |
---|
436 | * Results: |
---|
437 | * None. |
---|
438 | * |
---|
439 | * Side effects: |
---|
440 | * Modifies the call stack of the interpreter. Resets various fields of |
---|
441 | * the popped call frame. If a namespace has been deleted and has no more |
---|
442 | * activations on the call stack, the namespace is destroyed. |
---|
443 | * |
---|
444 | *---------------------------------------------------------------------- |
---|
445 | */ |
---|
446 | |
---|
447 | void |
---|
448 | Tcl_PopCallFrame( |
---|
449 | Tcl_Interp *interp) /* Interpreter with call frame to pop. */ |
---|
450 | { |
---|
451 | register Interp *iPtr = (Interp *) interp; |
---|
452 | register CallFrame *framePtr = iPtr->framePtr; |
---|
453 | Namespace *nsPtr; |
---|
454 | |
---|
455 | /* |
---|
456 | * It's important to remove the call frame from the interpreter's stack of |
---|
457 | * call frames before deleting local variables, so that traces invoked by |
---|
458 | * the variable deletion don't see the partially-deleted frame. |
---|
459 | */ |
---|
460 | |
---|
461 | if (framePtr->callerPtr) { |
---|
462 | iPtr->framePtr = framePtr->callerPtr; |
---|
463 | iPtr->varFramePtr = framePtr->callerVarPtr; |
---|
464 | } else { |
---|
465 | /* Tcl_PopCallFrame: trying to pop rootCallFrame! */ |
---|
466 | } |
---|
467 | |
---|
468 | if (framePtr->varTablePtr != NULL) { |
---|
469 | TclDeleteVars(iPtr, framePtr->varTablePtr); |
---|
470 | ckfree((char *) framePtr->varTablePtr); |
---|
471 | framePtr->varTablePtr = NULL; |
---|
472 | } |
---|
473 | if (framePtr->numCompiledLocals > 0) { |
---|
474 | TclDeleteCompiledLocalVars(iPtr, framePtr); |
---|
475 | if (--framePtr->localCachePtr->refCount == 0) { |
---|
476 | TclFreeLocalCache(interp, framePtr->localCachePtr); |
---|
477 | } |
---|
478 | framePtr->localCachePtr = NULL; |
---|
479 | } |
---|
480 | |
---|
481 | /* |
---|
482 | * Decrement the namespace's count of active call frames. If the namespace |
---|
483 | * is "dying" and there are no more active call frames, call |
---|
484 | * Tcl_DeleteNamespace to destroy it. |
---|
485 | */ |
---|
486 | |
---|
487 | nsPtr = framePtr->nsPtr; |
---|
488 | nsPtr->activationCount--; |
---|
489 | if ((nsPtr->flags & NS_DYING) |
---|
490 | && (nsPtr->activationCount - (nsPtr == iPtr->globalNsPtr) == 0)) { |
---|
491 | Tcl_DeleteNamespace((Tcl_Namespace *) nsPtr); |
---|
492 | } |
---|
493 | framePtr->nsPtr = NULL; |
---|
494 | } |
---|
495 | |
---|
496 | /* |
---|
497 | *---------------------------------------------------------------------- |
---|
498 | * |
---|
499 | * TclPushStackFrame -- |
---|
500 | * |
---|
501 | * Allocates a new call frame in the interpreter's execution stack, then |
---|
502 | * pushes it onto the interpreter's Tcl call stack. Called when executing |
---|
503 | * a Tcl procedure or a "namespace eval" or "namespace inscope" command. |
---|
504 | * |
---|
505 | * Results: |
---|
506 | * Returns TCL_OK if successful, or TCL_ERROR (along with an error |
---|
507 | * message in the interpreter's result object) if something goes wrong. |
---|
508 | * |
---|
509 | * Side effects: |
---|
510 | * Modifies the interpreter's Tcl call stack. |
---|
511 | * |
---|
512 | *---------------------------------------------------------------------- |
---|
513 | */ |
---|
514 | |
---|
515 | int |
---|
516 | TclPushStackFrame( |
---|
517 | Tcl_Interp *interp, /* Interpreter in which the new call frame is |
---|
518 | * to be pushed. */ |
---|
519 | Tcl_CallFrame **framePtrPtr,/* Place to store a pointer to the stack |
---|
520 | * allocated call frame. */ |
---|
521 | Tcl_Namespace *namespacePtr,/* Points to the namespace in which the frame |
---|
522 | * will execute. If NULL, the interpreter's |
---|
523 | * current namespace will be used. */ |
---|
524 | int isProcCallFrame) /* If nonzero, the frame represents a called |
---|
525 | * Tcl procedure and may have local vars. Vars |
---|
526 | * will ordinarily be looked up in the frame. |
---|
527 | * If new variables are created, they will be |
---|
528 | * created in the frame. If 0, the frame is |
---|
529 | * for a "namespace eval" or "namespace |
---|
530 | * inscope" command and var references are |
---|
531 | * treated as references to namespace |
---|
532 | * variables. */ |
---|
533 | { |
---|
534 | *framePtrPtr = (Tcl_CallFrame *) TclStackAlloc(interp, sizeof(CallFrame)); |
---|
535 | return Tcl_PushCallFrame(interp, *framePtrPtr, namespacePtr, |
---|
536 | isProcCallFrame); |
---|
537 | } |
---|
538 | |
---|
539 | void |
---|
540 | TclPopStackFrame( |
---|
541 | Tcl_Interp *interp) /* Interpreter with call frame to pop. */ |
---|
542 | { |
---|
543 | CallFrame *freePtr = ((Interp *)interp)->framePtr; |
---|
544 | |
---|
545 | Tcl_PopCallFrame(interp); |
---|
546 | TclStackFree(interp, freePtr); |
---|
547 | } |
---|
548 | |
---|
549 | /* |
---|
550 | *---------------------------------------------------------------------- |
---|
551 | * |
---|
552 | * EstablishErrorCodeTraces -- |
---|
553 | * |
---|
554 | * Creates traces on the ::errorCode variable to keep its value |
---|
555 | * consistent with the expectations of legacy code. |
---|
556 | * |
---|
557 | * Results: |
---|
558 | * None. |
---|
559 | * |
---|
560 | * Side effects: |
---|
561 | * Read and unset traces are established on ::errorCode. |
---|
562 | * |
---|
563 | *---------------------------------------------------------------------- |
---|
564 | */ |
---|
565 | |
---|
566 | static char * |
---|
567 | EstablishErrorCodeTraces( |
---|
568 | ClientData clientData, |
---|
569 | Tcl_Interp *interp, |
---|
570 | const char *name1, |
---|
571 | const char *name2, |
---|
572 | int flags) |
---|
573 | { |
---|
574 | Tcl_TraceVar(interp, "errorCode", TCL_GLOBAL_ONLY | TCL_TRACE_READS, |
---|
575 | ErrorCodeRead, NULL); |
---|
576 | Tcl_TraceVar(interp, "errorCode", TCL_GLOBAL_ONLY | TCL_TRACE_UNSETS, |
---|
577 | EstablishErrorCodeTraces, NULL); |
---|
578 | return NULL; |
---|
579 | } |
---|
580 | |
---|
581 | /* |
---|
582 | *---------------------------------------------------------------------- |
---|
583 | * |
---|
584 | * ErrorCodeRead -- |
---|
585 | * |
---|
586 | * Called when the ::errorCode variable is read. Copies the current value |
---|
587 | * of the interp's errorCode field into ::errorCode. |
---|
588 | * |
---|
589 | * Results: |
---|
590 | * None. |
---|
591 | * |
---|
592 | * Side effects: |
---|
593 | * None. |
---|
594 | * |
---|
595 | *---------------------------------------------------------------------- |
---|
596 | */ |
---|
597 | |
---|
598 | static char * |
---|
599 | ErrorCodeRead( |
---|
600 | ClientData clientData, |
---|
601 | Tcl_Interp *interp, |
---|
602 | const char *name1, |
---|
603 | const char *name2, |
---|
604 | int flags) |
---|
605 | { |
---|
606 | Interp *iPtr = (Interp *)interp; |
---|
607 | |
---|
608 | if (Tcl_InterpDeleted(interp) || !(iPtr->flags & ERR_LEGACY_COPY)) { |
---|
609 | return NULL; |
---|
610 | } |
---|
611 | if (iPtr->errorCode) { |
---|
612 | Tcl_ObjSetVar2(interp, iPtr->ecVar, NULL, |
---|
613 | iPtr->errorCode, TCL_GLOBAL_ONLY); |
---|
614 | return NULL; |
---|
615 | } |
---|
616 | if (NULL == Tcl_ObjGetVar2(interp, iPtr->ecVar, NULL, TCL_GLOBAL_ONLY)) { |
---|
617 | Tcl_ObjSetVar2(interp, iPtr->ecVar, NULL, |
---|
618 | Tcl_NewObj(), TCL_GLOBAL_ONLY); |
---|
619 | } |
---|
620 | return NULL; |
---|
621 | } |
---|
622 | |
---|
623 | /* |
---|
624 | *---------------------------------------------------------------------- |
---|
625 | * |
---|
626 | * EstablishErrorInfoTraces -- |
---|
627 | * |
---|
628 | * Creates traces on the ::errorInfo variable to keep its value |
---|
629 | * consistent with the expectations of legacy code. |
---|
630 | * |
---|
631 | * Results: |
---|
632 | * None. |
---|
633 | * |
---|
634 | * Side effects: |
---|
635 | * Read and unset traces are established on ::errorInfo. |
---|
636 | * |
---|
637 | *---------------------------------------------------------------------- |
---|
638 | */ |
---|
639 | |
---|
640 | static char * |
---|
641 | EstablishErrorInfoTraces( |
---|
642 | ClientData clientData, |
---|
643 | Tcl_Interp *interp, |
---|
644 | const char *name1, |
---|
645 | const char *name2, |
---|
646 | int flags) |
---|
647 | { |
---|
648 | Tcl_TraceVar(interp, "errorInfo", TCL_GLOBAL_ONLY | TCL_TRACE_READS, |
---|
649 | ErrorInfoRead, NULL); |
---|
650 | Tcl_TraceVar(interp, "errorInfo", TCL_GLOBAL_ONLY | TCL_TRACE_UNSETS, |
---|
651 | EstablishErrorInfoTraces, NULL); |
---|
652 | return NULL; |
---|
653 | } |
---|
654 | |
---|
655 | /* |
---|
656 | *---------------------------------------------------------------------- |
---|
657 | * |
---|
658 | * ErrorInfoRead -- |
---|
659 | * |
---|
660 | * Called when the ::errorInfo variable is read. Copies the current value |
---|
661 | * of the interp's errorInfo field into ::errorInfo. |
---|
662 | * |
---|
663 | * Results: |
---|
664 | * None. |
---|
665 | * |
---|
666 | * Side effects: |
---|
667 | * None. |
---|
668 | * |
---|
669 | *---------------------------------------------------------------------- |
---|
670 | */ |
---|
671 | |
---|
672 | static char * |
---|
673 | ErrorInfoRead( |
---|
674 | ClientData clientData, |
---|
675 | Tcl_Interp *interp, |
---|
676 | const char *name1, |
---|
677 | const char *name2, |
---|
678 | int flags) |
---|
679 | { |
---|
680 | Interp *iPtr = (Interp *) interp; |
---|
681 | |
---|
682 | if (Tcl_InterpDeleted(interp) || !(iPtr->flags & ERR_LEGACY_COPY)) { |
---|
683 | return NULL; |
---|
684 | } |
---|
685 | if (iPtr->errorInfo) { |
---|
686 | Tcl_ObjSetVar2(interp, iPtr->eiVar, NULL, |
---|
687 | iPtr->errorInfo, TCL_GLOBAL_ONLY); |
---|
688 | return NULL; |
---|
689 | } |
---|
690 | if (NULL == Tcl_ObjGetVar2(interp, iPtr->eiVar, NULL, TCL_GLOBAL_ONLY)) { |
---|
691 | Tcl_ObjSetVar2(interp, iPtr->eiVar, NULL, |
---|
692 | Tcl_NewObj(), TCL_GLOBAL_ONLY); |
---|
693 | } |
---|
694 | return NULL; |
---|
695 | } |
---|
696 | |
---|
697 | /* |
---|
698 | *---------------------------------------------------------------------- |
---|
699 | * |
---|
700 | * Tcl_CreateNamespace -- |
---|
701 | * |
---|
702 | * Creates a new namespace with the given name. If there is no active |
---|
703 | * namespace (i.e., the interpreter is being initialized), the global :: |
---|
704 | * namespace is created and returned. |
---|
705 | * |
---|
706 | * Results: |
---|
707 | * Returns a pointer to the new namespace if successful. If the namespace |
---|
708 | * already exists or if another error occurs, this routine returns NULL, |
---|
709 | * along with an error message in the interpreter's result object. |
---|
710 | * |
---|
711 | * Side effects: |
---|
712 | * If the name contains "::" qualifiers and a parent namespace does not |
---|
713 | * already exist, it is automatically created. |
---|
714 | * |
---|
715 | *---------------------------------------------------------------------- |
---|
716 | */ |
---|
717 | |
---|
718 | Tcl_Namespace * |
---|
719 | Tcl_CreateNamespace( |
---|
720 | Tcl_Interp *interp, /* Interpreter in which a new namespace is |
---|
721 | * being created. Also used for error |
---|
722 | * reporting. */ |
---|
723 | const char *name, /* Name for the new namespace. May be a |
---|
724 | * qualified name with names of ancestor |
---|
725 | * namespaces separated by "::"s. */ |
---|
726 | ClientData clientData, /* One-word value to store with namespace. */ |
---|
727 | Tcl_NamespaceDeleteProc *deleteProc) |
---|
728 | /* Function called to delete client data when |
---|
729 | * the namespace is deleted. NULL if no |
---|
730 | * function should be called. */ |
---|
731 | { |
---|
732 | Interp *iPtr = (Interp *) interp; |
---|
733 | register Namespace *nsPtr, *ancestorPtr; |
---|
734 | Namespace *parentPtr, *dummy1Ptr, *dummy2Ptr; |
---|
735 | Namespace *globalNsPtr = iPtr->globalNsPtr; |
---|
736 | const char *simpleName; |
---|
737 | Tcl_HashEntry *entryPtr; |
---|
738 | Tcl_DString buffer1, buffer2; |
---|
739 | Tcl_DString *namePtr, *buffPtr; |
---|
740 | int newEntry, nameLen; |
---|
741 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
---|
742 | |
---|
743 | /* |
---|
744 | * If there is no active namespace, the interpreter is being initialized. |
---|
745 | */ |
---|
746 | |
---|
747 | if ((globalNsPtr == NULL) && (iPtr->varFramePtr == NULL)) { |
---|
748 | /* |
---|
749 | * Treat this namespace as the global namespace, and avoid looking for |
---|
750 | * a parent. |
---|
751 | */ |
---|
752 | |
---|
753 | parentPtr = NULL; |
---|
754 | simpleName = ""; |
---|
755 | } else if (*name == '\0') { |
---|
756 | Tcl_ResetResult(interp); |
---|
757 | Tcl_AppendResult(interp, "can't create namespace \"\": " |
---|
758 | "only global namespace can have empty name", NULL); |
---|
759 | return NULL; |
---|
760 | } else { |
---|
761 | /* |
---|
762 | * Find the parent for the new namespace. |
---|
763 | */ |
---|
764 | |
---|
765 | TclGetNamespaceForQualName(interp, name, NULL, |
---|
766 | /*flags*/ (TCL_CREATE_NS_IF_UNKNOWN | TCL_LEAVE_ERR_MSG), |
---|
767 | &parentPtr, &dummy1Ptr, &dummy2Ptr, &simpleName); |
---|
768 | |
---|
769 | /* |
---|
770 | * If the unqualified name at the end is empty, there were trailing |
---|
771 | * "::"s after the namespace's name which we ignore. The new namespace |
---|
772 | * was already (recursively) created and is pointed to by parentPtr. |
---|
773 | */ |
---|
774 | |
---|
775 | if (*simpleName == '\0') { |
---|
776 | return (Tcl_Namespace *) parentPtr; |
---|
777 | } |
---|
778 | |
---|
779 | /* |
---|
780 | * Check for a bad namespace name and make sure that the name does not |
---|
781 | * already exist in the parent namespace. |
---|
782 | */ |
---|
783 | |
---|
784 | if (Tcl_FindHashEntry(&parentPtr->childTable, simpleName) != NULL) { |
---|
785 | Tcl_AppendResult(interp, "can't create namespace \"", name, |
---|
786 | "\": already exists", NULL); |
---|
787 | return NULL; |
---|
788 | } |
---|
789 | } |
---|
790 | |
---|
791 | /* |
---|
792 | * Create the new namespace and root it in its parent. Increment the count |
---|
793 | * of namespaces created. |
---|
794 | */ |
---|
795 | |
---|
796 | nsPtr = (Namespace *) ckalloc(sizeof(Namespace)); |
---|
797 | nsPtr->name = ckalloc((unsigned) (strlen(simpleName)+1)); |
---|
798 | strcpy(nsPtr->name, simpleName); |
---|
799 | nsPtr->fullName = NULL; /* Set below. */ |
---|
800 | nsPtr->clientData = clientData; |
---|
801 | nsPtr->deleteProc = deleteProc; |
---|
802 | nsPtr->parentPtr = parentPtr; |
---|
803 | Tcl_InitHashTable(&nsPtr->childTable, TCL_STRING_KEYS); |
---|
804 | nsPtr->nsId = ++(tsdPtr->numNsCreated); |
---|
805 | nsPtr->interp = interp; |
---|
806 | nsPtr->flags = 0; |
---|
807 | nsPtr->activationCount = 0; |
---|
808 | nsPtr->refCount = 0; |
---|
809 | Tcl_InitHashTable(&nsPtr->cmdTable, TCL_STRING_KEYS); |
---|
810 | TclInitVarHashTable(&nsPtr->varTable, nsPtr); |
---|
811 | nsPtr->exportArrayPtr = NULL; |
---|
812 | nsPtr->numExportPatterns = 0; |
---|
813 | nsPtr->maxExportPatterns = 0; |
---|
814 | nsPtr->cmdRefEpoch = 0; |
---|
815 | nsPtr->resolverEpoch = 0; |
---|
816 | nsPtr->cmdResProc = NULL; |
---|
817 | nsPtr->varResProc = NULL; |
---|
818 | nsPtr->compiledVarResProc = NULL; |
---|
819 | nsPtr->exportLookupEpoch = 0; |
---|
820 | nsPtr->ensembles = NULL; |
---|
821 | nsPtr->unknownHandlerPtr = NULL; |
---|
822 | nsPtr->commandPathLength = 0; |
---|
823 | nsPtr->commandPathArray = NULL; |
---|
824 | nsPtr->commandPathSourceList = NULL; |
---|
825 | |
---|
826 | if (parentPtr != NULL) { |
---|
827 | entryPtr = Tcl_CreateHashEntry(&parentPtr->childTable, simpleName, |
---|
828 | &newEntry); |
---|
829 | Tcl_SetHashValue(entryPtr, nsPtr); |
---|
830 | } else { |
---|
831 | /* |
---|
832 | * In the global namespace create traces to maintain the ::errorInfo |
---|
833 | * and ::errorCode variables. |
---|
834 | */ |
---|
835 | |
---|
836 | iPtr->globalNsPtr = nsPtr; |
---|
837 | EstablishErrorInfoTraces(NULL, interp, NULL, NULL, 0); |
---|
838 | EstablishErrorCodeTraces(NULL, interp, NULL, NULL, 0); |
---|
839 | } |
---|
840 | |
---|
841 | /* |
---|
842 | * Build the fully qualified name for this namespace. |
---|
843 | */ |
---|
844 | |
---|
845 | Tcl_DStringInit(&buffer1); |
---|
846 | Tcl_DStringInit(&buffer2); |
---|
847 | namePtr = &buffer1; |
---|
848 | buffPtr = &buffer2; |
---|
849 | for (ancestorPtr = nsPtr; ancestorPtr != NULL; |
---|
850 | ancestorPtr = ancestorPtr->parentPtr) { |
---|
851 | if (ancestorPtr != globalNsPtr) { |
---|
852 | register Tcl_DString *tempPtr = namePtr; |
---|
853 | |
---|
854 | Tcl_DStringAppend(buffPtr, "::", 2); |
---|
855 | Tcl_DStringAppend(buffPtr, ancestorPtr->name, -1); |
---|
856 | Tcl_DStringAppend(buffPtr, Tcl_DStringValue(namePtr), |
---|
857 | Tcl_DStringLength(namePtr)); |
---|
858 | |
---|
859 | /* |
---|
860 | * Clear the unwanted buffer or we end up appending to previous |
---|
861 | * results, making the namespace fullNames of nested namespaces |
---|
862 | * very wrong (and strange). |
---|
863 | */ |
---|
864 | |
---|
865 | Tcl_DStringSetLength(namePtr, 0); |
---|
866 | |
---|
867 | /* |
---|
868 | * Now swap the buffer pointers so that we build in the other |
---|
869 | * buffer. This is faster than repeated copying back and forth |
---|
870 | * between buffers. |
---|
871 | */ |
---|
872 | |
---|
873 | namePtr = buffPtr; |
---|
874 | buffPtr = tempPtr; |
---|
875 | } |
---|
876 | } |
---|
877 | |
---|
878 | name = Tcl_DStringValue(namePtr); |
---|
879 | nameLen = Tcl_DStringLength(namePtr); |
---|
880 | nsPtr->fullName = ckalloc((unsigned) (nameLen+1)); |
---|
881 | memcpy(nsPtr->fullName, name, (unsigned) nameLen + 1); |
---|
882 | |
---|
883 | Tcl_DStringFree(&buffer1); |
---|
884 | Tcl_DStringFree(&buffer2); |
---|
885 | |
---|
886 | /* |
---|
887 | * Return a pointer to the new namespace. |
---|
888 | */ |
---|
889 | |
---|
890 | return (Tcl_Namespace *) nsPtr; |
---|
891 | } |
---|
892 | |
---|
893 | /* |
---|
894 | *---------------------------------------------------------------------- |
---|
895 | * |
---|
896 | * Tcl_DeleteNamespace -- |
---|
897 | * |
---|
898 | * Deletes a namespace and all of the commands, variables, and other |
---|
899 | * namespaces within it. |
---|
900 | * |
---|
901 | * Results: |
---|
902 | * None. |
---|
903 | * |
---|
904 | * Side effects: |
---|
905 | * When a namespace is deleted, it is automatically removed as a child of |
---|
906 | * its parent namespace. Also, all its commands, variables and child |
---|
907 | * namespaces are deleted. |
---|
908 | * |
---|
909 | *---------------------------------------------------------------------- |
---|
910 | */ |
---|
911 | |
---|
912 | void |
---|
913 | Tcl_DeleteNamespace( |
---|
914 | Tcl_Namespace *namespacePtr)/* Points to the namespace to delete. */ |
---|
915 | { |
---|
916 | register Namespace *nsPtr = (Namespace *) namespacePtr; |
---|
917 | Interp *iPtr = (Interp *) nsPtr->interp; |
---|
918 | Namespace *globalNsPtr = (Namespace *) |
---|
919 | TclGetGlobalNamespace((Tcl_Interp *) iPtr); |
---|
920 | Tcl_HashEntry *entryPtr; |
---|
921 | |
---|
922 | /* |
---|
923 | * If the namespace has associated ensemble commands, delete them first. |
---|
924 | * This leaves the actual contents of the namespace alone (unless they are |
---|
925 | * linked ensemble commands, of course). Note that this code is actually |
---|
926 | * reentrant so command delete traces won't purturb things badly. |
---|
927 | */ |
---|
928 | |
---|
929 | while (nsPtr->ensembles != NULL) { |
---|
930 | EnsembleConfig *ensemblePtr = (EnsembleConfig *) nsPtr->ensembles; |
---|
931 | |
---|
932 | /* |
---|
933 | * Splice out and link to indicate that we've already been killed. |
---|
934 | */ |
---|
935 | |
---|
936 | nsPtr->ensembles = (Tcl_Ensemble *) ensemblePtr->next; |
---|
937 | ensemblePtr->next = ensemblePtr; |
---|
938 | Tcl_DeleteCommandFromToken(nsPtr->interp, ensemblePtr->token); |
---|
939 | } |
---|
940 | |
---|
941 | /* |
---|
942 | * If the namespace has a registered unknown handler (TIP 181), then free |
---|
943 | * it here. |
---|
944 | */ |
---|
945 | |
---|
946 | if (nsPtr->unknownHandlerPtr != NULL) { |
---|
947 | Tcl_DecrRefCount(nsPtr->unknownHandlerPtr); |
---|
948 | nsPtr->unknownHandlerPtr = NULL; |
---|
949 | } |
---|
950 | |
---|
951 | /* |
---|
952 | * If the namespace is on the call frame stack, it is marked as "dying" |
---|
953 | * (NS_DYING is OR'd into its flags): the namespace can't be looked up by |
---|
954 | * name but its commands and variables are still usable by those active |
---|
955 | * call frames. When all active call frames referring to the namespace |
---|
956 | * have been popped from the Tcl stack, Tcl_PopCallFrame will call this |
---|
957 | * function again to delete everything in the namespace. If no nsName |
---|
958 | * objects refer to the namespace (i.e., if its refCount is zero), its |
---|
959 | * commands and variables are deleted and the storage for its namespace |
---|
960 | * structure is freed. Otherwise, if its refCount is nonzero, the |
---|
961 | * namespace's commands and variables are deleted but the structure isn't |
---|
962 | * freed. Instead, NS_DEAD is OR'd into the structure's flags to allow the |
---|
963 | * namespace resolution code to recognize that the namespace is "deleted". |
---|
964 | * The structure's storage is freed by FreeNsNameInternalRep when its |
---|
965 | * refCount reaches 0. |
---|
966 | */ |
---|
967 | |
---|
968 | if (nsPtr->activationCount - (nsPtr == globalNsPtr) > 0) { |
---|
969 | nsPtr->flags |= NS_DYING; |
---|
970 | if (nsPtr->parentPtr != NULL) { |
---|
971 | entryPtr = Tcl_FindHashEntry(&nsPtr->parentPtr->childTable, |
---|
972 | nsPtr->name); |
---|
973 | if (entryPtr != NULL) { |
---|
974 | Tcl_DeleteHashEntry(entryPtr); |
---|
975 | } |
---|
976 | } |
---|
977 | nsPtr->parentPtr = NULL; |
---|
978 | } else if (!(nsPtr->flags & NS_KILLED)) { |
---|
979 | /* |
---|
980 | * Delete the namespace and everything in it. If this is the global |
---|
981 | * namespace, then clear it but don't free its storage unless the |
---|
982 | * interpreter is being torn down. Set the NS_KILLED flag to avoid |
---|
983 | * recursive calls here - if the namespace is really in the process of |
---|
984 | * being deleted, ignore any second call. |
---|
985 | */ |
---|
986 | |
---|
987 | nsPtr->flags |= (NS_DYING|NS_KILLED); |
---|
988 | |
---|
989 | TclTeardownNamespace(nsPtr); |
---|
990 | |
---|
991 | if ((nsPtr != globalNsPtr) || (iPtr->flags & DELETED)) { |
---|
992 | /* |
---|
993 | * If this is the global namespace, then it may have residual |
---|
994 | * "errorInfo" and "errorCode" variables for errors that occurred |
---|
995 | * while it was being torn down. Try to clear the variable list |
---|
996 | * one last time. |
---|
997 | */ |
---|
998 | |
---|
999 | TclDeleteNamespaceVars(nsPtr); |
---|
1000 | |
---|
1001 | Tcl_DeleteHashTable(&nsPtr->childTable); |
---|
1002 | Tcl_DeleteHashTable(&nsPtr->cmdTable); |
---|
1003 | |
---|
1004 | /* |
---|
1005 | * If the reference count is 0, then discard the namespace. |
---|
1006 | * Otherwise, mark it as "dead" so that it can't be used. |
---|
1007 | */ |
---|
1008 | |
---|
1009 | if (nsPtr->refCount == 0) { |
---|
1010 | NamespaceFree(nsPtr); |
---|
1011 | } else { |
---|
1012 | nsPtr->flags |= NS_DEAD; |
---|
1013 | } |
---|
1014 | } else { |
---|
1015 | /* |
---|
1016 | * Restore the ::errorInfo and ::errorCode traces. |
---|
1017 | */ |
---|
1018 | |
---|
1019 | EstablishErrorInfoTraces(NULL, nsPtr->interp, NULL, NULL, 0); |
---|
1020 | EstablishErrorCodeTraces(NULL, nsPtr->interp, NULL, NULL, 0); |
---|
1021 | |
---|
1022 | /* |
---|
1023 | * We didn't really kill it, so remove the KILLED marks, so it can |
---|
1024 | * get killed later, avoiding mem leaks. |
---|
1025 | */ |
---|
1026 | |
---|
1027 | nsPtr->flags &= ~(NS_DYING|NS_KILLED); |
---|
1028 | } |
---|
1029 | } |
---|
1030 | } |
---|
1031 | |
---|
1032 | /* |
---|
1033 | *---------------------------------------------------------------------- |
---|
1034 | * |
---|
1035 | * TclTeardownNamespace -- |
---|
1036 | * |
---|
1037 | * Used internally to dismantle and unlink a namespace when it is |
---|
1038 | * deleted. Divorces the namespace from its parent, and deletes all |
---|
1039 | * commands, variables, and child namespaces. |
---|
1040 | * |
---|
1041 | * This is kept separate from Tcl_DeleteNamespace so that the global |
---|
1042 | * namespace can be handled specially. |
---|
1043 | * |
---|
1044 | * Results: |
---|
1045 | * None. |
---|
1046 | * |
---|
1047 | * Side effects: |
---|
1048 | * Removes this namespace from its parent's child namespace hashtable. |
---|
1049 | * Deletes all commands, variables and namespaces in this namespace. |
---|
1050 | * |
---|
1051 | *---------------------------------------------------------------------- |
---|
1052 | */ |
---|
1053 | |
---|
1054 | void |
---|
1055 | TclTeardownNamespace( |
---|
1056 | register Namespace *nsPtr) /* Points to the namespace to be dismantled |
---|
1057 | * and unlinked from its parent. */ |
---|
1058 | { |
---|
1059 | Interp *iPtr = (Interp *) nsPtr->interp; |
---|
1060 | register Tcl_HashEntry *entryPtr; |
---|
1061 | Tcl_HashSearch search; |
---|
1062 | Tcl_Namespace *childNsPtr; |
---|
1063 | Tcl_Command cmd; |
---|
1064 | int i; |
---|
1065 | |
---|
1066 | /* |
---|
1067 | * Start by destroying the namespace's variable table, since variables |
---|
1068 | * might trigger traces. Variable table should be cleared but not freed! |
---|
1069 | * TclDeleteNamespaceVars frees it, so we reinitialize it afterwards. |
---|
1070 | */ |
---|
1071 | |
---|
1072 | TclDeleteNamespaceVars(nsPtr); |
---|
1073 | TclInitVarHashTable(&nsPtr->varTable, nsPtr); |
---|
1074 | |
---|
1075 | /* |
---|
1076 | * Delete all commands in this namespace. Be careful when traversing the |
---|
1077 | * hash table: when each command is deleted, it removes itself from the |
---|
1078 | * command table. |
---|
1079 | * |
---|
1080 | * Don't optimize to Tcl_NextHashEntry() because of traces. |
---|
1081 | */ |
---|
1082 | |
---|
1083 | for (entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); |
---|
1084 | entryPtr != NULL; |
---|
1085 | entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search)) { |
---|
1086 | cmd = Tcl_GetHashValue(entryPtr); |
---|
1087 | Tcl_DeleteCommandFromToken((Tcl_Interp *) iPtr, cmd); |
---|
1088 | } |
---|
1089 | Tcl_DeleteHashTable(&nsPtr->cmdTable); |
---|
1090 | Tcl_InitHashTable(&nsPtr->cmdTable, TCL_STRING_KEYS); |
---|
1091 | |
---|
1092 | /* |
---|
1093 | * Remove the namespace from its parent's child hashtable. |
---|
1094 | */ |
---|
1095 | |
---|
1096 | if (nsPtr->parentPtr != NULL) { |
---|
1097 | entryPtr = Tcl_FindHashEntry(&nsPtr->parentPtr->childTable, |
---|
1098 | nsPtr->name); |
---|
1099 | if (entryPtr != NULL) { |
---|
1100 | Tcl_DeleteHashEntry(entryPtr); |
---|
1101 | } |
---|
1102 | } |
---|
1103 | nsPtr->parentPtr = NULL; |
---|
1104 | |
---|
1105 | /* |
---|
1106 | * Delete the namespace path if one is installed. |
---|
1107 | */ |
---|
1108 | |
---|
1109 | if (nsPtr->commandPathLength != 0) { |
---|
1110 | UnlinkNsPath(nsPtr); |
---|
1111 | nsPtr->commandPathLength = 0; |
---|
1112 | } |
---|
1113 | if (nsPtr->commandPathSourceList != NULL) { |
---|
1114 | NamespacePathEntry *nsPathPtr = nsPtr->commandPathSourceList; |
---|
1115 | do { |
---|
1116 | if (nsPathPtr->nsPtr != NULL && nsPathPtr->creatorNsPtr != NULL) { |
---|
1117 | nsPathPtr->creatorNsPtr->cmdRefEpoch++; |
---|
1118 | } |
---|
1119 | nsPathPtr->nsPtr = NULL; |
---|
1120 | nsPathPtr = nsPathPtr->nextPtr; |
---|
1121 | } while (nsPathPtr != NULL); |
---|
1122 | nsPtr->commandPathSourceList = NULL; |
---|
1123 | } |
---|
1124 | |
---|
1125 | /* |
---|
1126 | * Delete all the child namespaces. |
---|
1127 | * |
---|
1128 | * BE CAREFUL: When each child is deleted, it will divorce itself from its |
---|
1129 | * parent. You can't traverse a hash table properly if its elements are |
---|
1130 | * being deleted. We use only the Tcl_FirstHashEntry function to be safe. |
---|
1131 | * |
---|
1132 | * Don't optimize to Tcl_NextHashEntry() because of traces. |
---|
1133 | */ |
---|
1134 | |
---|
1135 | for (entryPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search); |
---|
1136 | entryPtr != NULL; |
---|
1137 | entryPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search)) { |
---|
1138 | childNsPtr = Tcl_GetHashValue(entryPtr); |
---|
1139 | Tcl_DeleteNamespace(childNsPtr); |
---|
1140 | } |
---|
1141 | |
---|
1142 | /* |
---|
1143 | * Free the namespace's export pattern array. |
---|
1144 | */ |
---|
1145 | |
---|
1146 | if (nsPtr->exportArrayPtr != NULL) { |
---|
1147 | for (i = 0; i < nsPtr->numExportPatterns; i++) { |
---|
1148 | ckfree(nsPtr->exportArrayPtr[i]); |
---|
1149 | } |
---|
1150 | ckfree((char *) nsPtr->exportArrayPtr); |
---|
1151 | nsPtr->exportArrayPtr = NULL; |
---|
1152 | nsPtr->numExportPatterns = 0; |
---|
1153 | nsPtr->maxExportPatterns = 0; |
---|
1154 | } |
---|
1155 | |
---|
1156 | /* |
---|
1157 | * Free any client data associated with the namespace. |
---|
1158 | */ |
---|
1159 | |
---|
1160 | if (nsPtr->deleteProc != NULL) { |
---|
1161 | (*nsPtr->deleteProc)(nsPtr->clientData); |
---|
1162 | } |
---|
1163 | nsPtr->deleteProc = NULL; |
---|
1164 | nsPtr->clientData = NULL; |
---|
1165 | |
---|
1166 | /* |
---|
1167 | * Reset the namespace's id field to ensure that this namespace won't be |
---|
1168 | * interpreted as valid by, e.g., the cache validation code for cached |
---|
1169 | * command references in Tcl_GetCommandFromObj. |
---|
1170 | */ |
---|
1171 | |
---|
1172 | nsPtr->nsId = 0; |
---|
1173 | } |
---|
1174 | |
---|
1175 | /* |
---|
1176 | *---------------------------------------------------------------------- |
---|
1177 | * |
---|
1178 | * NamespaceFree -- |
---|
1179 | * |
---|
1180 | * Called after a namespace has been deleted, when its reference count |
---|
1181 | * reaches 0. Frees the data structure representing the namespace. |
---|
1182 | * |
---|
1183 | * Results: |
---|
1184 | * None. |
---|
1185 | * |
---|
1186 | * Side effects: |
---|
1187 | * None. |
---|
1188 | * |
---|
1189 | *---------------------------------------------------------------------- |
---|
1190 | */ |
---|
1191 | |
---|
1192 | static void |
---|
1193 | NamespaceFree( |
---|
1194 | register Namespace *nsPtr) /* Points to the namespace to free. */ |
---|
1195 | { |
---|
1196 | /* |
---|
1197 | * Most of the namespace's contents are freed when the namespace is |
---|
1198 | * deleted by Tcl_DeleteNamespace. All that remains is to free its names |
---|
1199 | * (for error messages), and the structure itself. |
---|
1200 | */ |
---|
1201 | |
---|
1202 | ckfree(nsPtr->name); |
---|
1203 | ckfree(nsPtr->fullName); |
---|
1204 | |
---|
1205 | ckfree((char *) nsPtr); |
---|
1206 | } |
---|
1207 | |
---|
1208 | /* |
---|
1209 | *---------------------------------------------------------------------- |
---|
1210 | * |
---|
1211 | * Tcl_Export -- |
---|
1212 | * |
---|
1213 | * Makes all the commands matching a pattern available to later be |
---|
1214 | * imported from the namespace specified by namespacePtr (or the current |
---|
1215 | * namespace if namespacePtr is NULL). The specified pattern is appended |
---|
1216 | * onto the namespace's export pattern list, which is optionally cleared |
---|
1217 | * beforehand. |
---|
1218 | * |
---|
1219 | * Results: |
---|
1220 | * Returns TCL_OK if successful, or TCL_ERROR (along with an error |
---|
1221 | * message in the interpreter's result) if something goes wrong. |
---|
1222 | * |
---|
1223 | * Side effects: |
---|
1224 | * Appends the export pattern onto the namespace's export list. |
---|
1225 | * Optionally reset the namespace's export pattern list. |
---|
1226 | * |
---|
1227 | *---------------------------------------------------------------------- |
---|
1228 | */ |
---|
1229 | |
---|
1230 | int |
---|
1231 | Tcl_Export( |
---|
1232 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
1233 | Tcl_Namespace *namespacePtr,/* Points to the namespace from which commands |
---|
1234 | * are to be exported. NULL for the current |
---|
1235 | * namespace. */ |
---|
1236 | const char *pattern, /* String pattern indicating which commands to |
---|
1237 | * export. This pattern may not include any |
---|
1238 | * namespace qualifiers; only commands in the |
---|
1239 | * specified namespace may be exported. */ |
---|
1240 | int resetListFirst) /* If nonzero, resets the namespace's export |
---|
1241 | * list before appending. */ |
---|
1242 | { |
---|
1243 | #define INIT_EXPORT_PATTERNS 5 |
---|
1244 | Namespace *nsPtr, *exportNsPtr, *dummyPtr; |
---|
1245 | Namespace *currNsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
1246 | const char *simplePattern; |
---|
1247 | char *patternCpy; |
---|
1248 | int neededElems, len, i; |
---|
1249 | |
---|
1250 | /* |
---|
1251 | * If the specified namespace is NULL, use the current namespace. |
---|
1252 | */ |
---|
1253 | |
---|
1254 | if (namespacePtr == NULL) { |
---|
1255 | nsPtr = (Namespace *) currNsPtr; |
---|
1256 | } else { |
---|
1257 | nsPtr = (Namespace *) namespacePtr; |
---|
1258 | } |
---|
1259 | |
---|
1260 | /* |
---|
1261 | * If resetListFirst is true (nonzero), clear the namespace's export |
---|
1262 | * pattern list. |
---|
1263 | */ |
---|
1264 | |
---|
1265 | if (resetListFirst) { |
---|
1266 | if (nsPtr->exportArrayPtr != NULL) { |
---|
1267 | for (i = 0; i < nsPtr->numExportPatterns; i++) { |
---|
1268 | ckfree(nsPtr->exportArrayPtr[i]); |
---|
1269 | } |
---|
1270 | ckfree((char *) nsPtr->exportArrayPtr); |
---|
1271 | nsPtr->exportArrayPtr = NULL; |
---|
1272 | TclInvalidateNsCmdLookup(nsPtr); |
---|
1273 | nsPtr->numExportPatterns = 0; |
---|
1274 | nsPtr->maxExportPatterns = 0; |
---|
1275 | } |
---|
1276 | } |
---|
1277 | |
---|
1278 | /* |
---|
1279 | * Check that the pattern doesn't have namespace qualifiers. |
---|
1280 | */ |
---|
1281 | |
---|
1282 | TclGetNamespaceForQualName(interp, pattern, nsPtr, |
---|
1283 | /*flags*/ (TCL_LEAVE_ERR_MSG | TCL_NAMESPACE_ONLY), |
---|
1284 | &exportNsPtr, &dummyPtr, &dummyPtr, &simplePattern); |
---|
1285 | |
---|
1286 | if ((exportNsPtr != nsPtr) || (strcmp(pattern, simplePattern) != 0)) { |
---|
1287 | Tcl_AppendResult(interp, "invalid export pattern \"", pattern, |
---|
1288 | "\": pattern can't specify a namespace", NULL); |
---|
1289 | return TCL_ERROR; |
---|
1290 | } |
---|
1291 | |
---|
1292 | /* |
---|
1293 | * Make sure that we don't already have the pattern in the array |
---|
1294 | */ |
---|
1295 | |
---|
1296 | if (nsPtr->exportArrayPtr != NULL) { |
---|
1297 | for (i = 0; i < nsPtr->numExportPatterns; i++) { |
---|
1298 | if (strcmp(pattern, nsPtr->exportArrayPtr[i]) == 0) { |
---|
1299 | /* |
---|
1300 | * The pattern already exists in the list. |
---|
1301 | */ |
---|
1302 | |
---|
1303 | return TCL_OK; |
---|
1304 | } |
---|
1305 | } |
---|
1306 | } |
---|
1307 | |
---|
1308 | /* |
---|
1309 | * Make sure there is room in the namespace's pattern array for the new |
---|
1310 | * pattern. |
---|
1311 | */ |
---|
1312 | |
---|
1313 | neededElems = nsPtr->numExportPatterns + 1; |
---|
1314 | if (neededElems > nsPtr->maxExportPatterns) { |
---|
1315 | nsPtr->maxExportPatterns = nsPtr->maxExportPatterns ? |
---|
1316 | 2 * nsPtr->maxExportPatterns : INIT_EXPORT_PATTERNS; |
---|
1317 | nsPtr->exportArrayPtr = (char **) |
---|
1318 | ckrealloc((char *) nsPtr->exportArrayPtr, |
---|
1319 | sizeof(char *) * nsPtr->maxExportPatterns); |
---|
1320 | } |
---|
1321 | |
---|
1322 | /* |
---|
1323 | * Add the pattern to the namespace's array of export patterns. |
---|
1324 | */ |
---|
1325 | |
---|
1326 | len = strlen(pattern); |
---|
1327 | patternCpy = ckalloc((unsigned) (len + 1)); |
---|
1328 | memcpy(patternCpy, pattern, (unsigned) len + 1); |
---|
1329 | |
---|
1330 | nsPtr->exportArrayPtr[nsPtr->numExportPatterns] = patternCpy; |
---|
1331 | nsPtr->numExportPatterns++; |
---|
1332 | |
---|
1333 | /* |
---|
1334 | * The list of commands actually exported from the namespace might have |
---|
1335 | * changed (probably will have!) However, we do not need to recompute this |
---|
1336 | * just yet; next time we need the info will be soon enough. |
---|
1337 | */ |
---|
1338 | |
---|
1339 | TclInvalidateNsCmdLookup(nsPtr); |
---|
1340 | |
---|
1341 | return TCL_OK; |
---|
1342 | #undef INIT_EXPORT_PATTERNS |
---|
1343 | } |
---|
1344 | |
---|
1345 | /* |
---|
1346 | *---------------------------------------------------------------------- |
---|
1347 | * |
---|
1348 | * Tcl_AppendExportList -- |
---|
1349 | * |
---|
1350 | * Appends onto the argument object the list of export patterns for the |
---|
1351 | * specified namespace. |
---|
1352 | * |
---|
1353 | * Results: |
---|
1354 | * The return value is normally TCL_OK; in this case the object |
---|
1355 | * referenced by objPtr has each export pattern appended to it. If an |
---|
1356 | * error occurs, TCL_ERROR is returned and the interpreter's result holds |
---|
1357 | * an error message. |
---|
1358 | * |
---|
1359 | * Side effects: |
---|
1360 | * If necessary, the object referenced by objPtr is converted into a list |
---|
1361 | * object. |
---|
1362 | * |
---|
1363 | *---------------------------------------------------------------------- |
---|
1364 | */ |
---|
1365 | |
---|
1366 | int |
---|
1367 | Tcl_AppendExportList( |
---|
1368 | Tcl_Interp *interp, /* Interpreter used for error reporting. */ |
---|
1369 | Tcl_Namespace *namespacePtr,/* Points to the namespace whose export |
---|
1370 | * pattern list is appended onto objPtr. NULL |
---|
1371 | * for the current namespace. */ |
---|
1372 | Tcl_Obj *objPtr) /* Points to the Tcl object onto which the |
---|
1373 | * export pattern list is appended. */ |
---|
1374 | { |
---|
1375 | Namespace *nsPtr; |
---|
1376 | int i, result; |
---|
1377 | |
---|
1378 | /* |
---|
1379 | * If the specified namespace is NULL, use the current namespace. |
---|
1380 | */ |
---|
1381 | |
---|
1382 | if (namespacePtr == NULL) { |
---|
1383 | nsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
1384 | } else { |
---|
1385 | nsPtr = (Namespace *) namespacePtr; |
---|
1386 | } |
---|
1387 | |
---|
1388 | /* |
---|
1389 | * Append the export pattern list onto objPtr. |
---|
1390 | */ |
---|
1391 | |
---|
1392 | for (i = 0; i < nsPtr->numExportPatterns; i++) { |
---|
1393 | result = Tcl_ListObjAppendElement(interp, objPtr, |
---|
1394 | Tcl_NewStringObj(nsPtr->exportArrayPtr[i], -1)); |
---|
1395 | if (result != TCL_OK) { |
---|
1396 | return result; |
---|
1397 | } |
---|
1398 | } |
---|
1399 | return TCL_OK; |
---|
1400 | } |
---|
1401 | |
---|
1402 | /* |
---|
1403 | *---------------------------------------------------------------------- |
---|
1404 | * |
---|
1405 | * Tcl_Import -- |
---|
1406 | * |
---|
1407 | * Imports all of the commands matching a pattern into the namespace |
---|
1408 | * specified by namespacePtr (or the current namespace if contextNsPtr is |
---|
1409 | * NULL). This is done by creating a new command (the "imported command") |
---|
1410 | * that points to the real command in its original namespace. |
---|
1411 | * |
---|
1412 | * If matching commands are on the autoload path but haven't been loaded |
---|
1413 | * yet, this command forces them to be loaded, then creates the links to |
---|
1414 | * them. |
---|
1415 | * |
---|
1416 | * Results: |
---|
1417 | * Returns TCL_OK if successful, or TCL_ERROR (along with an error |
---|
1418 | * message in the interpreter's result) if something goes wrong. |
---|
1419 | * |
---|
1420 | * Side effects: |
---|
1421 | * Creates new commands in the importing namespace. These indirect calls |
---|
1422 | * back to the real command and are deleted if the real commands are |
---|
1423 | * deleted. |
---|
1424 | * |
---|
1425 | *---------------------------------------------------------------------- |
---|
1426 | */ |
---|
1427 | |
---|
1428 | int |
---|
1429 | Tcl_Import( |
---|
1430 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
1431 | Tcl_Namespace *namespacePtr,/* Points to the namespace into which the |
---|
1432 | * commands are to be imported. NULL for the |
---|
1433 | * current namespace. */ |
---|
1434 | const char *pattern, /* String pattern indicating which commands to |
---|
1435 | * import. This pattern should be qualified by |
---|
1436 | * the name of the namespace from which to |
---|
1437 | * import the command(s). */ |
---|
1438 | int allowOverwrite) /* If nonzero, allow existing commands to be |
---|
1439 | * overwritten by imported commands. If 0, |
---|
1440 | * return an error if an imported cmd |
---|
1441 | * conflicts with an existing one. */ |
---|
1442 | { |
---|
1443 | Namespace *nsPtr, *importNsPtr, *dummyPtr; |
---|
1444 | const char *simplePattern; |
---|
1445 | register Tcl_HashEntry *hPtr; |
---|
1446 | Tcl_HashSearch search; |
---|
1447 | |
---|
1448 | /* |
---|
1449 | * If the specified namespace is NULL, use the current namespace. |
---|
1450 | */ |
---|
1451 | |
---|
1452 | if (namespacePtr == NULL) { |
---|
1453 | nsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
1454 | } else { |
---|
1455 | nsPtr = (Namespace *) namespacePtr; |
---|
1456 | } |
---|
1457 | |
---|
1458 | /* |
---|
1459 | * First, invoke the "auto_import" command with the pattern being |
---|
1460 | * imported. This command is part of the Tcl library. It looks for |
---|
1461 | * imported commands in autoloaded libraries and loads them in. That way, |
---|
1462 | * they will be found when we try to create links below. |
---|
1463 | * |
---|
1464 | * Note that we don't just call Tcl_EvalObjv() directly because we do not |
---|
1465 | * want absence of the command to be a failure case. |
---|
1466 | */ |
---|
1467 | |
---|
1468 | if (Tcl_FindCommand(interp,"auto_import",NULL,TCL_GLOBAL_ONLY) != NULL) { |
---|
1469 | Tcl_Obj *objv[2]; |
---|
1470 | int result; |
---|
1471 | |
---|
1472 | TclNewLiteralStringObj(objv[0], "auto_import"); |
---|
1473 | objv[1] = Tcl_NewStringObj(pattern, -1); |
---|
1474 | |
---|
1475 | Tcl_IncrRefCount(objv[0]); |
---|
1476 | Tcl_IncrRefCount(objv[1]); |
---|
1477 | result = Tcl_EvalObjv(interp, 2, objv, TCL_GLOBAL_ONLY); |
---|
1478 | Tcl_DecrRefCount(objv[0]); |
---|
1479 | Tcl_DecrRefCount(objv[1]); |
---|
1480 | |
---|
1481 | if (result != TCL_OK) { |
---|
1482 | return TCL_ERROR; |
---|
1483 | } |
---|
1484 | Tcl_ResetResult(interp); |
---|
1485 | } |
---|
1486 | |
---|
1487 | /* |
---|
1488 | * From the pattern, find the namespace from which we are importing and |
---|
1489 | * get the simple pattern (no namespace qualifiers or ::'s) at the end. |
---|
1490 | */ |
---|
1491 | |
---|
1492 | if (strlen(pattern) == 0) { |
---|
1493 | Tcl_SetObjResult(interp, Tcl_NewStringObj("empty import pattern", -1)); |
---|
1494 | return TCL_ERROR; |
---|
1495 | } |
---|
1496 | TclGetNamespaceForQualName(interp, pattern, nsPtr, |
---|
1497 | /*flags*/ (TCL_LEAVE_ERR_MSG | TCL_NAMESPACE_ONLY), |
---|
1498 | &importNsPtr, &dummyPtr, &dummyPtr, &simplePattern); |
---|
1499 | |
---|
1500 | if (importNsPtr == NULL) { |
---|
1501 | Tcl_AppendResult(interp, "unknown namespace in import pattern \"", |
---|
1502 | pattern, "\"", NULL); |
---|
1503 | Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "NAMESPACE", pattern, NULL); |
---|
1504 | return TCL_ERROR; |
---|
1505 | } |
---|
1506 | if (importNsPtr == nsPtr) { |
---|
1507 | if (pattern == simplePattern) { |
---|
1508 | Tcl_AppendResult(interp, |
---|
1509 | "no namespace specified in import pattern \"", pattern, |
---|
1510 | "\"", NULL); |
---|
1511 | } else { |
---|
1512 | Tcl_AppendResult(interp, "import pattern \"", pattern, |
---|
1513 | "\" tries to import from namespace \"", |
---|
1514 | importNsPtr->name, "\" into itself", NULL); |
---|
1515 | } |
---|
1516 | return TCL_ERROR; |
---|
1517 | } |
---|
1518 | |
---|
1519 | /* |
---|
1520 | * Scan through the command table in the source namespace and look for |
---|
1521 | * exported commands that match the string pattern. Create an "imported |
---|
1522 | * command" in the current namespace for each imported command; these |
---|
1523 | * commands redirect their invocations to the "real" command. |
---|
1524 | */ |
---|
1525 | |
---|
1526 | if ((simplePattern != NULL) && TclMatchIsTrivial(simplePattern)) { |
---|
1527 | hPtr = Tcl_FindHashEntry(&importNsPtr->cmdTable, simplePattern); |
---|
1528 | if (hPtr == NULL) { |
---|
1529 | return TCL_OK; |
---|
1530 | } |
---|
1531 | return DoImport(interp, nsPtr, hPtr, simplePattern, pattern, |
---|
1532 | importNsPtr, allowOverwrite); |
---|
1533 | } |
---|
1534 | for (hPtr = Tcl_FirstHashEntry(&importNsPtr->cmdTable, &search); |
---|
1535 | (hPtr != NULL); hPtr = Tcl_NextHashEntry(&search)) { |
---|
1536 | char *cmdName = Tcl_GetHashKey(&importNsPtr->cmdTable, hPtr); |
---|
1537 | if (Tcl_StringMatch(cmdName, simplePattern) && |
---|
1538 | DoImport(interp, nsPtr, hPtr, cmdName, pattern, importNsPtr, |
---|
1539 | allowOverwrite) == TCL_ERROR) { |
---|
1540 | return TCL_ERROR; |
---|
1541 | } |
---|
1542 | } |
---|
1543 | return TCL_OK; |
---|
1544 | } |
---|
1545 | |
---|
1546 | /* |
---|
1547 | *---------------------------------------------------------------------- |
---|
1548 | * |
---|
1549 | * DoImport -- |
---|
1550 | * |
---|
1551 | * Import a particular command from one namespace into another. Helper |
---|
1552 | * for Tcl_Import(). |
---|
1553 | * |
---|
1554 | * Results: |
---|
1555 | * Standard Tcl result code. If TCL_ERROR, appends an error message to |
---|
1556 | * the interpreter result. |
---|
1557 | * |
---|
1558 | * Side effects: |
---|
1559 | * A new command is created in the target namespace unless this is a |
---|
1560 | * reimport of exactly the same command as before. |
---|
1561 | * |
---|
1562 | *---------------------------------------------------------------------- |
---|
1563 | */ |
---|
1564 | |
---|
1565 | static int |
---|
1566 | DoImport( |
---|
1567 | Tcl_Interp *interp, |
---|
1568 | Namespace *nsPtr, |
---|
1569 | Tcl_HashEntry *hPtr, |
---|
1570 | const char *cmdName, |
---|
1571 | const char *pattern, |
---|
1572 | Namespace *importNsPtr, |
---|
1573 | int allowOverwrite) |
---|
1574 | { |
---|
1575 | int i = 0, exported = 0; |
---|
1576 | Tcl_HashEntry *found; |
---|
1577 | |
---|
1578 | /* |
---|
1579 | * The command cmdName in the source namespace matches the pattern. Check |
---|
1580 | * whether it was exported. If it wasn't, we ignore it. |
---|
1581 | */ |
---|
1582 | |
---|
1583 | while (!exported && (i < importNsPtr->numExportPatterns)) { |
---|
1584 | exported |= Tcl_StringMatch(cmdName, importNsPtr->exportArrayPtr[i++]); |
---|
1585 | } |
---|
1586 | if (!exported) { |
---|
1587 | return TCL_OK; |
---|
1588 | } |
---|
1589 | |
---|
1590 | /* |
---|
1591 | * Unless there is a name clash, create an imported command in the current |
---|
1592 | * namespace that refers to cmdPtr. |
---|
1593 | */ |
---|
1594 | |
---|
1595 | found = Tcl_FindHashEntry(&nsPtr->cmdTable, cmdName); |
---|
1596 | if ((found == NULL) || allowOverwrite) { |
---|
1597 | /* |
---|
1598 | * Create the imported command and its client data. To create the new |
---|
1599 | * command in the current namespace, generate a fully qualified name |
---|
1600 | * for it. |
---|
1601 | */ |
---|
1602 | |
---|
1603 | Tcl_DString ds; |
---|
1604 | Tcl_Command importedCmd; |
---|
1605 | ImportedCmdData *dataPtr; |
---|
1606 | Command *cmdPtr; |
---|
1607 | ImportRef *refPtr; |
---|
1608 | |
---|
1609 | Tcl_DStringInit(&ds); |
---|
1610 | Tcl_DStringAppend(&ds, nsPtr->fullName, -1); |
---|
1611 | if (nsPtr != ((Interp *) interp)->globalNsPtr) { |
---|
1612 | Tcl_DStringAppend(&ds, "::", 2); |
---|
1613 | } |
---|
1614 | Tcl_DStringAppend(&ds, cmdName, -1); |
---|
1615 | |
---|
1616 | /* |
---|
1617 | * Check whether creating the new imported command in the current |
---|
1618 | * namespace would create a cycle of imported command references. |
---|
1619 | */ |
---|
1620 | |
---|
1621 | cmdPtr = Tcl_GetHashValue(hPtr); |
---|
1622 | if (found != NULL && cmdPtr->deleteProc == DeleteImportedCmd) { |
---|
1623 | Command *overwrite = Tcl_GetHashValue(found); |
---|
1624 | Command *link = cmdPtr; |
---|
1625 | |
---|
1626 | while (link->deleteProc == DeleteImportedCmd) { |
---|
1627 | ImportedCmdData *dataPtr = link->objClientData; |
---|
1628 | |
---|
1629 | link = dataPtr->realCmdPtr; |
---|
1630 | if (overwrite == link) { |
---|
1631 | Tcl_AppendResult(interp, "import pattern \"", pattern, |
---|
1632 | "\" would create a loop containing command \"", |
---|
1633 | Tcl_DStringValue(&ds), "\"", NULL); |
---|
1634 | Tcl_DStringFree(&ds); |
---|
1635 | return TCL_ERROR; |
---|
1636 | } |
---|
1637 | } |
---|
1638 | } |
---|
1639 | |
---|
1640 | dataPtr = (ImportedCmdData *) ckalloc(sizeof(ImportedCmdData)); |
---|
1641 | importedCmd = Tcl_CreateObjCommand(interp, Tcl_DStringValue(&ds), |
---|
1642 | InvokeImportedCmd, dataPtr, DeleteImportedCmd); |
---|
1643 | dataPtr->realCmdPtr = cmdPtr; |
---|
1644 | dataPtr->selfPtr = (Command *) importedCmd; |
---|
1645 | dataPtr->selfPtr->compileProc = cmdPtr->compileProc; |
---|
1646 | Tcl_DStringFree(&ds); |
---|
1647 | |
---|
1648 | /* |
---|
1649 | * Create an ImportRef structure describing this new import command |
---|
1650 | * and add it to the import ref list in the "real" command. |
---|
1651 | */ |
---|
1652 | |
---|
1653 | refPtr = (ImportRef *) ckalloc(sizeof(ImportRef)); |
---|
1654 | refPtr->importedCmdPtr = (Command *) importedCmd; |
---|
1655 | refPtr->nextPtr = cmdPtr->importRefPtr; |
---|
1656 | cmdPtr->importRefPtr = refPtr; |
---|
1657 | } else { |
---|
1658 | Command *overwrite = Tcl_GetHashValue(found); |
---|
1659 | |
---|
1660 | if (overwrite->deleteProc == DeleteImportedCmd) { |
---|
1661 | ImportedCmdData *dataPtr = overwrite->objClientData; |
---|
1662 | |
---|
1663 | if (dataPtr->realCmdPtr == Tcl_GetHashValue(hPtr)) { |
---|
1664 | /* |
---|
1665 | * Repeated import of same command is acceptable. |
---|
1666 | */ |
---|
1667 | |
---|
1668 | return TCL_OK; |
---|
1669 | } |
---|
1670 | } |
---|
1671 | Tcl_AppendResult(interp, "can't import command \"", cmdName, |
---|
1672 | "\": already exists", NULL); |
---|
1673 | return TCL_ERROR; |
---|
1674 | } |
---|
1675 | return TCL_OK; |
---|
1676 | } |
---|
1677 | |
---|
1678 | /* |
---|
1679 | *---------------------------------------------------------------------- |
---|
1680 | * |
---|
1681 | * Tcl_ForgetImport -- |
---|
1682 | * |
---|
1683 | * Deletes commands previously imported into the namespace indicated. |
---|
1684 | * The by namespacePtr, or the current namespace of interp, when |
---|
1685 | * namespacePtr is NULL. The pattern controls which imported commands are |
---|
1686 | * deleted. A simple pattern, one without namespace separators, matches |
---|
1687 | * the current command names of imported commands in the namespace. |
---|
1688 | * Matching imported commands are deleted. A qualified pattern is |
---|
1689 | * interpreted as deletion selection on the basis of where the command is |
---|
1690 | * imported from. The original command and "first link" command for each |
---|
1691 | * imported command are determined, and they are matched against the |
---|
1692 | * pattern. A match leads to deletion of the imported command. |
---|
1693 | * |
---|
1694 | * Results: |
---|
1695 | * Returns TCL_ERROR and records an error message in the interp result if |
---|
1696 | * a namespace qualified pattern refers to a namespace that does not |
---|
1697 | * exist. Otherwise, returns TCL_OK. |
---|
1698 | * |
---|
1699 | * Side effects: |
---|
1700 | * May delete commands. |
---|
1701 | * |
---|
1702 | *---------------------------------------------------------------------- |
---|
1703 | */ |
---|
1704 | |
---|
1705 | int |
---|
1706 | Tcl_ForgetImport( |
---|
1707 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
1708 | Tcl_Namespace *namespacePtr,/* Points to the namespace from which |
---|
1709 | * previously imported commands should be |
---|
1710 | * removed. NULL for current namespace. */ |
---|
1711 | const char *pattern) /* String pattern indicating which imported |
---|
1712 | * commands to remove. */ |
---|
1713 | { |
---|
1714 | Namespace *nsPtr, *sourceNsPtr, *dummyPtr; |
---|
1715 | const char *simplePattern; |
---|
1716 | char *cmdName; |
---|
1717 | register Tcl_HashEntry *hPtr; |
---|
1718 | Tcl_HashSearch search; |
---|
1719 | |
---|
1720 | /* |
---|
1721 | * If the specified namespace is NULL, use the current namespace. |
---|
1722 | */ |
---|
1723 | |
---|
1724 | if (namespacePtr == NULL) { |
---|
1725 | nsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
1726 | } else { |
---|
1727 | nsPtr = (Namespace *) namespacePtr; |
---|
1728 | } |
---|
1729 | |
---|
1730 | /* |
---|
1731 | * Parse the pattern into its namespace-qualification (if any) and the |
---|
1732 | * simple pattern. |
---|
1733 | */ |
---|
1734 | |
---|
1735 | TclGetNamespaceForQualName(interp, pattern, nsPtr, |
---|
1736 | /*flags*/ (TCL_LEAVE_ERR_MSG | TCL_NAMESPACE_ONLY), |
---|
1737 | &sourceNsPtr, &dummyPtr, &dummyPtr, &simplePattern); |
---|
1738 | |
---|
1739 | if (sourceNsPtr == NULL) { |
---|
1740 | Tcl_AppendResult(interp, |
---|
1741 | "unknown namespace in namespace forget pattern \"", |
---|
1742 | pattern, "\"", NULL); |
---|
1743 | Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "NAMESPACE", pattern, NULL); |
---|
1744 | return TCL_ERROR; |
---|
1745 | } |
---|
1746 | |
---|
1747 | if (strcmp(pattern, simplePattern) == 0) { |
---|
1748 | /* |
---|
1749 | * The pattern is simple. Delete any imported commands that match it. |
---|
1750 | */ |
---|
1751 | |
---|
1752 | if (TclMatchIsTrivial(simplePattern)) { |
---|
1753 | Command *cmdPtr; |
---|
1754 | |
---|
1755 | hPtr = Tcl_FindHashEntry(&nsPtr->cmdTable, simplePattern); |
---|
1756 | if ((hPtr != NULL) |
---|
1757 | && (cmdPtr = Tcl_GetHashValue(hPtr)) |
---|
1758 | && (cmdPtr->deleteProc == DeleteImportedCmd)) { |
---|
1759 | Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); |
---|
1760 | } |
---|
1761 | return TCL_OK; |
---|
1762 | } |
---|
1763 | for (hPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); |
---|
1764 | (hPtr != NULL); hPtr = Tcl_NextHashEntry(&search)) { |
---|
1765 | Command *cmdPtr = Tcl_GetHashValue(hPtr); |
---|
1766 | |
---|
1767 | if (cmdPtr->deleteProc != DeleteImportedCmd) { |
---|
1768 | continue; |
---|
1769 | } |
---|
1770 | cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, hPtr); |
---|
1771 | if (Tcl_StringMatch(cmdName, simplePattern)) { |
---|
1772 | Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); |
---|
1773 | } |
---|
1774 | } |
---|
1775 | return TCL_OK; |
---|
1776 | } |
---|
1777 | |
---|
1778 | /* |
---|
1779 | * The pattern was namespace-qualified. |
---|
1780 | */ |
---|
1781 | |
---|
1782 | for (hPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); (hPtr != NULL); |
---|
1783 | hPtr = Tcl_NextHashEntry(&search)) { |
---|
1784 | Tcl_CmdInfo info; |
---|
1785 | Tcl_Command token = Tcl_GetHashValue(hPtr); |
---|
1786 | Tcl_Command origin = TclGetOriginalCommand(token); |
---|
1787 | |
---|
1788 | if (Tcl_GetCommandInfoFromToken(origin, &info) == 0) { |
---|
1789 | continue; /* Not an imported command. */ |
---|
1790 | } |
---|
1791 | if (info.namespacePtr != (Tcl_Namespace *) sourceNsPtr) { |
---|
1792 | /* |
---|
1793 | * Original not in namespace we're matching. Check the first link |
---|
1794 | * in the import chain. |
---|
1795 | */ |
---|
1796 | |
---|
1797 | Command *cmdPtr = (Command *) token; |
---|
1798 | ImportedCmdData *dataPtr = cmdPtr->objClientData; |
---|
1799 | Tcl_Command firstToken = (Tcl_Command) dataPtr->realCmdPtr; |
---|
1800 | |
---|
1801 | if (firstToken == origin) { |
---|
1802 | continue; |
---|
1803 | } |
---|
1804 | Tcl_GetCommandInfoFromToken(firstToken, &info); |
---|
1805 | if (info.namespacePtr != (Tcl_Namespace *) sourceNsPtr) { |
---|
1806 | continue; |
---|
1807 | } |
---|
1808 | origin = firstToken; |
---|
1809 | } |
---|
1810 | if (Tcl_StringMatch(Tcl_GetCommandName(NULL, origin), simplePattern)) { |
---|
1811 | Tcl_DeleteCommandFromToken(interp, token); |
---|
1812 | } |
---|
1813 | } |
---|
1814 | return TCL_OK; |
---|
1815 | } |
---|
1816 | |
---|
1817 | /* |
---|
1818 | *---------------------------------------------------------------------- |
---|
1819 | * |
---|
1820 | * TclGetOriginalCommand -- |
---|
1821 | * |
---|
1822 | * An imported command is created in an namespace when a "real" command |
---|
1823 | * is imported from another namespace. If the specified command is an |
---|
1824 | * imported command, this function returns the original command it refers |
---|
1825 | * to. |
---|
1826 | * |
---|
1827 | * Results: |
---|
1828 | * If the command was imported into a sequence of namespaces a, b,...,n |
---|
1829 | * where each successive namespace just imports the command from the |
---|
1830 | * previous namespace, this function returns the Tcl_Command token in the |
---|
1831 | * first namespace, a. Otherwise, if the specified command is not an |
---|
1832 | * imported command, the function returns NULL. |
---|
1833 | * |
---|
1834 | * Side effects: |
---|
1835 | * None. |
---|
1836 | * |
---|
1837 | *---------------------------------------------------------------------- |
---|
1838 | */ |
---|
1839 | |
---|
1840 | Tcl_Command |
---|
1841 | TclGetOriginalCommand( |
---|
1842 | Tcl_Command command) /* The imported command for which the original |
---|
1843 | * command should be returned. */ |
---|
1844 | { |
---|
1845 | register Command *cmdPtr = (Command *) command; |
---|
1846 | ImportedCmdData *dataPtr; |
---|
1847 | |
---|
1848 | if (cmdPtr->deleteProc != DeleteImportedCmd) { |
---|
1849 | return NULL; |
---|
1850 | } |
---|
1851 | |
---|
1852 | while (cmdPtr->deleteProc == DeleteImportedCmd) { |
---|
1853 | dataPtr = cmdPtr->objClientData; |
---|
1854 | cmdPtr = dataPtr->realCmdPtr; |
---|
1855 | } |
---|
1856 | return (Tcl_Command) cmdPtr; |
---|
1857 | } |
---|
1858 | |
---|
1859 | /* |
---|
1860 | *---------------------------------------------------------------------- |
---|
1861 | * |
---|
1862 | * InvokeImportedCmd -- |
---|
1863 | * |
---|
1864 | * Invoked by Tcl whenever the user calls an imported command that was |
---|
1865 | * created by Tcl_Import. Finds the "real" command (in another |
---|
1866 | * namespace), and passes control to it. |
---|
1867 | * |
---|
1868 | * Results: |
---|
1869 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
1870 | * |
---|
1871 | * Side effects: |
---|
1872 | * Returns a result in the interpreter's result object. If anything goes |
---|
1873 | * wrong, the result object is set to an error message. |
---|
1874 | * |
---|
1875 | *---------------------------------------------------------------------- |
---|
1876 | */ |
---|
1877 | |
---|
1878 | static int |
---|
1879 | InvokeImportedCmd( |
---|
1880 | ClientData clientData, /* Points to the imported command's |
---|
1881 | * ImportedCmdData structure. */ |
---|
1882 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
1883 | int objc, /* Number of arguments. */ |
---|
1884 | Tcl_Obj *const objv[]) /* The argument objects. */ |
---|
1885 | { |
---|
1886 | register ImportedCmdData *dataPtr = clientData; |
---|
1887 | register Command *realCmdPtr = dataPtr->realCmdPtr; |
---|
1888 | |
---|
1889 | return (*realCmdPtr->objProc)(realCmdPtr->objClientData, interp, |
---|
1890 | objc, objv); |
---|
1891 | } |
---|
1892 | |
---|
1893 | /* |
---|
1894 | *---------------------------------------------------------------------- |
---|
1895 | * |
---|
1896 | * DeleteImportedCmd -- |
---|
1897 | * |
---|
1898 | * Invoked by Tcl whenever an imported command is deleted. The "real" |
---|
1899 | * command keeps a list of all the imported commands that refer to it, so |
---|
1900 | * those imported commands can be deleted when the real command is |
---|
1901 | * deleted. This function removes the imported command reference from the |
---|
1902 | * real command's list, and frees up the memory associated with the |
---|
1903 | * imported command. |
---|
1904 | * |
---|
1905 | * Results: |
---|
1906 | * None. |
---|
1907 | * |
---|
1908 | * Side effects: |
---|
1909 | * Removes the imported command from the real command's import list. |
---|
1910 | * |
---|
1911 | *---------------------------------------------------------------------- |
---|
1912 | */ |
---|
1913 | |
---|
1914 | static void |
---|
1915 | DeleteImportedCmd( |
---|
1916 | ClientData clientData) /* Points to the imported command's |
---|
1917 | * ImportedCmdData structure. */ |
---|
1918 | { |
---|
1919 | ImportedCmdData *dataPtr = clientData; |
---|
1920 | Command *realCmdPtr = dataPtr->realCmdPtr; |
---|
1921 | Command *selfPtr = dataPtr->selfPtr; |
---|
1922 | register ImportRef *refPtr, *prevPtr; |
---|
1923 | |
---|
1924 | prevPtr = NULL; |
---|
1925 | for (refPtr = realCmdPtr->importRefPtr; refPtr != NULL; |
---|
1926 | refPtr = refPtr->nextPtr) { |
---|
1927 | if (refPtr->importedCmdPtr == selfPtr) { |
---|
1928 | /* |
---|
1929 | * Remove *refPtr from real command's list of imported commands |
---|
1930 | * that refer to it. |
---|
1931 | */ |
---|
1932 | |
---|
1933 | if (prevPtr == NULL) { /* refPtr is first in list. */ |
---|
1934 | realCmdPtr->importRefPtr = refPtr->nextPtr; |
---|
1935 | } else { |
---|
1936 | prevPtr->nextPtr = refPtr->nextPtr; |
---|
1937 | } |
---|
1938 | ckfree((char *) refPtr); |
---|
1939 | ckfree((char *) dataPtr); |
---|
1940 | return; |
---|
1941 | } |
---|
1942 | prevPtr = refPtr; |
---|
1943 | } |
---|
1944 | |
---|
1945 | Tcl_Panic("DeleteImportedCmd: did not find cmd in real cmd's list of import references"); |
---|
1946 | } |
---|
1947 | |
---|
1948 | /* |
---|
1949 | *---------------------------------------------------------------------- |
---|
1950 | * |
---|
1951 | * TclGetNamespaceForQualName -- |
---|
1952 | * |
---|
1953 | * Given a qualified name specifying a command, variable, or namespace, |
---|
1954 | * and a namespace in which to resolve the name, this function returns a |
---|
1955 | * pointer to the namespace that contains the item. A qualified name |
---|
1956 | * consists of the "simple" name of an item qualified by the names of an |
---|
1957 | * arbitrary number of containing namespace separated by "::"s. If the |
---|
1958 | * qualified name starts with "::", it is interpreted absolutely from the |
---|
1959 | * global namespace. Otherwise, it is interpreted relative to the |
---|
1960 | * namespace specified by cxtNsPtr if it is non-NULL. If cxtNsPtr is |
---|
1961 | * NULL, the name is interpreted relative to the current namespace. |
---|
1962 | * |
---|
1963 | * A relative name like "foo::bar::x" can be found starting in either the |
---|
1964 | * current namespace or in the global namespace. So each search usually |
---|
1965 | * follows two tracks, and two possible namespaces are returned. If the |
---|
1966 | * function sets either *nsPtrPtr or *altNsPtrPtr to NULL, then that path |
---|
1967 | * failed. |
---|
1968 | * |
---|
1969 | * If "flags" contains TCL_GLOBAL_ONLY, the relative qualified name is |
---|
1970 | * sought only in the global :: namespace. The alternate search (also) |
---|
1971 | * starting from the global namespace is ignored and *altNsPtrPtr is set |
---|
1972 | * NULL. |
---|
1973 | * |
---|
1974 | * If "flags" contains TCL_NAMESPACE_ONLY, the relative qualified name is |
---|
1975 | * sought only in the namespace specified by cxtNsPtr. The alternate |
---|
1976 | * search starting from the global namespace is ignored and *altNsPtrPtr |
---|
1977 | * is set NULL. If both TCL_GLOBAL_ONLY and TCL_NAMESPACE_ONLY are |
---|
1978 | * specified, TCL_GLOBAL_ONLY is ignored and the search starts from the |
---|
1979 | * namespace specified by cxtNsPtr. |
---|
1980 | * |
---|
1981 | * If "flags" contains TCL_CREATE_NS_IF_UNKNOWN, all namespace components |
---|
1982 | * of the qualified name that cannot be found are automatically created |
---|
1983 | * within their specified parent. This makes sure that functions like |
---|
1984 | * Tcl_CreateCommand always succeed. There is no alternate search path, |
---|
1985 | * so *altNsPtrPtr is set NULL. |
---|
1986 | * |
---|
1987 | * If "flags" contains TCL_FIND_ONLY_NS, the qualified name is treated as |
---|
1988 | * a reference to a namespace, and the entire qualified name is followed. |
---|
1989 | * If the name is relative, the namespace is looked up only in the |
---|
1990 | * current namespace. A pointer to the namespace is stored in *nsPtrPtr |
---|
1991 | * and NULL is stored in *simpleNamePtr. Otherwise, if TCL_FIND_ONLY_NS |
---|
1992 | * is not specified, only the leading components are treated as namespace |
---|
1993 | * names, and a pointer to the simple name of the final component is |
---|
1994 | * stored in *simpleNamePtr. |
---|
1995 | * |
---|
1996 | * Results: |
---|
1997 | * It sets *nsPtrPtr and *altNsPtrPtr to point to the two possible |
---|
1998 | * namespaces which represent the last (containing) namespace in the |
---|
1999 | * qualified name. If the function sets either *nsPtrPtr or *altNsPtrPtr |
---|
2000 | * to NULL, then the search along that path failed. The function also |
---|
2001 | * stores a pointer to the simple name of the final component in |
---|
2002 | * *simpleNamePtr. If the qualified name is "::" or was treated as a |
---|
2003 | * namespace reference (TCL_FIND_ONLY_NS), the function stores a pointer |
---|
2004 | * to the namespace in *nsPtrPtr, NULL in *altNsPtrPtr, and sets |
---|
2005 | * *simpleNamePtr to point to an empty string. |
---|
2006 | * |
---|
2007 | * If there is an error, this function returns TCL_ERROR. If "flags" |
---|
2008 | * contains TCL_LEAVE_ERR_MSG, an error message is returned in the |
---|
2009 | * interpreter's result object. Otherwise, the interpreter's result |
---|
2010 | * object is left unchanged. |
---|
2011 | * |
---|
2012 | * *actualCxtPtrPtr is set to the actual context namespace. It is set to |
---|
2013 | * the input context namespace pointer in cxtNsPtr. If cxtNsPtr is NULL, |
---|
2014 | * it is set to the current namespace context. |
---|
2015 | * |
---|
2016 | * For backwards compatibility with the TclPro byte code loader, this |
---|
2017 | * function always returns TCL_OK. |
---|
2018 | * |
---|
2019 | * Side effects: |
---|
2020 | * If "flags" contains TCL_CREATE_NS_IF_UNKNOWN, new namespaces may be |
---|
2021 | * created. |
---|
2022 | * |
---|
2023 | *---------------------------------------------------------------------- |
---|
2024 | */ |
---|
2025 | |
---|
2026 | int |
---|
2027 | TclGetNamespaceForQualName( |
---|
2028 | Tcl_Interp *interp, /* Interpreter in which to find the namespace |
---|
2029 | * containing qualName. */ |
---|
2030 | const char *qualName, /* A namespace-qualified name of an command, |
---|
2031 | * variable, or namespace. */ |
---|
2032 | Namespace *cxtNsPtr, /* The namespace in which to start the search |
---|
2033 | * for qualName's namespace. If NULL start |
---|
2034 | * from the current namespace. Ignored if |
---|
2035 | * TCL_GLOBAL_ONLY is set. */ |
---|
2036 | int flags, /* Flags controlling the search: an OR'd |
---|
2037 | * combination of TCL_GLOBAL_ONLY, |
---|
2038 | * TCL_NAMESPACE_ONLY, TCL_FIND_ONLY_NS, and |
---|
2039 | * TCL_CREATE_NS_IF_UNKNOWN. */ |
---|
2040 | Namespace **nsPtrPtr, /* Address where function stores a pointer to |
---|
2041 | * containing namespace if qualName is found |
---|
2042 | * starting from *cxtNsPtr or, if |
---|
2043 | * TCL_GLOBAL_ONLY is set, if qualName is |
---|
2044 | * found in the global :: namespace. NULL is |
---|
2045 | * stored otherwise. */ |
---|
2046 | Namespace **altNsPtrPtr, /* Address where function stores a pointer to |
---|
2047 | * containing namespace if qualName is found |
---|
2048 | * starting from the global :: namespace. |
---|
2049 | * NULL is stored if qualName isn't found |
---|
2050 | * starting from :: or if the TCL_GLOBAL_ONLY, |
---|
2051 | * TCL_NAMESPACE_ONLY, TCL_FIND_ONLY_NS, |
---|
2052 | * TCL_CREATE_NS_IF_UNKNOWN flag is set. */ |
---|
2053 | Namespace **actualCxtPtrPtr,/* Address where function stores a pointer to |
---|
2054 | * the actual namespace from which the search |
---|
2055 | * started. This is either cxtNsPtr, the :: |
---|
2056 | * namespace if TCL_GLOBAL_ONLY was specified, |
---|
2057 | * or the current namespace if cxtNsPtr was |
---|
2058 | * NULL. */ |
---|
2059 | const char **simpleNamePtr) /* Address where function stores the simple |
---|
2060 | * name at end of the qualName, or NULL if |
---|
2061 | * qualName is "::" or the flag |
---|
2062 | * TCL_FIND_ONLY_NS was specified. */ |
---|
2063 | { |
---|
2064 | Interp *iPtr = (Interp *) interp; |
---|
2065 | Namespace *nsPtr = cxtNsPtr; |
---|
2066 | Namespace *altNsPtr; |
---|
2067 | Namespace *globalNsPtr = iPtr->globalNsPtr; |
---|
2068 | const char *start, *end; |
---|
2069 | const char *nsName; |
---|
2070 | Tcl_HashEntry *entryPtr; |
---|
2071 | Tcl_DString buffer; |
---|
2072 | int len; |
---|
2073 | |
---|
2074 | /* |
---|
2075 | * Determine the context namespace nsPtr in which to start the primary |
---|
2076 | * search. If the qualName name starts with a "::" or TCL_GLOBAL_ONLY was |
---|
2077 | * specified, search from the global namespace. Otherwise, use the |
---|
2078 | * namespace given in cxtNsPtr, or if that is NULL, use the current |
---|
2079 | * namespace context. Note that we always treat two or more adjacent ":"s |
---|
2080 | * as a namespace separator. |
---|
2081 | */ |
---|
2082 | |
---|
2083 | if (flags & TCL_GLOBAL_ONLY) { |
---|
2084 | nsPtr = globalNsPtr; |
---|
2085 | } else if (nsPtr == NULL) { |
---|
2086 | nsPtr = iPtr->varFramePtr->nsPtr; |
---|
2087 | } |
---|
2088 | |
---|
2089 | start = qualName; /* Points to start of qualifying |
---|
2090 | * namespace. */ |
---|
2091 | if ((*qualName == ':') && (*(qualName+1) == ':')) { |
---|
2092 | start = qualName+2; /* Skip over the initial :: */ |
---|
2093 | while (*start == ':') { |
---|
2094 | start++; /* Skip over a subsequent : */ |
---|
2095 | } |
---|
2096 | nsPtr = globalNsPtr; |
---|
2097 | if (*start == '\0') { /* qualName is just two or more |
---|
2098 | * ":"s. */ |
---|
2099 | *nsPtrPtr = globalNsPtr; |
---|
2100 | *altNsPtrPtr = NULL; |
---|
2101 | *actualCxtPtrPtr = globalNsPtr; |
---|
2102 | *simpleNamePtr = start; /* Points to empty string. */ |
---|
2103 | return TCL_OK; |
---|
2104 | } |
---|
2105 | } |
---|
2106 | *actualCxtPtrPtr = nsPtr; |
---|
2107 | |
---|
2108 | /* |
---|
2109 | * Start an alternate search path starting with the global namespace. |
---|
2110 | * However, if the starting context is the global namespace, or if the |
---|
2111 | * flag is set to search only the namespace *cxtNsPtr, ignore the |
---|
2112 | * alternate search path. |
---|
2113 | */ |
---|
2114 | |
---|
2115 | altNsPtr = globalNsPtr; |
---|
2116 | if ((nsPtr == globalNsPtr) |
---|
2117 | || (flags & (TCL_NAMESPACE_ONLY | TCL_FIND_ONLY_NS))) { |
---|
2118 | altNsPtr = NULL; |
---|
2119 | } |
---|
2120 | |
---|
2121 | /* |
---|
2122 | * Loop to resolve each namespace qualifier in qualName. |
---|
2123 | */ |
---|
2124 | |
---|
2125 | Tcl_DStringInit(&buffer); |
---|
2126 | end = start; |
---|
2127 | while (*start != '\0') { |
---|
2128 | /* |
---|
2129 | * Find the next namespace qualifier (i.e., a name ending in "::") or |
---|
2130 | * the end of the qualified name (i.e., a name ending in "\0"). Set |
---|
2131 | * len to the number of characters, starting from start, in the name; |
---|
2132 | * set end to point after the "::"s or at the "\0". |
---|
2133 | */ |
---|
2134 | |
---|
2135 | len = 0; |
---|
2136 | for (end = start; *end != '\0'; end++) { |
---|
2137 | if ((*end == ':') && (*(end+1) == ':')) { |
---|
2138 | end += 2; /* Skip over the initial :: */ |
---|
2139 | while (*end == ':') { |
---|
2140 | end++; /* Skip over the subsequent : */ |
---|
2141 | } |
---|
2142 | break; /* Exit for loop; end is after ::'s */ |
---|
2143 | } |
---|
2144 | len++; |
---|
2145 | } |
---|
2146 | |
---|
2147 | if (*end=='\0' && !(end-start>=2 && *(end-1)==':' && *(end-2)==':')) { |
---|
2148 | /* |
---|
2149 | * qualName ended with a simple name at start. If TCL_FIND_ONLY_NS |
---|
2150 | * was specified, look this up as a namespace. Otherwise, start is |
---|
2151 | * the name of a cmd or var and we are done. |
---|
2152 | */ |
---|
2153 | |
---|
2154 | if (flags & TCL_FIND_ONLY_NS) { |
---|
2155 | nsName = start; |
---|
2156 | } else { |
---|
2157 | *nsPtrPtr = nsPtr; |
---|
2158 | *altNsPtrPtr = altNsPtr; |
---|
2159 | *simpleNamePtr = start; |
---|
2160 | Tcl_DStringFree(&buffer); |
---|
2161 | return TCL_OK; |
---|
2162 | } |
---|
2163 | } else { |
---|
2164 | /* |
---|
2165 | * start points to the beginning of a namespace qualifier ending |
---|
2166 | * in "::". end points to the start of a name in that namespace |
---|
2167 | * that might be empty. Copy the namespace qualifier to a buffer |
---|
2168 | * so it can be null terminated. We can't modify the incoming |
---|
2169 | * qualName since it may be a string constant. |
---|
2170 | */ |
---|
2171 | |
---|
2172 | Tcl_DStringSetLength(&buffer, 0); |
---|
2173 | Tcl_DStringAppend(&buffer, start, len); |
---|
2174 | nsName = Tcl_DStringValue(&buffer); |
---|
2175 | } |
---|
2176 | |
---|
2177 | /* |
---|
2178 | * Look up the namespace qualifier nsName in the current namespace |
---|
2179 | * context. If it isn't found but TCL_CREATE_NS_IF_UNKNOWN is set, |
---|
2180 | * create that qualifying namespace. This is needed for functions like |
---|
2181 | * Tcl_CreateCommand that cannot fail. |
---|
2182 | */ |
---|
2183 | |
---|
2184 | if (nsPtr != NULL) { |
---|
2185 | entryPtr = Tcl_FindHashEntry(&nsPtr->childTable, nsName); |
---|
2186 | if (entryPtr != NULL) { |
---|
2187 | nsPtr = Tcl_GetHashValue(entryPtr); |
---|
2188 | } else if (flags & TCL_CREATE_NS_IF_UNKNOWN) { |
---|
2189 | Tcl_CallFrame *framePtr; |
---|
2190 | |
---|
2191 | (void) TclPushStackFrame(interp, &framePtr, |
---|
2192 | (Tcl_Namespace *) nsPtr, /*isProcCallFrame*/ 0); |
---|
2193 | |
---|
2194 | nsPtr = (Namespace *) Tcl_CreateNamespace(interp, nsName, |
---|
2195 | NULL, NULL); |
---|
2196 | TclPopStackFrame(interp); |
---|
2197 | |
---|
2198 | if (nsPtr == NULL) { |
---|
2199 | Tcl_Panic("Could not create namespace '%s'", nsName); |
---|
2200 | } |
---|
2201 | } else { /* Namespace not found and was not |
---|
2202 | * created. */ |
---|
2203 | nsPtr = NULL; |
---|
2204 | } |
---|
2205 | } |
---|
2206 | |
---|
2207 | /* |
---|
2208 | * Look up the namespace qualifier in the alternate search path too. |
---|
2209 | */ |
---|
2210 | |
---|
2211 | if (altNsPtr != NULL) { |
---|
2212 | entryPtr = Tcl_FindHashEntry(&altNsPtr->childTable, nsName); |
---|
2213 | if (entryPtr != NULL) { |
---|
2214 | altNsPtr = Tcl_GetHashValue(entryPtr); |
---|
2215 | } else { |
---|
2216 | altNsPtr = NULL; |
---|
2217 | } |
---|
2218 | } |
---|
2219 | |
---|
2220 | /* |
---|
2221 | * If both search paths have failed, return NULL results. |
---|
2222 | */ |
---|
2223 | |
---|
2224 | if ((nsPtr == NULL) && (altNsPtr == NULL)) { |
---|
2225 | *nsPtrPtr = NULL; |
---|
2226 | *altNsPtrPtr = NULL; |
---|
2227 | *simpleNamePtr = NULL; |
---|
2228 | Tcl_DStringFree(&buffer); |
---|
2229 | return TCL_OK; |
---|
2230 | } |
---|
2231 | |
---|
2232 | start = end; |
---|
2233 | } |
---|
2234 | |
---|
2235 | /* |
---|
2236 | * We ignore trailing "::"s in a namespace name, but in a command or |
---|
2237 | * variable name, trailing "::"s refer to the cmd or var named {}. |
---|
2238 | */ |
---|
2239 | |
---|
2240 | if ((flags & TCL_FIND_ONLY_NS) || (end>start && *(end-1)!=':')) { |
---|
2241 | *simpleNamePtr = NULL; /* Found namespace name. */ |
---|
2242 | } else { |
---|
2243 | *simpleNamePtr = end; /* Found cmd/var: points to empty |
---|
2244 | * string. */ |
---|
2245 | } |
---|
2246 | |
---|
2247 | /* |
---|
2248 | * As a special case, if we are looking for a namespace and qualName is "" |
---|
2249 | * and the current active namespace (nsPtr) is not the global namespace, |
---|
2250 | * return NULL (no namespace was found). This is because namespaces can |
---|
2251 | * not have empty names except for the global namespace. |
---|
2252 | */ |
---|
2253 | |
---|
2254 | if ((flags & TCL_FIND_ONLY_NS) && (*qualName == '\0') |
---|
2255 | && (nsPtr != globalNsPtr)) { |
---|
2256 | nsPtr = NULL; |
---|
2257 | } |
---|
2258 | |
---|
2259 | *nsPtrPtr = nsPtr; |
---|
2260 | *altNsPtrPtr = altNsPtr; |
---|
2261 | Tcl_DStringFree(&buffer); |
---|
2262 | return TCL_OK; |
---|
2263 | } |
---|
2264 | |
---|
2265 | /* |
---|
2266 | *---------------------------------------------------------------------- |
---|
2267 | * |
---|
2268 | * Tcl_FindNamespace -- |
---|
2269 | * |
---|
2270 | * Searches for a namespace. |
---|
2271 | * |
---|
2272 | * Results: |
---|
2273 | * Returns a pointer to the namespace if it is found. Otherwise, returns |
---|
2274 | * NULL and leaves an error message in the interpreter's result object if |
---|
2275 | * "flags" contains TCL_LEAVE_ERR_MSG. |
---|
2276 | * |
---|
2277 | * Side effects: |
---|
2278 | * None. |
---|
2279 | * |
---|
2280 | *---------------------------------------------------------------------- |
---|
2281 | */ |
---|
2282 | |
---|
2283 | Tcl_Namespace * |
---|
2284 | Tcl_FindNamespace( |
---|
2285 | Tcl_Interp *interp, /* The interpreter in which to find the |
---|
2286 | * namespace. */ |
---|
2287 | const char *name, /* Namespace name. If it starts with "::", |
---|
2288 | * will be looked up in global namespace. |
---|
2289 | * Else, looked up first in contextNsPtr |
---|
2290 | * (current namespace if contextNsPtr is |
---|
2291 | * NULL), then in global namespace. */ |
---|
2292 | Tcl_Namespace *contextNsPtr,/* Ignored if TCL_GLOBAL_ONLY flag is set or |
---|
2293 | * if the name starts with "::". Otherwise, |
---|
2294 | * points to namespace in which to resolve |
---|
2295 | * name; if NULL, look up name in the current |
---|
2296 | * namespace. */ |
---|
2297 | register int flags) /* Flags controlling namespace lookup: an OR'd |
---|
2298 | * combination of TCL_GLOBAL_ONLY and |
---|
2299 | * TCL_LEAVE_ERR_MSG flags. */ |
---|
2300 | { |
---|
2301 | Namespace *nsPtr, *dummy1Ptr, *dummy2Ptr; |
---|
2302 | const char *dummy; |
---|
2303 | |
---|
2304 | /* |
---|
2305 | * Find the namespace(s) that contain the specified namespace name. Add |
---|
2306 | * the TCL_FIND_ONLY_NS flag to resolve the name all the way down to its |
---|
2307 | * last component, a namespace. |
---|
2308 | */ |
---|
2309 | |
---|
2310 | TclGetNamespaceForQualName(interp, name, (Namespace *) contextNsPtr, |
---|
2311 | flags|TCL_FIND_ONLY_NS, &nsPtr, &dummy1Ptr, &dummy2Ptr, &dummy); |
---|
2312 | |
---|
2313 | if (nsPtr != NULL) { |
---|
2314 | return (Tcl_Namespace *) nsPtr; |
---|
2315 | } else if (flags & TCL_LEAVE_ERR_MSG) { |
---|
2316 | Tcl_ResetResult(interp); |
---|
2317 | Tcl_AppendResult(interp, "unknown namespace \"", name, "\"", NULL); |
---|
2318 | Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "NAMESPACE", name, NULL); |
---|
2319 | } |
---|
2320 | return NULL; |
---|
2321 | } |
---|
2322 | |
---|
2323 | /* |
---|
2324 | *---------------------------------------------------------------------- |
---|
2325 | * |
---|
2326 | * Tcl_FindCommand -- |
---|
2327 | * |
---|
2328 | * Searches for a command. |
---|
2329 | * |
---|
2330 | * Results: |
---|
2331 | * Returns a token for the command if it is found. Otherwise, if it can't |
---|
2332 | * be found or there is an error, returns NULL and leaves an error |
---|
2333 | * message in the interpreter's result object if "flags" contains |
---|
2334 | * TCL_LEAVE_ERR_MSG. |
---|
2335 | * |
---|
2336 | * Side effects: |
---|
2337 | * None. |
---|
2338 | * |
---|
2339 | *---------------------------------------------------------------------- |
---|
2340 | */ |
---|
2341 | |
---|
2342 | Tcl_Command |
---|
2343 | Tcl_FindCommand( |
---|
2344 | Tcl_Interp *interp, /* The interpreter in which to find the |
---|
2345 | * command and to report errors. */ |
---|
2346 | const char *name, /* Command's name. If it starts with "::", |
---|
2347 | * will be looked up in global namespace. |
---|
2348 | * Else, looked up first in contextNsPtr |
---|
2349 | * (current namespace if contextNsPtr is |
---|
2350 | * NULL), then in global namespace. */ |
---|
2351 | Tcl_Namespace *contextNsPtr,/* Ignored if TCL_GLOBAL_ONLY flag set. |
---|
2352 | * Otherwise, points to namespace in which to |
---|
2353 | * resolve name. If NULL, look up name in the |
---|
2354 | * current namespace. */ |
---|
2355 | int flags) /* An OR'd combination of flags: |
---|
2356 | * TCL_GLOBAL_ONLY (look up name only in |
---|
2357 | * global namespace), TCL_NAMESPACE_ONLY (look |
---|
2358 | * up only in contextNsPtr, or the current |
---|
2359 | * namespace if contextNsPtr is NULL), and |
---|
2360 | * TCL_LEAVE_ERR_MSG. If both TCL_GLOBAL_ONLY |
---|
2361 | * and TCL_NAMESPACE_ONLY are given, |
---|
2362 | * TCL_GLOBAL_ONLY is ignored. */ |
---|
2363 | { |
---|
2364 | Interp *iPtr = (Interp *) interp; |
---|
2365 | Namespace *cxtNsPtr; |
---|
2366 | register Tcl_HashEntry *entryPtr; |
---|
2367 | register Command *cmdPtr; |
---|
2368 | const char *simpleName; |
---|
2369 | int result; |
---|
2370 | |
---|
2371 | /* |
---|
2372 | * If this namespace has a command resolver, then give it first crack at |
---|
2373 | * the command resolution. If the interpreter has any command resolvers, |
---|
2374 | * consult them next. The command resolver functions may return a |
---|
2375 | * Tcl_Command value, they may signal to continue onward, or they may |
---|
2376 | * signal an error. |
---|
2377 | */ |
---|
2378 | |
---|
2379 | if ((flags & TCL_GLOBAL_ONLY) || !strncmp(name, "::", 2)) { |
---|
2380 | cxtNsPtr = (Namespace *) TclGetGlobalNamespace(interp); |
---|
2381 | } else if (contextNsPtr != NULL) { |
---|
2382 | cxtNsPtr = (Namespace *) contextNsPtr; |
---|
2383 | } else { |
---|
2384 | cxtNsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
2385 | } |
---|
2386 | |
---|
2387 | if (cxtNsPtr->cmdResProc != NULL || iPtr->resolverPtr != NULL) { |
---|
2388 | ResolverScheme *resPtr = iPtr->resolverPtr; |
---|
2389 | Tcl_Command cmd; |
---|
2390 | |
---|
2391 | if (cxtNsPtr->cmdResProc) { |
---|
2392 | result = (*cxtNsPtr->cmdResProc)(interp, name, |
---|
2393 | (Tcl_Namespace *) cxtNsPtr, flags, &cmd); |
---|
2394 | } else { |
---|
2395 | result = TCL_CONTINUE; |
---|
2396 | } |
---|
2397 | |
---|
2398 | while (result == TCL_CONTINUE && resPtr) { |
---|
2399 | if (resPtr->cmdResProc) { |
---|
2400 | result = (*resPtr->cmdResProc)(interp, name, |
---|
2401 | (Tcl_Namespace *) cxtNsPtr, flags, &cmd); |
---|
2402 | } |
---|
2403 | resPtr = resPtr->nextPtr; |
---|
2404 | } |
---|
2405 | |
---|
2406 | if (result == TCL_OK) { |
---|
2407 | return cmd; |
---|
2408 | } else if (result != TCL_CONTINUE) { |
---|
2409 | return NULL; |
---|
2410 | } |
---|
2411 | } |
---|
2412 | |
---|
2413 | /* |
---|
2414 | * Find the namespace(s) that contain the command. |
---|
2415 | */ |
---|
2416 | |
---|
2417 | cmdPtr = NULL; |
---|
2418 | if (cxtNsPtr->commandPathLength!=0 && strncmp(name, "::", 2)) { |
---|
2419 | int i; |
---|
2420 | Namespace *pathNsPtr, *realNsPtr, *dummyNsPtr; |
---|
2421 | |
---|
2422 | (void) TclGetNamespaceForQualName(interp, name, cxtNsPtr, |
---|
2423 | TCL_NAMESPACE_ONLY, &realNsPtr, &dummyNsPtr, &dummyNsPtr, |
---|
2424 | &simpleName); |
---|
2425 | if ((realNsPtr != NULL) && (simpleName != NULL)) { |
---|
2426 | if ((cxtNsPtr == realNsPtr) |
---|
2427 | || !(realNsPtr->flags & NS_DYING)) { |
---|
2428 | entryPtr = Tcl_FindHashEntry(&realNsPtr->cmdTable, simpleName); |
---|
2429 | if (entryPtr != NULL) { |
---|
2430 | cmdPtr = Tcl_GetHashValue(entryPtr); |
---|
2431 | } |
---|
2432 | } |
---|
2433 | } |
---|
2434 | |
---|
2435 | /* |
---|
2436 | * Next, check along the path. |
---|
2437 | */ |
---|
2438 | |
---|
2439 | for (i=0 ; i<cxtNsPtr->commandPathLength && cmdPtr==NULL ; i++) { |
---|
2440 | pathNsPtr = cxtNsPtr->commandPathArray[i].nsPtr; |
---|
2441 | if (pathNsPtr == NULL) { |
---|
2442 | continue; |
---|
2443 | } |
---|
2444 | (void) TclGetNamespaceForQualName(interp, name, pathNsPtr, |
---|
2445 | TCL_NAMESPACE_ONLY, &realNsPtr, &dummyNsPtr, &dummyNsPtr, |
---|
2446 | &simpleName); |
---|
2447 | if ((realNsPtr != NULL) && (simpleName != NULL) |
---|
2448 | && !(realNsPtr->flags & NS_DYING)) { |
---|
2449 | entryPtr = Tcl_FindHashEntry(&realNsPtr->cmdTable, simpleName); |
---|
2450 | if (entryPtr != NULL) { |
---|
2451 | cmdPtr = Tcl_GetHashValue(entryPtr); |
---|
2452 | } |
---|
2453 | } |
---|
2454 | } |
---|
2455 | |
---|
2456 | /* |
---|
2457 | * If we've still not found the command, look in the global namespace |
---|
2458 | * as a last resort. |
---|
2459 | */ |
---|
2460 | |
---|
2461 | if (cmdPtr == NULL) { |
---|
2462 | (void) TclGetNamespaceForQualName(interp, name, NULL, |
---|
2463 | TCL_GLOBAL_ONLY, &realNsPtr, &dummyNsPtr, &dummyNsPtr, |
---|
2464 | &simpleName); |
---|
2465 | if ((realNsPtr != NULL) && (simpleName != NULL) |
---|
2466 | && !(realNsPtr->flags & NS_DYING)) { |
---|
2467 | entryPtr = Tcl_FindHashEntry(&realNsPtr->cmdTable, simpleName); |
---|
2468 | if (entryPtr != NULL) { |
---|
2469 | cmdPtr = Tcl_GetHashValue(entryPtr); |
---|
2470 | } |
---|
2471 | } |
---|
2472 | } |
---|
2473 | } else { |
---|
2474 | Namespace *nsPtr[2]; |
---|
2475 | register int search; |
---|
2476 | |
---|
2477 | TclGetNamespaceForQualName(interp, name, (Namespace *) contextNsPtr, |
---|
2478 | flags, &nsPtr[0], &nsPtr[1], &cxtNsPtr, &simpleName); |
---|
2479 | |
---|
2480 | /* |
---|
2481 | * Look for the command in the command table of its namespace. Be sure |
---|
2482 | * to check both possible search paths: from the specified namespace |
---|
2483 | * context and from the global namespace. |
---|
2484 | */ |
---|
2485 | |
---|
2486 | for (search = 0; (search < 2) && (cmdPtr == NULL); search++) { |
---|
2487 | if ((nsPtr[search] != NULL) && (simpleName != NULL)) { |
---|
2488 | entryPtr = Tcl_FindHashEntry(&nsPtr[search]->cmdTable, |
---|
2489 | simpleName); |
---|
2490 | if (entryPtr != NULL) { |
---|
2491 | cmdPtr = Tcl_GetHashValue(entryPtr); |
---|
2492 | } |
---|
2493 | } |
---|
2494 | } |
---|
2495 | } |
---|
2496 | |
---|
2497 | if (cmdPtr != NULL) { |
---|
2498 | return (Tcl_Command) cmdPtr; |
---|
2499 | } |
---|
2500 | |
---|
2501 | if (flags & TCL_LEAVE_ERR_MSG) { |
---|
2502 | Tcl_ResetResult(interp); |
---|
2503 | Tcl_AppendResult(interp, "unknown command \"", name, "\"", NULL); |
---|
2504 | Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "COMMAND", name, NULL); |
---|
2505 | } |
---|
2506 | return NULL; |
---|
2507 | } |
---|
2508 | |
---|
2509 | /* |
---|
2510 | *---------------------------------------------------------------------- |
---|
2511 | * |
---|
2512 | * TclResetShadowedCmdRefs -- |
---|
2513 | * |
---|
2514 | * Called when a command is added to a namespace to check for existing |
---|
2515 | * command references that the new command may invalidate. Consider the |
---|
2516 | * following cases that could happen when you add a command "foo" to a |
---|
2517 | * namespace "b": |
---|
2518 | * 1. It could shadow a command named "foo" at the global scope. If |
---|
2519 | * it does, all command references in the namespace "b" are |
---|
2520 | * suspect. |
---|
2521 | * 2. Suppose the namespace "b" resides in a namespace "a". Then to |
---|
2522 | * "a" the new command "b::foo" could shadow another command |
---|
2523 | * "b::foo" in the global namespace. If so, then all command |
---|
2524 | * references in "a" * are suspect. |
---|
2525 | * The same checks are applied to all parent namespaces, until we reach |
---|
2526 | * the global :: namespace. |
---|
2527 | * |
---|
2528 | * Results: |
---|
2529 | * None. |
---|
2530 | * |
---|
2531 | * Side effects: |
---|
2532 | * If the new command shadows an existing command, the cmdRefEpoch |
---|
2533 | * counter is incremented in each namespace that sees the shadow. This |
---|
2534 | * invalidates all command references that were previously cached in that |
---|
2535 | * namespace. The next time the commands are used, they are resolved from |
---|
2536 | * scratch. |
---|
2537 | * |
---|
2538 | *---------------------------------------------------------------------- |
---|
2539 | */ |
---|
2540 | |
---|
2541 | void |
---|
2542 | TclResetShadowedCmdRefs( |
---|
2543 | Tcl_Interp *interp, /* Interpreter containing the new command. */ |
---|
2544 | Command *newCmdPtr) /* Points to the new command. */ |
---|
2545 | { |
---|
2546 | char *cmdName; |
---|
2547 | Tcl_HashEntry *hPtr; |
---|
2548 | register Namespace *nsPtr; |
---|
2549 | Namespace *trailNsPtr, *shadowNsPtr; |
---|
2550 | Namespace *globalNsPtr = (Namespace *) TclGetGlobalNamespace(interp); |
---|
2551 | int found, i; |
---|
2552 | int trailFront = -1; |
---|
2553 | int trailSize = 5; /* Formerly NUM_TRAIL_ELEMS. */ |
---|
2554 | Namespace **trailPtr = (Namespace **) |
---|
2555 | TclStackAlloc(interp, trailSize * sizeof(Namespace *)); |
---|
2556 | |
---|
2557 | /* |
---|
2558 | * Start at the namespace containing the new command, and work up through |
---|
2559 | * the list of parents. Stop just before the global namespace, since the |
---|
2560 | * global namespace can't "shadow" its own entries. |
---|
2561 | * |
---|
2562 | * The namespace "trail" list we build consists of the names of each |
---|
2563 | * namespace that encloses the new command, in order from outermost to |
---|
2564 | * innermost: for example, "a" then "b". Each iteration of this loop |
---|
2565 | * eventually extends the trail upwards by one namespace, nsPtr. We use |
---|
2566 | * this trail list to see if nsPtr (e.g. "a" in 2. above) could have |
---|
2567 | * now-invalid cached command references. This will happen if nsPtr |
---|
2568 | * (e.g. "a") contains a sequence of child namespaces (e.g. "b") such that |
---|
2569 | * there is a identically-named sequence of child namespaces starting from |
---|
2570 | * :: (e.g. "::b") whose tail namespace contains a command also named |
---|
2571 | * cmdName. |
---|
2572 | */ |
---|
2573 | |
---|
2574 | cmdName = Tcl_GetHashKey(newCmdPtr->hPtr->tablePtr, newCmdPtr->hPtr); |
---|
2575 | for (nsPtr=newCmdPtr->nsPtr ; (nsPtr!=NULL) && (nsPtr!=globalNsPtr) ; |
---|
2576 | nsPtr=nsPtr->parentPtr) { |
---|
2577 | /* |
---|
2578 | * Find the maximal sequence of child namespaces contained in nsPtr |
---|
2579 | * such that there is a identically-named sequence of child namespaces |
---|
2580 | * starting from ::. shadowNsPtr will be the tail of this sequence, or |
---|
2581 | * the deepest namespace under :: that might contain a command now |
---|
2582 | * shadowed by cmdName. We check below if shadowNsPtr actually |
---|
2583 | * contains a command cmdName. |
---|
2584 | */ |
---|
2585 | |
---|
2586 | found = 1; |
---|
2587 | shadowNsPtr = globalNsPtr; |
---|
2588 | |
---|
2589 | for (i = trailFront; i >= 0; i--) { |
---|
2590 | trailNsPtr = trailPtr[i]; |
---|
2591 | hPtr = Tcl_FindHashEntry(&shadowNsPtr->childTable, |
---|
2592 | trailNsPtr->name); |
---|
2593 | if (hPtr != NULL) { |
---|
2594 | shadowNsPtr = Tcl_GetHashValue(hPtr); |
---|
2595 | } else { |
---|
2596 | found = 0; |
---|
2597 | break; |
---|
2598 | } |
---|
2599 | } |
---|
2600 | |
---|
2601 | /* |
---|
2602 | * If shadowNsPtr contains a command named cmdName, we invalidate all |
---|
2603 | * of the command refs cached in nsPtr. As a boundary case, |
---|
2604 | * shadowNsPtr is initially :: and we check for case 1. above. |
---|
2605 | */ |
---|
2606 | |
---|
2607 | if (found) { |
---|
2608 | hPtr = Tcl_FindHashEntry(&shadowNsPtr->cmdTable, cmdName); |
---|
2609 | if (hPtr != NULL) { |
---|
2610 | nsPtr->cmdRefEpoch++; |
---|
2611 | TclInvalidateNsPath(nsPtr); |
---|
2612 | |
---|
2613 | /* |
---|
2614 | * If the shadowed command was compiled to bytecodes, we |
---|
2615 | * invalidate all the bytecodes in nsPtr, to force a new |
---|
2616 | * compilation. We use the resolverEpoch to signal the need |
---|
2617 | * for a fresh compilation of every bytecode. |
---|
2618 | */ |
---|
2619 | |
---|
2620 | if (((Command *)Tcl_GetHashValue(hPtr))->compileProc != NULL) { |
---|
2621 | nsPtr->resolverEpoch++; |
---|
2622 | } |
---|
2623 | } |
---|
2624 | } |
---|
2625 | |
---|
2626 | /* |
---|
2627 | * Insert nsPtr at the front of the trail list: i.e., at the end of |
---|
2628 | * the trailPtr array. |
---|
2629 | */ |
---|
2630 | |
---|
2631 | trailFront++; |
---|
2632 | if (trailFront == trailSize) { |
---|
2633 | int newSize = 2 * trailSize; |
---|
2634 | trailPtr = (Namespace **) TclStackRealloc(interp, |
---|
2635 | trailPtr, newSize * sizeof(Namespace *)); |
---|
2636 | trailSize = newSize; |
---|
2637 | } |
---|
2638 | trailPtr[trailFront] = nsPtr; |
---|
2639 | } |
---|
2640 | TclStackFree(interp, trailPtr); |
---|
2641 | } |
---|
2642 | |
---|
2643 | /* |
---|
2644 | *---------------------------------------------------------------------- |
---|
2645 | * |
---|
2646 | * TclGetNamespaceFromObj, GetNamespaceFromObj -- |
---|
2647 | * |
---|
2648 | * Gets the namespace specified by the name in a Tcl_Obj. |
---|
2649 | * |
---|
2650 | * Results: |
---|
2651 | * Returns TCL_OK if the namespace was resolved successfully, and stores |
---|
2652 | * a pointer to the namespace in the location specified by nsPtrPtr. If |
---|
2653 | * the namespace can't be found, or anything else goes wrong, this |
---|
2654 | * function returns TCL_ERROR and writes an error message to interp, |
---|
2655 | * if non-NULL. |
---|
2656 | * |
---|
2657 | * Side effects: |
---|
2658 | * May update the internal representation for the object, caching the |
---|
2659 | * namespace reference. The next time this function is called, the |
---|
2660 | * namespace value can be found quickly. |
---|
2661 | * |
---|
2662 | *---------------------------------------------------------------------- |
---|
2663 | */ |
---|
2664 | |
---|
2665 | int |
---|
2666 | TclGetNamespaceFromObj( |
---|
2667 | Tcl_Interp *interp, /* The current interpreter. */ |
---|
2668 | Tcl_Obj *objPtr, /* The object to be resolved as the name of a |
---|
2669 | * namespace. */ |
---|
2670 | Tcl_Namespace **nsPtrPtr) /* Result namespace pointer goes here. */ |
---|
2671 | { |
---|
2672 | if (GetNamespaceFromObj(interp, objPtr, nsPtrPtr) == TCL_ERROR) { |
---|
2673 | const char *name = TclGetString(objPtr); |
---|
2674 | |
---|
2675 | if ((name[0] == ':') && (name[1] == ':')) { |
---|
2676 | Tcl_SetObjResult(interp, Tcl_ObjPrintf( |
---|
2677 | "namespace \"%s\" not found", name)); |
---|
2678 | } else { |
---|
2679 | /* |
---|
2680 | * Get the current namespace name. |
---|
2681 | */ |
---|
2682 | |
---|
2683 | NamespaceCurrentCmd(NULL, interp, 2, NULL); |
---|
2684 | Tcl_SetObjResult(interp, Tcl_ObjPrintf( |
---|
2685 | "namespace \"%s\" not found in \"%s\"", name, |
---|
2686 | Tcl_GetStringResult(interp))); |
---|
2687 | } |
---|
2688 | Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "NAMESPACE", name, NULL); |
---|
2689 | return TCL_ERROR; |
---|
2690 | } |
---|
2691 | return TCL_OK; |
---|
2692 | } |
---|
2693 | |
---|
2694 | static int |
---|
2695 | GetNamespaceFromObj( |
---|
2696 | Tcl_Interp *interp, /* The current interpreter. */ |
---|
2697 | Tcl_Obj *objPtr, /* The object to be resolved as the name of a |
---|
2698 | * namespace. */ |
---|
2699 | Tcl_Namespace **nsPtrPtr) /* Result namespace pointer goes here. */ |
---|
2700 | { |
---|
2701 | ResolvedNsName *resNamePtr; |
---|
2702 | Namespace *nsPtr, *refNsPtr; |
---|
2703 | |
---|
2704 | if (objPtr->typePtr == &nsNameType) { |
---|
2705 | /* |
---|
2706 | * Check that the ResolvedNsName is still valid; avoid letting the ref |
---|
2707 | * cross interps. |
---|
2708 | */ |
---|
2709 | |
---|
2710 | resNamePtr = (ResolvedNsName *) objPtr->internalRep.twoPtrValue.ptr1; |
---|
2711 | nsPtr = resNamePtr->nsPtr; |
---|
2712 | refNsPtr = resNamePtr->refNsPtr; |
---|
2713 | if (!(nsPtr->flags & NS_DYING) && (interp == nsPtr->interp) && |
---|
2714 | (!refNsPtr || ((interp == refNsPtr->interp) && |
---|
2715 | (refNsPtr== (Namespace *) Tcl_GetCurrentNamespace(interp))))) { |
---|
2716 | *nsPtrPtr = (Tcl_Namespace *) nsPtr; |
---|
2717 | return TCL_OK; |
---|
2718 | } |
---|
2719 | } |
---|
2720 | if (SetNsNameFromAny(interp, objPtr) == TCL_OK) { |
---|
2721 | resNamePtr = (ResolvedNsName *) objPtr->internalRep.twoPtrValue.ptr1; |
---|
2722 | *nsPtrPtr = (Tcl_Namespace *) resNamePtr->nsPtr; |
---|
2723 | return TCL_OK; |
---|
2724 | } |
---|
2725 | return TCL_ERROR; |
---|
2726 | } |
---|
2727 | |
---|
2728 | /* |
---|
2729 | *---------------------------------------------------------------------- |
---|
2730 | * |
---|
2731 | * Tcl_NamespaceObjCmd -- |
---|
2732 | * |
---|
2733 | * Invoked to implement the "namespace" command that creates, deletes, or |
---|
2734 | * manipulates Tcl namespaces. Handles the following syntax: |
---|
2735 | * |
---|
2736 | * namespace children ?name? ?pattern? |
---|
2737 | * namespace code arg |
---|
2738 | * namespace current |
---|
2739 | * namespace delete ?name name...? |
---|
2740 | * namespace ensemble subcommand ?arg...? |
---|
2741 | * namespace eval name arg ?arg...? |
---|
2742 | * namespace exists name |
---|
2743 | * namespace export ?-clear? ?pattern pattern...? |
---|
2744 | * namespace forget ?pattern pattern...? |
---|
2745 | * namespace import ?-force? ?pattern pattern...? |
---|
2746 | * namespace inscope name arg ?arg...? |
---|
2747 | * namespace origin name |
---|
2748 | * namespace parent ?name? |
---|
2749 | * namespace qualifiers string |
---|
2750 | * namespace tail string |
---|
2751 | * namespace which ?-command? ?-variable? name |
---|
2752 | * |
---|
2753 | * Results: |
---|
2754 | * Returns TCL_OK if the command is successful. Returns TCL_ERROR if |
---|
2755 | * anything goes wrong. |
---|
2756 | * |
---|
2757 | * Side effects: |
---|
2758 | * Based on the subcommand name (e.g., "import"), this function |
---|
2759 | * dispatches to a corresponding function NamespaceXXXCmd defined |
---|
2760 | * statically in this file. This function's side effects depend on |
---|
2761 | * whatever that subcommand function does. If there is an error, this |
---|
2762 | * function returns an error message in the interpreter's result object. |
---|
2763 | * Otherwise it may return a result in the interpreter's result object. |
---|
2764 | * |
---|
2765 | *---------------------------------------------------------------------- |
---|
2766 | */ |
---|
2767 | |
---|
2768 | int |
---|
2769 | Tcl_NamespaceObjCmd( |
---|
2770 | ClientData clientData, /* Arbitrary value passed to cmd. */ |
---|
2771 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
2772 | int objc, /* Number of arguments. */ |
---|
2773 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
2774 | { |
---|
2775 | static const char *subCmds[] = { |
---|
2776 | "children", "code", "current", "delete", "ensemble", |
---|
2777 | "eval", "exists", "export", "forget", "import", |
---|
2778 | "inscope", "origin", "parent", "path", "qualifiers", |
---|
2779 | "tail", "unknown", "upvar", "which", NULL |
---|
2780 | }; |
---|
2781 | enum NSSubCmdIdx { |
---|
2782 | NSChildrenIdx, NSCodeIdx, NSCurrentIdx, NSDeleteIdx, NSEnsembleIdx, |
---|
2783 | NSEvalIdx, NSExistsIdx, NSExportIdx, NSForgetIdx, NSImportIdx, |
---|
2784 | NSInscopeIdx, NSOriginIdx, NSParentIdx, NSPathIdx, NSQualifiersIdx, |
---|
2785 | NSTailIdx, NSUnknownIdx, NSUpvarIdx, NSWhichIdx |
---|
2786 | }; |
---|
2787 | int index, result; |
---|
2788 | |
---|
2789 | if (objc < 2) { |
---|
2790 | Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?arg ...?"); |
---|
2791 | return TCL_ERROR; |
---|
2792 | } |
---|
2793 | |
---|
2794 | /* |
---|
2795 | * Return an index reflecting the particular subcommand. |
---|
2796 | */ |
---|
2797 | |
---|
2798 | result = Tcl_GetIndexFromObj((Tcl_Interp *) interp, objv[1], subCmds, |
---|
2799 | "option", /*flags*/ 0, (int *) &index); |
---|
2800 | if (result != TCL_OK) { |
---|
2801 | return result; |
---|
2802 | } |
---|
2803 | |
---|
2804 | switch (index) { |
---|
2805 | case NSChildrenIdx: |
---|
2806 | result = NamespaceChildrenCmd(clientData, interp, objc, objv); |
---|
2807 | break; |
---|
2808 | case NSCodeIdx: |
---|
2809 | result = NamespaceCodeCmd(clientData, interp, objc, objv); |
---|
2810 | break; |
---|
2811 | case NSCurrentIdx: |
---|
2812 | result = NamespaceCurrentCmd(clientData, interp, objc, objv); |
---|
2813 | break; |
---|
2814 | case NSDeleteIdx: |
---|
2815 | result = NamespaceDeleteCmd(clientData, interp, objc, objv); |
---|
2816 | break; |
---|
2817 | case NSEnsembleIdx: |
---|
2818 | result = NamespaceEnsembleCmd(clientData, interp, objc, objv); |
---|
2819 | break; |
---|
2820 | case NSEvalIdx: |
---|
2821 | result = NamespaceEvalCmd(clientData, interp, objc, objv); |
---|
2822 | break; |
---|
2823 | case NSExistsIdx: |
---|
2824 | result = NamespaceExistsCmd(clientData, interp, objc, objv); |
---|
2825 | break; |
---|
2826 | case NSExportIdx: |
---|
2827 | result = NamespaceExportCmd(clientData, interp, objc, objv); |
---|
2828 | break; |
---|
2829 | case NSForgetIdx: |
---|
2830 | result = NamespaceForgetCmd(clientData, interp, objc, objv); |
---|
2831 | break; |
---|
2832 | case NSImportIdx: |
---|
2833 | result = NamespaceImportCmd(clientData, interp, objc, objv); |
---|
2834 | break; |
---|
2835 | case NSInscopeIdx: |
---|
2836 | result = NamespaceInscopeCmd(clientData, interp, objc, objv); |
---|
2837 | break; |
---|
2838 | case NSOriginIdx: |
---|
2839 | result = NamespaceOriginCmd(clientData, interp, objc, objv); |
---|
2840 | break; |
---|
2841 | case NSParentIdx: |
---|
2842 | result = NamespaceParentCmd(clientData, interp, objc, objv); |
---|
2843 | break; |
---|
2844 | case NSPathIdx: |
---|
2845 | result = NamespacePathCmd(clientData, interp, objc, objv); |
---|
2846 | break; |
---|
2847 | case NSQualifiersIdx: |
---|
2848 | result = NamespaceQualifiersCmd(clientData, interp, objc, objv); |
---|
2849 | break; |
---|
2850 | case NSTailIdx: |
---|
2851 | result = NamespaceTailCmd(clientData, interp, objc, objv); |
---|
2852 | break; |
---|
2853 | case NSUpvarIdx: |
---|
2854 | result = NamespaceUpvarCmd(clientData, interp, objc, objv); |
---|
2855 | break; |
---|
2856 | case NSUnknownIdx: |
---|
2857 | result = NamespaceUnknownCmd(clientData, interp, objc, objv); |
---|
2858 | break; |
---|
2859 | case NSWhichIdx: |
---|
2860 | result = NamespaceWhichCmd(clientData, interp, objc, objv); |
---|
2861 | break; |
---|
2862 | } |
---|
2863 | return result; |
---|
2864 | } |
---|
2865 | |
---|
2866 | /* |
---|
2867 | *---------------------------------------------------------------------- |
---|
2868 | * |
---|
2869 | * NamespaceChildrenCmd -- |
---|
2870 | * |
---|
2871 | * Invoked to implement the "namespace children" command that returns a |
---|
2872 | * list containing the fully-qualified names of the child namespaces of a |
---|
2873 | * given namespace. Handles the following syntax: |
---|
2874 | * |
---|
2875 | * namespace children ?name? ?pattern? |
---|
2876 | * |
---|
2877 | * Results: |
---|
2878 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
2879 | * |
---|
2880 | * Side effects: |
---|
2881 | * Returns a result in the interpreter's result object. If anything goes |
---|
2882 | * wrong, the result is an error message. |
---|
2883 | * |
---|
2884 | *---------------------------------------------------------------------- |
---|
2885 | */ |
---|
2886 | |
---|
2887 | static int |
---|
2888 | NamespaceChildrenCmd( |
---|
2889 | ClientData dummy, /* Not used. */ |
---|
2890 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
2891 | int objc, /* Number of arguments. */ |
---|
2892 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
2893 | { |
---|
2894 | Tcl_Namespace *namespacePtr; |
---|
2895 | Namespace *nsPtr, *childNsPtr; |
---|
2896 | Namespace *globalNsPtr = (Namespace *) TclGetGlobalNamespace(interp); |
---|
2897 | char *pattern = NULL; |
---|
2898 | Tcl_DString buffer; |
---|
2899 | register Tcl_HashEntry *entryPtr; |
---|
2900 | Tcl_HashSearch search; |
---|
2901 | Tcl_Obj *listPtr, *elemPtr; |
---|
2902 | |
---|
2903 | /* |
---|
2904 | * Get a pointer to the specified namespace, or the current namespace. |
---|
2905 | */ |
---|
2906 | |
---|
2907 | if (objc == 2) { |
---|
2908 | nsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
2909 | } else if ((objc == 3) || (objc == 4)) { |
---|
2910 | if (TclGetNamespaceFromObj(interp, objv[2], &namespacePtr) != TCL_OK) { |
---|
2911 | return TCL_ERROR; |
---|
2912 | } |
---|
2913 | nsPtr = (Namespace *) namespacePtr; |
---|
2914 | } else { |
---|
2915 | Tcl_WrongNumArgs(interp, 2, objv, "?name? ?pattern?"); |
---|
2916 | return TCL_ERROR; |
---|
2917 | } |
---|
2918 | |
---|
2919 | /* |
---|
2920 | * Get the glob-style pattern, if any, used to narrow the search. |
---|
2921 | */ |
---|
2922 | |
---|
2923 | Tcl_DStringInit(&buffer); |
---|
2924 | if (objc == 4) { |
---|
2925 | char *name = TclGetString(objv[3]); |
---|
2926 | |
---|
2927 | if ((*name == ':') && (*(name+1) == ':')) { |
---|
2928 | pattern = name; |
---|
2929 | } else { |
---|
2930 | Tcl_DStringAppend(&buffer, nsPtr->fullName, -1); |
---|
2931 | if (nsPtr != globalNsPtr) { |
---|
2932 | Tcl_DStringAppend(&buffer, "::", 2); |
---|
2933 | } |
---|
2934 | Tcl_DStringAppend(&buffer, name, -1); |
---|
2935 | pattern = Tcl_DStringValue(&buffer); |
---|
2936 | } |
---|
2937 | } |
---|
2938 | |
---|
2939 | /* |
---|
2940 | * Create a list containing the full names of all child namespaces whose |
---|
2941 | * names match the specified pattern, if any. |
---|
2942 | */ |
---|
2943 | |
---|
2944 | listPtr = Tcl_NewListObj(0, NULL); |
---|
2945 | if ((pattern != NULL) && TclMatchIsTrivial(pattern)) { |
---|
2946 | unsigned int length = strlen(nsPtr->fullName); |
---|
2947 | |
---|
2948 | if (strncmp(pattern, nsPtr->fullName, length) != 0) { |
---|
2949 | goto searchDone; |
---|
2950 | } |
---|
2951 | if (Tcl_FindHashEntry(&nsPtr->childTable, pattern+length) != NULL) { |
---|
2952 | Tcl_ListObjAppendElement(interp, listPtr, |
---|
2953 | Tcl_NewStringObj(pattern, -1)); |
---|
2954 | } |
---|
2955 | goto searchDone; |
---|
2956 | } |
---|
2957 | entryPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search); |
---|
2958 | while (entryPtr != NULL) { |
---|
2959 | childNsPtr = Tcl_GetHashValue(entryPtr); |
---|
2960 | if ((pattern == NULL) |
---|
2961 | || Tcl_StringMatch(childNsPtr->fullName, pattern)) { |
---|
2962 | elemPtr = Tcl_NewStringObj(childNsPtr->fullName, -1); |
---|
2963 | Tcl_ListObjAppendElement(interp, listPtr, elemPtr); |
---|
2964 | } |
---|
2965 | entryPtr = Tcl_NextHashEntry(&search); |
---|
2966 | } |
---|
2967 | |
---|
2968 | searchDone: |
---|
2969 | Tcl_SetObjResult(interp, listPtr); |
---|
2970 | Tcl_DStringFree(&buffer); |
---|
2971 | return TCL_OK; |
---|
2972 | } |
---|
2973 | |
---|
2974 | /* |
---|
2975 | *---------------------------------------------------------------------- |
---|
2976 | * |
---|
2977 | * NamespaceCodeCmd -- |
---|
2978 | * |
---|
2979 | * Invoked to implement the "namespace code" command to capture the |
---|
2980 | * namespace context of a command. Handles the following syntax: |
---|
2981 | * |
---|
2982 | * namespace code arg |
---|
2983 | * |
---|
2984 | * Here "arg" can be a list. "namespace code arg" produces a result |
---|
2985 | * equivalent to that produced by the command |
---|
2986 | * |
---|
2987 | * list ::namespace inscope [namespace current] $arg |
---|
2988 | * |
---|
2989 | * However, if "arg" is itself a scoped value starting with "::namespace |
---|
2990 | * inscope", then the result is just "arg". |
---|
2991 | * |
---|
2992 | * Results: |
---|
2993 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
2994 | * |
---|
2995 | * Side effects: |
---|
2996 | * If anything goes wrong, this function returns an error message as the |
---|
2997 | * result in the interpreter's result object. |
---|
2998 | * |
---|
2999 | *---------------------------------------------------------------------- |
---|
3000 | */ |
---|
3001 | |
---|
3002 | static int |
---|
3003 | NamespaceCodeCmd( |
---|
3004 | ClientData dummy, /* Not used. */ |
---|
3005 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
3006 | int objc, /* Number of arguments. */ |
---|
3007 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
3008 | { |
---|
3009 | Namespace *currNsPtr; |
---|
3010 | Tcl_Obj *listPtr, *objPtr; |
---|
3011 | register char *arg, *p; |
---|
3012 | int length; |
---|
3013 | |
---|
3014 | if (objc != 3) { |
---|
3015 | Tcl_WrongNumArgs(interp, 2, objv, "arg"); |
---|
3016 | return TCL_ERROR; |
---|
3017 | } |
---|
3018 | |
---|
3019 | /* |
---|
3020 | * If "arg" is already a scoped value, then return it directly. |
---|
3021 | */ |
---|
3022 | |
---|
3023 | arg = TclGetStringFromObj(objv[2], &length); |
---|
3024 | while (*arg == ':') { |
---|
3025 | arg++; |
---|
3026 | length--; |
---|
3027 | } |
---|
3028 | if (*arg=='n' && length>17 && strncmp(arg, "namespace", 9)==0) { |
---|
3029 | for (p=arg+9 ; isspace(UCHAR(*p)) ; p++) { |
---|
3030 | /* empty body: skip over whitespace */ |
---|
3031 | } |
---|
3032 | if (*p=='i' && (p+7 <= arg+length) && strncmp(p, "inscope", 7)==0) { |
---|
3033 | Tcl_SetObjResult(interp, objv[2]); |
---|
3034 | return TCL_OK; |
---|
3035 | } |
---|
3036 | } |
---|
3037 | |
---|
3038 | /* |
---|
3039 | * Otherwise, construct a scoped command by building a list with |
---|
3040 | * "namespace inscope", the full name of the current namespace, and the |
---|
3041 | * argument "arg". By constructing a list, we ensure that scoped commands |
---|
3042 | * are interpreted properly when they are executed later, by the |
---|
3043 | * "namespace inscope" command. |
---|
3044 | */ |
---|
3045 | |
---|
3046 | TclNewObj(listPtr); |
---|
3047 | TclNewLiteralStringObj(objPtr, "::namespace"); |
---|
3048 | Tcl_ListObjAppendElement(interp, listPtr, objPtr); |
---|
3049 | TclNewLiteralStringObj(objPtr, "inscope"); |
---|
3050 | Tcl_ListObjAppendElement(interp, listPtr, objPtr); |
---|
3051 | |
---|
3052 | currNsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
3053 | if (currNsPtr == (Namespace *) TclGetGlobalNamespace(interp)) { |
---|
3054 | TclNewLiteralStringObj(objPtr, "::"); |
---|
3055 | } else { |
---|
3056 | objPtr = Tcl_NewStringObj(currNsPtr->fullName, -1); |
---|
3057 | } |
---|
3058 | Tcl_ListObjAppendElement(interp, listPtr, objPtr); |
---|
3059 | |
---|
3060 | Tcl_ListObjAppendElement(interp, listPtr, objv[2]); |
---|
3061 | |
---|
3062 | Tcl_SetObjResult(interp, listPtr); |
---|
3063 | return TCL_OK; |
---|
3064 | } |
---|
3065 | |
---|
3066 | /* |
---|
3067 | *---------------------------------------------------------------------- |
---|
3068 | * |
---|
3069 | * NamespaceCurrentCmd -- |
---|
3070 | * |
---|
3071 | * Invoked to implement the "namespace current" command which returns the |
---|
3072 | * fully-qualified name of the current namespace. Handles the following |
---|
3073 | * syntax: |
---|
3074 | * |
---|
3075 | * namespace current |
---|
3076 | * |
---|
3077 | * Results: |
---|
3078 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
3079 | * |
---|
3080 | * Side effects: |
---|
3081 | * Returns a result in the interpreter's result object. If anything goes |
---|
3082 | * wrong, the result is an error message. |
---|
3083 | * |
---|
3084 | *---------------------------------------------------------------------- |
---|
3085 | */ |
---|
3086 | |
---|
3087 | static int |
---|
3088 | NamespaceCurrentCmd( |
---|
3089 | ClientData dummy, /* Not used. */ |
---|
3090 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
3091 | int objc, /* Number of arguments. */ |
---|
3092 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
3093 | { |
---|
3094 | register Namespace *currNsPtr; |
---|
3095 | |
---|
3096 | if (objc != 2) { |
---|
3097 | Tcl_WrongNumArgs(interp, 2, objv, NULL); |
---|
3098 | return TCL_ERROR; |
---|
3099 | } |
---|
3100 | |
---|
3101 | /* |
---|
3102 | * The "real" name of the global namespace ("::") is the null string, but |
---|
3103 | * we return "::" for it as a convenience to programmers. Note that "" and |
---|
3104 | * "::" are treated as synonyms by the namespace code so that it is still |
---|
3105 | * easy to do things like: |
---|
3106 | * |
---|
3107 | * namespace [namespace current]::bar { ... } |
---|
3108 | */ |
---|
3109 | |
---|
3110 | currNsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
3111 | if (currNsPtr == (Namespace *) TclGetGlobalNamespace(interp)) { |
---|
3112 | Tcl_SetObjResult(interp, Tcl_NewStringObj("::", 2)); |
---|
3113 | } else { |
---|
3114 | Tcl_SetObjResult(interp, Tcl_NewStringObj(currNsPtr->fullName, -1)); |
---|
3115 | } |
---|
3116 | return TCL_OK; |
---|
3117 | } |
---|
3118 | |
---|
3119 | /* |
---|
3120 | *---------------------------------------------------------------------- |
---|
3121 | * |
---|
3122 | * NamespaceDeleteCmd -- |
---|
3123 | * |
---|
3124 | * Invoked to implement the "namespace delete" command to delete |
---|
3125 | * namespace(s). Handles the following syntax: |
---|
3126 | * |
---|
3127 | * namespace delete ?name name...? |
---|
3128 | * |
---|
3129 | * Each name identifies a namespace. It may include a sequence of |
---|
3130 | * namespace qualifiers separated by "::"s. If a namespace is found, it |
---|
3131 | * is deleted: all variables and procedures contained in that namespace |
---|
3132 | * are deleted. If that namespace is being used on the call stack, it is |
---|
3133 | * kept alive (but logically deleted) until it is removed from the call |
---|
3134 | * stack: that is, it can no longer be referenced by name but any |
---|
3135 | * currently executing procedure that refers to it is allowed to do so |
---|
3136 | * until the procedure returns. If the namespace can't be found, this |
---|
3137 | * function returns an error. If no namespaces are specified, this |
---|
3138 | * command does nothing. |
---|
3139 | * |
---|
3140 | * Results: |
---|
3141 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
3142 | * |
---|
3143 | * Side effects: |
---|
3144 | * Deletes the specified namespaces. If anything goes wrong, this |
---|
3145 | * function returns an error message in the interpreter's result object. |
---|
3146 | * |
---|
3147 | *---------------------------------------------------------------------- |
---|
3148 | */ |
---|
3149 | |
---|
3150 | static int |
---|
3151 | NamespaceDeleteCmd( |
---|
3152 | ClientData dummy, /* Not used. */ |
---|
3153 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
3154 | int objc, /* Number of arguments. */ |
---|
3155 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
3156 | { |
---|
3157 | Tcl_Namespace *namespacePtr; |
---|
3158 | char *name; |
---|
3159 | register int i; |
---|
3160 | |
---|
3161 | if (objc < 2) { |
---|
3162 | Tcl_WrongNumArgs(interp, 2, objv, "?name name...?"); |
---|
3163 | return TCL_ERROR; |
---|
3164 | } |
---|
3165 | |
---|
3166 | /* |
---|
3167 | * Destroying one namespace may cause another to be destroyed. Break this |
---|
3168 | * into two passes: first check to make sure that all namespaces on the |
---|
3169 | * command line are valid, and report any errors. |
---|
3170 | */ |
---|
3171 | |
---|
3172 | for (i = 2; i < objc; i++) { |
---|
3173 | name = TclGetString(objv[i]); |
---|
3174 | namespacePtr = Tcl_FindNamespace(interp, name, NULL, /*flags*/ 0); |
---|
3175 | if ((namespacePtr == NULL) |
---|
3176 | || (((Namespace *)namespacePtr)->flags & NS_KILLED)) { |
---|
3177 | Tcl_AppendResult(interp, "unknown namespace \"", |
---|
3178 | TclGetString(objv[i]), |
---|
3179 | "\" in namespace delete command", NULL); |
---|
3180 | Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "NAMESPACE", |
---|
3181 | TclGetString(objv[i]), NULL); |
---|
3182 | return TCL_ERROR; |
---|
3183 | } |
---|
3184 | } |
---|
3185 | |
---|
3186 | /* |
---|
3187 | * Okay, now delete each namespace. |
---|
3188 | */ |
---|
3189 | |
---|
3190 | for (i = 2; i < objc; i++) { |
---|
3191 | name = TclGetString(objv[i]); |
---|
3192 | namespacePtr = Tcl_FindNamespace(interp, name, NULL, /* flags */ 0); |
---|
3193 | if (namespacePtr) { |
---|
3194 | Tcl_DeleteNamespace(namespacePtr); |
---|
3195 | } |
---|
3196 | } |
---|
3197 | return TCL_OK; |
---|
3198 | } |
---|
3199 | |
---|
3200 | /* |
---|
3201 | *---------------------------------------------------------------------- |
---|
3202 | * |
---|
3203 | * NamespaceEvalCmd -- |
---|
3204 | * |
---|
3205 | * Invoked to implement the "namespace eval" command. Executes commands |
---|
3206 | * in a namespace. If the namespace does not already exist, it is |
---|
3207 | * created. Handles the following syntax: |
---|
3208 | * |
---|
3209 | * namespace eval name arg ?arg...? |
---|
3210 | * |
---|
3211 | * If more than one arg argument is specified, the command that is |
---|
3212 | * executed is the result of concatenating the arguments together with a |
---|
3213 | * space between each argument. |
---|
3214 | * |
---|
3215 | * Results: |
---|
3216 | * Returns TCL_OK if the namespace is found and the commands are executed |
---|
3217 | * successfully. Returns TCL_ERROR if anything goes wrong. |
---|
3218 | * |
---|
3219 | * Side effects: |
---|
3220 | * Returns the result of the command in the interpreter's result object. |
---|
3221 | * If anything goes wrong, this function returns an error message as the |
---|
3222 | * result. |
---|
3223 | * |
---|
3224 | *---------------------------------------------------------------------- |
---|
3225 | */ |
---|
3226 | |
---|
3227 | static int |
---|
3228 | NamespaceEvalCmd( |
---|
3229 | ClientData dummy, /* Not used. */ |
---|
3230 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
3231 | int objc, /* Number of arguments. */ |
---|
3232 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
3233 | { |
---|
3234 | Tcl_Namespace *namespacePtr; |
---|
3235 | CallFrame *framePtr, **framePtrPtr; |
---|
3236 | Tcl_Obj *objPtr; |
---|
3237 | int result; |
---|
3238 | |
---|
3239 | if (objc < 4) { |
---|
3240 | Tcl_WrongNumArgs(interp, 2, objv, "name arg ?arg...?"); |
---|
3241 | return TCL_ERROR; |
---|
3242 | } |
---|
3243 | |
---|
3244 | /* |
---|
3245 | * Try to resolve the namespace reference, caching the result in the |
---|
3246 | * namespace object along the way. |
---|
3247 | */ |
---|
3248 | |
---|
3249 | result = GetNamespaceFromObj(interp, objv[2], &namespacePtr); |
---|
3250 | |
---|
3251 | /* |
---|
3252 | * If the namespace wasn't found, try to create it. |
---|
3253 | */ |
---|
3254 | |
---|
3255 | if (result == TCL_ERROR) { |
---|
3256 | char *name = TclGetString(objv[2]); |
---|
3257 | |
---|
3258 | namespacePtr = Tcl_CreateNamespace(interp, name, NULL, NULL); |
---|
3259 | if (namespacePtr == NULL) { |
---|
3260 | return TCL_ERROR; |
---|
3261 | } |
---|
3262 | } |
---|
3263 | |
---|
3264 | /* |
---|
3265 | * Make the specified namespace the current namespace and evaluate the |
---|
3266 | * command(s). |
---|
3267 | */ |
---|
3268 | |
---|
3269 | /* This is needed to satisfy GCC 3.3's strict aliasing rules */ |
---|
3270 | framePtrPtr = &framePtr; |
---|
3271 | result = TclPushStackFrame(interp, (Tcl_CallFrame **) framePtrPtr, |
---|
3272 | namespacePtr, /*isProcCallFrame*/ 0); |
---|
3273 | if (result != TCL_OK) { |
---|
3274 | return TCL_ERROR; |
---|
3275 | } |
---|
3276 | |
---|
3277 | framePtr->objc = objc; |
---|
3278 | framePtr->objv = objv; |
---|
3279 | |
---|
3280 | if (objc == 4) { |
---|
3281 | /* |
---|
3282 | * TIP #280: Make invoker available to eval'd script. |
---|
3283 | */ |
---|
3284 | |
---|
3285 | Interp *iPtr = (Interp *) interp; |
---|
3286 | |
---|
3287 | result = TclEvalObjEx(interp, objv[3], 0, iPtr->cmdFramePtr, 3); |
---|
3288 | } else { |
---|
3289 | /* |
---|
3290 | * More than one argument: concatenate them together with spaces |
---|
3291 | * between, then evaluate the result. Tcl_EvalObjEx will delete the |
---|
3292 | * object when it decrements its refcount after eval'ing it. |
---|
3293 | */ |
---|
3294 | |
---|
3295 | objPtr = Tcl_ConcatObj(objc-3, objv+3); |
---|
3296 | |
---|
3297 | /* |
---|
3298 | * TIP #280: Make invoking context available to eval'd script. |
---|
3299 | */ |
---|
3300 | |
---|
3301 | result = TclEvalObjEx(interp, objPtr, TCL_EVAL_DIRECT, NULL, 0); |
---|
3302 | } |
---|
3303 | |
---|
3304 | if (result == TCL_ERROR) { |
---|
3305 | int length = strlen(namespacePtr->fullName); |
---|
3306 | int limit = 200; |
---|
3307 | int overflow = (length > limit); |
---|
3308 | |
---|
3309 | Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( |
---|
3310 | "\n (in namespace eval \"%.*s%s\" script line %d)", |
---|
3311 | (overflow ? limit : length), namespacePtr->fullName, |
---|
3312 | (overflow ? "..." : ""), interp->errorLine)); |
---|
3313 | } |
---|
3314 | |
---|
3315 | /* |
---|
3316 | * Restore the previous "current" namespace. |
---|
3317 | */ |
---|
3318 | |
---|
3319 | TclPopStackFrame(interp); |
---|
3320 | return result; |
---|
3321 | } |
---|
3322 | |
---|
3323 | /* |
---|
3324 | *---------------------------------------------------------------------- |
---|
3325 | * |
---|
3326 | * NamespaceExistsCmd -- |
---|
3327 | * |
---|
3328 | * Invoked to implement the "namespace exists" command that returns true |
---|
3329 | * if the given namespace currently exists, and false otherwise. Handles |
---|
3330 | * the following syntax: |
---|
3331 | * |
---|
3332 | * namespace exists name |
---|
3333 | * |
---|
3334 | * Results: |
---|
3335 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
3336 | * |
---|
3337 | * Side effects: |
---|
3338 | * Returns a result in the interpreter's result object. If anything goes |
---|
3339 | * wrong, the result is an error message. |
---|
3340 | * |
---|
3341 | *---------------------------------------------------------------------- |
---|
3342 | */ |
---|
3343 | |
---|
3344 | static int |
---|
3345 | NamespaceExistsCmd( |
---|
3346 | ClientData dummy, /* Not used. */ |
---|
3347 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
3348 | int objc, /* Number of arguments. */ |
---|
3349 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
3350 | { |
---|
3351 | Tcl_Namespace *namespacePtr; |
---|
3352 | |
---|
3353 | if (objc != 3) { |
---|
3354 | Tcl_WrongNumArgs(interp, 2, objv, "name"); |
---|
3355 | return TCL_ERROR; |
---|
3356 | } |
---|
3357 | |
---|
3358 | Tcl_SetObjResult(interp, Tcl_NewBooleanObj( |
---|
3359 | GetNamespaceFromObj(interp, objv[2], &namespacePtr) == TCL_OK)); |
---|
3360 | return TCL_OK; |
---|
3361 | } |
---|
3362 | |
---|
3363 | /* |
---|
3364 | *---------------------------------------------------------------------- |
---|
3365 | * |
---|
3366 | * NamespaceExportCmd -- |
---|
3367 | * |
---|
3368 | * Invoked to implement the "namespace export" command that specifies |
---|
3369 | * which commands are exported from a namespace. The exported commands |
---|
3370 | * are those that can be imported into another namespace using "namespace |
---|
3371 | * import". Both commands defined in a namespace and commands the |
---|
3372 | * namespace has imported can be exported by a namespace. This command |
---|
3373 | * has the following syntax: |
---|
3374 | * |
---|
3375 | * namespace export ?-clear? ?pattern pattern...? |
---|
3376 | * |
---|
3377 | * Each pattern may contain "string match"-style pattern matching special |
---|
3378 | * characters, but the pattern may not include any namespace qualifiers: |
---|
3379 | * that is, the pattern must specify commands in the current (exporting) |
---|
3380 | * namespace. The specified patterns are appended onto the namespace's |
---|
3381 | * list of export patterns. |
---|
3382 | * |
---|
3383 | * To reset the namespace's export pattern list, specify the "-clear" |
---|
3384 | * flag. |
---|
3385 | * |
---|
3386 | * If there are no export patterns and the "-clear" flag isn't given, |
---|
3387 | * this command returns the namespace's current export list. |
---|
3388 | * |
---|
3389 | * Results: |
---|
3390 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
3391 | * |
---|
3392 | * Side effects: |
---|
3393 | * Returns a result in the interpreter's result object. If anything goes |
---|
3394 | * wrong, the result is an error message. |
---|
3395 | * |
---|
3396 | *---------------------------------------------------------------------- |
---|
3397 | */ |
---|
3398 | |
---|
3399 | static int |
---|
3400 | NamespaceExportCmd( |
---|
3401 | ClientData dummy, /* Not used. */ |
---|
3402 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
3403 | int objc, /* Number of arguments. */ |
---|
3404 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
3405 | { |
---|
3406 | Namespace *currNsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
3407 | char *pattern, *string; |
---|
3408 | int resetListFirst = 0; |
---|
3409 | int firstArg, patternCt, i, result; |
---|
3410 | |
---|
3411 | if (objc < 2) { |
---|
3412 | Tcl_WrongNumArgs(interp, 2, objv, "?-clear? ?pattern pattern...?"); |
---|
3413 | return TCL_ERROR; |
---|
3414 | } |
---|
3415 | |
---|
3416 | /* |
---|
3417 | * Process the optional "-clear" argument. |
---|
3418 | */ |
---|
3419 | |
---|
3420 | firstArg = 2; |
---|
3421 | if (firstArg < objc) { |
---|
3422 | string = TclGetString(objv[firstArg]); |
---|
3423 | if (strcmp(string, "-clear") == 0) { |
---|
3424 | resetListFirst = 1; |
---|
3425 | firstArg++; |
---|
3426 | } |
---|
3427 | } |
---|
3428 | |
---|
3429 | /* |
---|
3430 | * If no pattern arguments are given, and "-clear" isn't specified, return |
---|
3431 | * the namespace's current export pattern list. |
---|
3432 | */ |
---|
3433 | |
---|
3434 | patternCt = (objc - firstArg); |
---|
3435 | if (patternCt == 0) { |
---|
3436 | if (firstArg > 2) { |
---|
3437 | return TCL_OK; |
---|
3438 | } else { |
---|
3439 | /* |
---|
3440 | * Create list with export patterns. |
---|
3441 | */ |
---|
3442 | |
---|
3443 | Tcl_Obj *listPtr = Tcl_NewListObj(0, NULL); |
---|
3444 | result = Tcl_AppendExportList(interp, (Tcl_Namespace *) currNsPtr, |
---|
3445 | listPtr); |
---|
3446 | if (result != TCL_OK) { |
---|
3447 | return result; |
---|
3448 | } |
---|
3449 | Tcl_SetObjResult(interp, listPtr); |
---|
3450 | return TCL_OK; |
---|
3451 | } |
---|
3452 | } |
---|
3453 | |
---|
3454 | /* |
---|
3455 | * Add each pattern to the namespace's export pattern list. |
---|
3456 | */ |
---|
3457 | |
---|
3458 | for (i = firstArg; i < objc; i++) { |
---|
3459 | pattern = TclGetString(objv[i]); |
---|
3460 | result = Tcl_Export(interp, (Tcl_Namespace *) currNsPtr, pattern, |
---|
3461 | ((i == firstArg)? resetListFirst : 0)); |
---|
3462 | if (result != TCL_OK) { |
---|
3463 | return result; |
---|
3464 | } |
---|
3465 | } |
---|
3466 | return TCL_OK; |
---|
3467 | } |
---|
3468 | |
---|
3469 | /* |
---|
3470 | *---------------------------------------------------------------------- |
---|
3471 | * |
---|
3472 | * NamespaceForgetCmd -- |
---|
3473 | * |
---|
3474 | * Invoked to implement the "namespace forget" command to remove imported |
---|
3475 | * commands from a namespace. Handles the following syntax: |
---|
3476 | * |
---|
3477 | * namespace forget ?pattern pattern...? |
---|
3478 | * |
---|
3479 | * Each pattern is a name like "foo::*" or "a::b::x*". That is, the |
---|
3480 | * pattern may include the special pattern matching characters recognized |
---|
3481 | * by the "string match" command, but only in the command name at the end |
---|
3482 | * of the qualified name; the special pattern characters may not appear |
---|
3483 | * in a namespace name. All of the commands that match that pattern are |
---|
3484 | * checked to see if they have an imported command in the current |
---|
3485 | * namespace that refers to the matched command. If there is an alias, it |
---|
3486 | * is removed. |
---|
3487 | * |
---|
3488 | * Results: |
---|
3489 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
3490 | * |
---|
3491 | * Side effects: |
---|
3492 | * Imported commands are removed from the current namespace. If anything |
---|
3493 | * goes wrong, this function returns an error message in the |
---|
3494 | * interpreter's result object. |
---|
3495 | * |
---|
3496 | *---------------------------------------------------------------------- |
---|
3497 | */ |
---|
3498 | |
---|
3499 | static int |
---|
3500 | NamespaceForgetCmd( |
---|
3501 | ClientData dummy, /* Not used. */ |
---|
3502 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
3503 | int objc, /* Number of arguments. */ |
---|
3504 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
3505 | { |
---|
3506 | char *pattern; |
---|
3507 | register int i, result; |
---|
3508 | |
---|
3509 | if (objc < 2) { |
---|
3510 | Tcl_WrongNumArgs(interp, 2, objv, "?pattern pattern...?"); |
---|
3511 | return TCL_ERROR; |
---|
3512 | } |
---|
3513 | |
---|
3514 | for (i = 2; i < objc; i++) { |
---|
3515 | pattern = TclGetString(objv[i]); |
---|
3516 | result = Tcl_ForgetImport(interp, NULL, pattern); |
---|
3517 | if (result != TCL_OK) { |
---|
3518 | return result; |
---|
3519 | } |
---|
3520 | } |
---|
3521 | return TCL_OK; |
---|
3522 | } |
---|
3523 | |
---|
3524 | /* |
---|
3525 | *---------------------------------------------------------------------- |
---|
3526 | * |
---|
3527 | * NamespaceImportCmd -- |
---|
3528 | * |
---|
3529 | * Invoked to implement the "namespace import" command that imports |
---|
3530 | * commands into a namespace. Handles the following syntax: |
---|
3531 | * |
---|
3532 | * namespace import ?-force? ?pattern pattern...? |
---|
3533 | * |
---|
3534 | * Each pattern is a namespace-qualified name like "foo::*", "a::b::x*", |
---|
3535 | * or "bar::p". That is, the pattern may include the special pattern |
---|
3536 | * matching characters recognized by the "string match" command, but only |
---|
3537 | * in the command name at the end of the qualified name; the special |
---|
3538 | * pattern characters may not appear in a namespace name. All of the |
---|
3539 | * commands that match the pattern and which are exported from their |
---|
3540 | * namespace are made accessible from the current namespace context. This |
---|
3541 | * is done by creating a new "imported command" in the current namespace |
---|
3542 | * that points to the real command in its original namespace; when the |
---|
3543 | * imported command is called, it invokes the real command. |
---|
3544 | * |
---|
3545 | * If an imported command conflicts with an existing command, it is |
---|
3546 | * treated as an error. But if the "-force" option is included, then |
---|
3547 | * existing commands are overwritten by the imported commands. |
---|
3548 | * |
---|
3549 | * If there are no pattern arguments and the "-force" flag isn't given, |
---|
3550 | * this command returns the list of commands currently imported in |
---|
3551 | * the current namespace. |
---|
3552 | * |
---|
3553 | * Results: |
---|
3554 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
3555 | * |
---|
3556 | * Side effects: |
---|
3557 | * Adds imported commands to the current namespace. If anything goes |
---|
3558 | * wrong, this function returns an error message in the interpreter's |
---|
3559 | * result object. |
---|
3560 | * |
---|
3561 | *---------------------------------------------------------------------- |
---|
3562 | */ |
---|
3563 | |
---|
3564 | static int |
---|
3565 | NamespaceImportCmd( |
---|
3566 | ClientData dummy, /* Not used. */ |
---|
3567 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
3568 | int objc, /* Number of arguments. */ |
---|
3569 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
3570 | { |
---|
3571 | int allowOverwrite = 0; |
---|
3572 | char *string, *pattern; |
---|
3573 | register int i, result; |
---|
3574 | int firstArg; |
---|
3575 | |
---|
3576 | if (objc < 2) { |
---|
3577 | Tcl_WrongNumArgs(interp, 2, objv, "?-force? ?pattern pattern...?"); |
---|
3578 | return TCL_ERROR; |
---|
3579 | } |
---|
3580 | |
---|
3581 | /* |
---|
3582 | * Skip over the optional "-force" as the first argument. |
---|
3583 | */ |
---|
3584 | |
---|
3585 | firstArg = 2; |
---|
3586 | if (firstArg < objc) { |
---|
3587 | string = TclGetString(objv[firstArg]); |
---|
3588 | if ((*string == '-') && (strcmp(string, "-force") == 0)) { |
---|
3589 | allowOverwrite = 1; |
---|
3590 | firstArg++; |
---|
3591 | } |
---|
3592 | } else { |
---|
3593 | /* |
---|
3594 | * When objc == 2, command is just [namespace import]. Introspection |
---|
3595 | * form to return list of imported commands. |
---|
3596 | */ |
---|
3597 | |
---|
3598 | Tcl_HashEntry *hPtr; |
---|
3599 | Tcl_HashSearch search; |
---|
3600 | Namespace *nsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
3601 | Tcl_Obj *listPtr; |
---|
3602 | |
---|
3603 | TclNewObj(listPtr); |
---|
3604 | for (hPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); |
---|
3605 | hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { |
---|
3606 | Command *cmdPtr = Tcl_GetHashValue(hPtr); |
---|
3607 | |
---|
3608 | if (cmdPtr->deleteProc == DeleteImportedCmd) { |
---|
3609 | Tcl_ListObjAppendElement(NULL, listPtr, Tcl_NewStringObj( |
---|
3610 | Tcl_GetHashKey(&nsPtr->cmdTable, hPtr) ,-1)); |
---|
3611 | } |
---|
3612 | } |
---|
3613 | Tcl_SetObjResult(interp, listPtr); |
---|
3614 | return TCL_OK; |
---|
3615 | } |
---|
3616 | |
---|
3617 | /* |
---|
3618 | * Handle the imports for each of the patterns. |
---|
3619 | */ |
---|
3620 | |
---|
3621 | for (i = firstArg; i < objc; i++) { |
---|
3622 | pattern = TclGetString(objv[i]); |
---|
3623 | result = Tcl_Import(interp, NULL, pattern, allowOverwrite); |
---|
3624 | if (result != TCL_OK) { |
---|
3625 | return result; |
---|
3626 | } |
---|
3627 | } |
---|
3628 | return TCL_OK; |
---|
3629 | } |
---|
3630 | |
---|
3631 | /* |
---|
3632 | *---------------------------------------------------------------------- |
---|
3633 | * |
---|
3634 | * NamespaceInscopeCmd -- |
---|
3635 | * |
---|
3636 | * Invoked to implement the "namespace inscope" command that executes a |
---|
3637 | * script in the context of a particular namespace. This command is not |
---|
3638 | * expected to be used directly by programmers; calls to it are generated |
---|
3639 | * implicitly when programs use "namespace code" commands to register |
---|
3640 | * callback scripts. Handles the following syntax: |
---|
3641 | * |
---|
3642 | * namespace inscope name arg ?arg...? |
---|
3643 | * |
---|
3644 | * The "namespace inscope" command is much like the "namespace eval" |
---|
3645 | * command except that it has lappend semantics and the namespace must |
---|
3646 | * already exist. It treats the first argument as a list, and appends any |
---|
3647 | * arguments after the first onto the end as proper list elements. For |
---|
3648 | * example, |
---|
3649 | * |
---|
3650 | * namespace inscope ::foo {a b} c d e |
---|
3651 | * |
---|
3652 | * is equivalent to |
---|
3653 | * |
---|
3654 | * namespace eval ::foo [concat {a b} [list c d e]] |
---|
3655 | * |
---|
3656 | * This lappend semantics is important because many callback scripts are |
---|
3657 | * actually prefixes. |
---|
3658 | * |
---|
3659 | * Results: |
---|
3660 | * Returns TCL_OK to indicate success, or TCL_ERROR to indicate failure. |
---|
3661 | * |
---|
3662 | * Side effects: |
---|
3663 | * Returns a result in the Tcl interpreter's result object. |
---|
3664 | * |
---|
3665 | *---------------------------------------------------------------------- |
---|
3666 | */ |
---|
3667 | |
---|
3668 | static int |
---|
3669 | NamespaceInscopeCmd( |
---|
3670 | ClientData dummy, /* Not used. */ |
---|
3671 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
3672 | int objc, /* Number of arguments. */ |
---|
3673 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
3674 | { |
---|
3675 | Tcl_Namespace *namespacePtr; |
---|
3676 | CallFrame *framePtr, **framePtrPtr; |
---|
3677 | int i, result; |
---|
3678 | |
---|
3679 | if (objc < 4) { |
---|
3680 | Tcl_WrongNumArgs(interp, 2, objv, "name arg ?arg...?"); |
---|
3681 | return TCL_ERROR; |
---|
3682 | } |
---|
3683 | |
---|
3684 | /* |
---|
3685 | * Resolve the namespace reference. |
---|
3686 | */ |
---|
3687 | |
---|
3688 | if (TclGetNamespaceFromObj(interp, objv[2], &namespacePtr) != TCL_OK) { |
---|
3689 | return TCL_ERROR; |
---|
3690 | } |
---|
3691 | |
---|
3692 | /* |
---|
3693 | * Make the specified namespace the current namespace. |
---|
3694 | */ |
---|
3695 | |
---|
3696 | framePtrPtr = &framePtr; /* This is needed to satisfy GCC's |
---|
3697 | * strict aliasing rules. */ |
---|
3698 | result = TclPushStackFrame(interp, (Tcl_CallFrame **) framePtrPtr, |
---|
3699 | namespacePtr, /*isProcCallFrame*/ 0); |
---|
3700 | if (result != TCL_OK) { |
---|
3701 | return result; |
---|
3702 | } |
---|
3703 | |
---|
3704 | framePtr->objc = objc; |
---|
3705 | framePtr->objv = objv; |
---|
3706 | |
---|
3707 | /* |
---|
3708 | * Execute the command. If there is just one argument, just treat it as a |
---|
3709 | * script and evaluate it. Otherwise, create a list from the arguments |
---|
3710 | * after the first one, then concatenate the first argument and the list |
---|
3711 | * of extra arguments to form the command to evaluate. |
---|
3712 | */ |
---|
3713 | |
---|
3714 | if (objc == 4) { |
---|
3715 | result = Tcl_EvalObjEx(interp, objv[3], 0); |
---|
3716 | } else { |
---|
3717 | Tcl_Obj *concatObjv[2]; |
---|
3718 | register Tcl_Obj *listPtr, *cmdObjPtr; |
---|
3719 | |
---|
3720 | listPtr = Tcl_NewListObj(0, NULL); |
---|
3721 | for (i = 4; i < objc; i++) { |
---|
3722 | if (Tcl_ListObjAppendElement(interp, listPtr, objv[i]) != TCL_OK) { |
---|
3723 | Tcl_DecrRefCount(listPtr); /* Free unneeded obj. */ |
---|
3724 | return TCL_ERROR; |
---|
3725 | } |
---|
3726 | } |
---|
3727 | |
---|
3728 | concatObjv[0] = objv[3]; |
---|
3729 | concatObjv[1] = listPtr; |
---|
3730 | cmdObjPtr = Tcl_ConcatObj(2, concatObjv); |
---|
3731 | result = Tcl_EvalObjEx(interp, cmdObjPtr, TCL_EVAL_DIRECT); |
---|
3732 | Tcl_DecrRefCount(listPtr); /* We're done with the list object. */ |
---|
3733 | } |
---|
3734 | |
---|
3735 | if (result == TCL_ERROR) { |
---|
3736 | int length = strlen(namespacePtr->fullName); |
---|
3737 | int limit = 200; |
---|
3738 | int overflow = (length > limit); |
---|
3739 | |
---|
3740 | Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( |
---|
3741 | "\n (in namespace inscope \"%.*s%s\" script line %d)", |
---|
3742 | (overflow ? limit : length), namespacePtr->fullName, |
---|
3743 | (overflow ? "..." : ""), interp->errorLine)); |
---|
3744 | } |
---|
3745 | |
---|
3746 | /* |
---|
3747 | * Restore the previous "current" namespace. |
---|
3748 | */ |
---|
3749 | |
---|
3750 | TclPopStackFrame(interp); |
---|
3751 | return result; |
---|
3752 | } |
---|
3753 | |
---|
3754 | /* |
---|
3755 | *---------------------------------------------------------------------- |
---|
3756 | * |
---|
3757 | * NamespaceOriginCmd -- |
---|
3758 | * |
---|
3759 | * Invoked to implement the "namespace origin" command to return the |
---|
3760 | * fully-qualified name of the "real" command to which the specified |
---|
3761 | * "imported command" refers. Handles the following syntax: |
---|
3762 | * |
---|
3763 | * namespace origin name |
---|
3764 | * |
---|
3765 | * Results: |
---|
3766 | * An imported command is created in an namespace when that namespace |
---|
3767 | * imports a command from another namespace. If a command is imported |
---|
3768 | * into a sequence of namespaces a, b,...,n where each successive |
---|
3769 | * namespace just imports the command from the previous namespace, this |
---|
3770 | * command returns the fully-qualified name of the original command in |
---|
3771 | * the first namespace, a. If "name" does not refer to an alias, its |
---|
3772 | * fully-qualified name is returned. The returned name is stored in the |
---|
3773 | * interpreter's result object. This function returns TCL_OK if |
---|
3774 | * successful, and TCL_ERROR if anything goes wrong. |
---|
3775 | * |
---|
3776 | * Side effects: |
---|
3777 | * If anything goes wrong, this function returns an error message in the |
---|
3778 | * interpreter's result object. |
---|
3779 | * |
---|
3780 | *---------------------------------------------------------------------- |
---|
3781 | */ |
---|
3782 | |
---|
3783 | static int |
---|
3784 | NamespaceOriginCmd( |
---|
3785 | ClientData dummy, /* Not used. */ |
---|
3786 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
3787 | int objc, /* Number of arguments. */ |
---|
3788 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
3789 | { |
---|
3790 | Tcl_Command command, origCommand; |
---|
3791 | Tcl_Obj *resultPtr; |
---|
3792 | |
---|
3793 | if (objc != 3) { |
---|
3794 | Tcl_WrongNumArgs(interp, 2, objv, "name"); |
---|
3795 | return TCL_ERROR; |
---|
3796 | } |
---|
3797 | |
---|
3798 | command = Tcl_GetCommandFromObj(interp, objv[2]); |
---|
3799 | if (command == NULL) { |
---|
3800 | Tcl_AppendResult(interp, "invalid command name \"", |
---|
3801 | TclGetString(objv[2]), "\"", NULL); |
---|
3802 | Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "COMMAND", |
---|
3803 | TclGetString(objv[2]), NULL); |
---|
3804 | return TCL_ERROR; |
---|
3805 | } |
---|
3806 | origCommand = TclGetOriginalCommand(command); |
---|
3807 | TclNewObj(resultPtr); |
---|
3808 | if (origCommand == NULL) { |
---|
3809 | /* |
---|
3810 | * The specified command isn't an imported command. Return the |
---|
3811 | * command's name qualified by the full name of the namespace it was |
---|
3812 | * defined in. |
---|
3813 | */ |
---|
3814 | |
---|
3815 | Tcl_GetCommandFullName(interp, command, resultPtr); |
---|
3816 | } else { |
---|
3817 | Tcl_GetCommandFullName(interp, origCommand, resultPtr); |
---|
3818 | } |
---|
3819 | Tcl_SetObjResult(interp, resultPtr); |
---|
3820 | return TCL_OK; |
---|
3821 | } |
---|
3822 | |
---|
3823 | /* |
---|
3824 | *---------------------------------------------------------------------- |
---|
3825 | * |
---|
3826 | * NamespaceParentCmd -- |
---|
3827 | * |
---|
3828 | * Invoked to implement the "namespace parent" command that returns the |
---|
3829 | * fully-qualified name of the parent namespace for a specified |
---|
3830 | * namespace. Handles the following syntax: |
---|
3831 | * |
---|
3832 | * namespace parent ?name? |
---|
3833 | * |
---|
3834 | * Results: |
---|
3835 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
3836 | * |
---|
3837 | * Side effects: |
---|
3838 | * Returns a result in the interpreter's result object. If anything goes |
---|
3839 | * wrong, the result is an error message. |
---|
3840 | * |
---|
3841 | *---------------------------------------------------------------------- |
---|
3842 | */ |
---|
3843 | |
---|
3844 | static int |
---|
3845 | NamespaceParentCmd( |
---|
3846 | ClientData dummy, /* Not used. */ |
---|
3847 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
3848 | int objc, /* Number of arguments. */ |
---|
3849 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
3850 | { |
---|
3851 | Tcl_Namespace *nsPtr; |
---|
3852 | |
---|
3853 | if (objc == 2) { |
---|
3854 | nsPtr = TclGetCurrentNamespace(interp); |
---|
3855 | } else if (objc == 3) { |
---|
3856 | if (TclGetNamespaceFromObj(interp, objv[2], &nsPtr) != TCL_OK) { |
---|
3857 | return TCL_ERROR; |
---|
3858 | } |
---|
3859 | } else { |
---|
3860 | Tcl_WrongNumArgs(interp, 2, objv, "?name?"); |
---|
3861 | return TCL_ERROR; |
---|
3862 | } |
---|
3863 | |
---|
3864 | /* |
---|
3865 | * Report the parent of the specified namespace. |
---|
3866 | */ |
---|
3867 | |
---|
3868 | if (nsPtr->parentPtr != NULL) { |
---|
3869 | Tcl_SetObjResult(interp, Tcl_NewStringObj( |
---|
3870 | nsPtr->parentPtr->fullName, -1)); |
---|
3871 | } |
---|
3872 | return TCL_OK; |
---|
3873 | } |
---|
3874 | |
---|
3875 | /* |
---|
3876 | *---------------------------------------------------------------------- |
---|
3877 | * |
---|
3878 | * NamespacePathCmd -- |
---|
3879 | * |
---|
3880 | * Invoked to implement the "namespace path" command that reads and |
---|
3881 | * writes the current namespace's command resolution path. Has one |
---|
3882 | * optional argument: if present, it is a list of named namespaces to set |
---|
3883 | * the path to, and if absent, the current path should be returned. |
---|
3884 | * Handles the following syntax: |
---|
3885 | * |
---|
3886 | * namespace path ?nsList? |
---|
3887 | * |
---|
3888 | * Results: |
---|
3889 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong |
---|
3890 | * (most notably if the namespace list contains the name of something |
---|
3891 | * other than a namespace). In the successful-exit case, may set the |
---|
3892 | * interpreter result to the list of names of the namespaces on the |
---|
3893 | * current namespace's path. |
---|
3894 | * |
---|
3895 | * Side effects: |
---|
3896 | * May update the namespace path (triggering a recomputing of all command |
---|
3897 | * names that depend on the namespace for resolution). |
---|
3898 | * |
---|
3899 | *---------------------------------------------------------------------- |
---|
3900 | */ |
---|
3901 | |
---|
3902 | static int |
---|
3903 | NamespacePathCmd( |
---|
3904 | ClientData dummy, /* Not used. */ |
---|
3905 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
3906 | int objc, /* Number of arguments. */ |
---|
3907 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
3908 | { |
---|
3909 | Namespace *nsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
3910 | int i, nsObjc, result = TCL_ERROR; |
---|
3911 | Tcl_Obj **nsObjv; |
---|
3912 | Tcl_Namespace **namespaceList = NULL; |
---|
3913 | |
---|
3914 | if (objc > 3) { |
---|
3915 | Tcl_WrongNumArgs(interp, 2, objv, "?pathList?"); |
---|
3916 | return TCL_ERROR; |
---|
3917 | } |
---|
3918 | |
---|
3919 | /* |
---|
3920 | * If no path is given, return the current path. |
---|
3921 | */ |
---|
3922 | |
---|
3923 | if (objc == 2) { |
---|
3924 | /* |
---|
3925 | * Not a very fast way to compute this, but easy to get right. |
---|
3926 | */ |
---|
3927 | |
---|
3928 | for (i=0 ; i<nsPtr->commandPathLength ; i++) { |
---|
3929 | if (nsPtr->commandPathArray[i].nsPtr != NULL) { |
---|
3930 | Tcl_AppendElement(interp, |
---|
3931 | nsPtr->commandPathArray[i].nsPtr->fullName); |
---|
3932 | } |
---|
3933 | } |
---|
3934 | return TCL_OK; |
---|
3935 | } |
---|
3936 | |
---|
3937 | /* |
---|
3938 | * There is a path given, so parse it into an array of namespace pointers. |
---|
3939 | */ |
---|
3940 | |
---|
3941 | if (TclListObjGetElements(interp, objv[2], &nsObjc, &nsObjv) != TCL_OK) { |
---|
3942 | goto badNamespace; |
---|
3943 | } |
---|
3944 | if (nsObjc != 0) { |
---|
3945 | namespaceList = (Tcl_Namespace **) |
---|
3946 | TclStackAlloc(interp, sizeof(Tcl_Namespace *) * nsObjc); |
---|
3947 | |
---|
3948 | for (i=0 ; i<nsObjc ; i++) { |
---|
3949 | if (TclGetNamespaceFromObj(interp, nsObjv[i], |
---|
3950 | &namespaceList[i]) != TCL_OK) { |
---|
3951 | goto badNamespace; |
---|
3952 | } |
---|
3953 | } |
---|
3954 | } |
---|
3955 | |
---|
3956 | /* |
---|
3957 | * Now we have the list of valid namespaces, install it as the path. |
---|
3958 | */ |
---|
3959 | |
---|
3960 | TclSetNsPath(nsPtr, nsObjc, namespaceList); |
---|
3961 | |
---|
3962 | result = TCL_OK; |
---|
3963 | badNamespace: |
---|
3964 | if (namespaceList != NULL) { |
---|
3965 | TclStackFree(interp, namespaceList); |
---|
3966 | } |
---|
3967 | return result; |
---|
3968 | } |
---|
3969 | |
---|
3970 | /* |
---|
3971 | *---------------------------------------------------------------------- |
---|
3972 | * |
---|
3973 | * TclSetNsPath -- |
---|
3974 | * |
---|
3975 | * Sets the namespace command name resolution path to the given list of |
---|
3976 | * namespaces. If the list is empty (of zero length) the path is set to |
---|
3977 | * empty and the default old-style behaviour of command name resolution |
---|
3978 | * is used. |
---|
3979 | * |
---|
3980 | * Results: |
---|
3981 | * nothing |
---|
3982 | * |
---|
3983 | * Side effects: |
---|
3984 | * Invalidates the command name resolution caches for any command |
---|
3985 | * resolved in the given namespace. |
---|
3986 | * |
---|
3987 | *---------------------------------------------------------------------- |
---|
3988 | */ |
---|
3989 | |
---|
3990 | void |
---|
3991 | TclSetNsPath( |
---|
3992 | Namespace *nsPtr, /* Namespace whose path is to be set. */ |
---|
3993 | int pathLength, /* Length of pathAry. */ |
---|
3994 | Tcl_Namespace *pathAry[]) /* Array of namespaces that are the path. */ |
---|
3995 | { |
---|
3996 | if (pathLength != 0) { |
---|
3997 | NamespacePathEntry *tmpPathArray = (NamespacePathEntry *) |
---|
3998 | ckalloc(sizeof(NamespacePathEntry) * pathLength); |
---|
3999 | int i; |
---|
4000 | |
---|
4001 | for (i=0 ; i<pathLength ; i++) { |
---|
4002 | tmpPathArray[i].nsPtr = (Namespace *) pathAry[i]; |
---|
4003 | tmpPathArray[i].creatorNsPtr = nsPtr; |
---|
4004 | tmpPathArray[i].prevPtr = NULL; |
---|
4005 | tmpPathArray[i].nextPtr = |
---|
4006 | tmpPathArray[i].nsPtr->commandPathSourceList; |
---|
4007 | if (tmpPathArray[i].nextPtr != NULL) { |
---|
4008 | tmpPathArray[i].nextPtr->prevPtr = &tmpPathArray[i]; |
---|
4009 | } |
---|
4010 | tmpPathArray[i].nsPtr->commandPathSourceList = &tmpPathArray[i]; |
---|
4011 | } |
---|
4012 | if (nsPtr->commandPathLength != 0) { |
---|
4013 | UnlinkNsPath(nsPtr); |
---|
4014 | } |
---|
4015 | nsPtr->commandPathArray = tmpPathArray; |
---|
4016 | } else { |
---|
4017 | if (nsPtr->commandPathLength != 0) { |
---|
4018 | UnlinkNsPath(nsPtr); |
---|
4019 | } |
---|
4020 | } |
---|
4021 | |
---|
4022 | nsPtr->commandPathLength = pathLength; |
---|
4023 | nsPtr->cmdRefEpoch++; |
---|
4024 | nsPtr->resolverEpoch++; |
---|
4025 | } |
---|
4026 | |
---|
4027 | /* |
---|
4028 | *---------------------------------------------------------------------- |
---|
4029 | * |
---|
4030 | * UnlinkNsPath -- |
---|
4031 | * |
---|
4032 | * Delete the given namespace's command name resolution path. Only call |
---|
4033 | * if the path is non-empty. Caller must reset the counter containing the |
---|
4034 | * path size. |
---|
4035 | * |
---|
4036 | * Results: |
---|
4037 | * nothing |
---|
4038 | * |
---|
4039 | * Side effects: |
---|
4040 | * Deletes the array of path entries and unlinks those path entries from |
---|
4041 | * the target namespace's list of interested namespaces. |
---|
4042 | * |
---|
4043 | *---------------------------------------------------------------------- |
---|
4044 | */ |
---|
4045 | |
---|
4046 | static void |
---|
4047 | UnlinkNsPath( |
---|
4048 | Namespace *nsPtr) |
---|
4049 | { |
---|
4050 | int i; |
---|
4051 | for (i=0 ; i<nsPtr->commandPathLength ; i++) { |
---|
4052 | NamespacePathEntry *nsPathPtr = &nsPtr->commandPathArray[i]; |
---|
4053 | if (nsPathPtr->prevPtr != NULL) { |
---|
4054 | nsPathPtr->prevPtr->nextPtr = nsPathPtr->nextPtr; |
---|
4055 | } |
---|
4056 | if (nsPathPtr->nextPtr != NULL) { |
---|
4057 | nsPathPtr->nextPtr->prevPtr = nsPathPtr->prevPtr; |
---|
4058 | } |
---|
4059 | if (nsPathPtr->nsPtr != NULL) { |
---|
4060 | if (nsPathPtr->nsPtr->commandPathSourceList == nsPathPtr) { |
---|
4061 | nsPathPtr->nsPtr->commandPathSourceList = nsPathPtr->nextPtr; |
---|
4062 | } |
---|
4063 | } |
---|
4064 | } |
---|
4065 | ckfree((char *) nsPtr->commandPathArray); |
---|
4066 | } |
---|
4067 | |
---|
4068 | /* |
---|
4069 | *---------------------------------------------------------------------- |
---|
4070 | * |
---|
4071 | * TclInvalidateNsPath -- |
---|
4072 | * |
---|
4073 | * Invalidate the name resolution caches for all names looked up in |
---|
4074 | * namespaces whose name path includes the given namespace. |
---|
4075 | * |
---|
4076 | * Results: |
---|
4077 | * nothing |
---|
4078 | * |
---|
4079 | * Side effects: |
---|
4080 | * Increments the command reference epoch in each namespace whose path |
---|
4081 | * includes the given namespace. This causes any cached resolved names |
---|
4082 | * whose root cacheing context starts at that namespace to be recomputed |
---|
4083 | * the next time they are used. |
---|
4084 | * |
---|
4085 | *---------------------------------------------------------------------- |
---|
4086 | */ |
---|
4087 | |
---|
4088 | void |
---|
4089 | TclInvalidateNsPath( |
---|
4090 | Namespace *nsPtr) |
---|
4091 | { |
---|
4092 | NamespacePathEntry *nsPathPtr = nsPtr->commandPathSourceList; |
---|
4093 | while (nsPathPtr != NULL) { |
---|
4094 | if (nsPathPtr->nsPtr != NULL) { |
---|
4095 | nsPathPtr->creatorNsPtr->cmdRefEpoch++; |
---|
4096 | } |
---|
4097 | nsPathPtr = nsPathPtr->nextPtr; |
---|
4098 | } |
---|
4099 | } |
---|
4100 | |
---|
4101 | /* |
---|
4102 | *---------------------------------------------------------------------- |
---|
4103 | * |
---|
4104 | * NamespaceQualifiersCmd -- |
---|
4105 | * |
---|
4106 | * Invoked to implement the "namespace qualifiers" command that returns |
---|
4107 | * any leading namespace qualifiers in a string. These qualifiers are |
---|
4108 | * namespace names separated by "::"s. For example, for "::foo::p" this |
---|
4109 | * command returns "::foo", and for "::" it returns "". This command is |
---|
4110 | * the complement of the "namespace tail" command. Note that this command |
---|
4111 | * does not check whether the "namespace" names are, in fact, the names |
---|
4112 | * of currently defined namespaces. Handles the following syntax: |
---|
4113 | * |
---|
4114 | * namespace qualifiers string |
---|
4115 | * |
---|
4116 | * Results: |
---|
4117 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
4118 | * |
---|
4119 | * Side effects: |
---|
4120 | * Returns a result in the interpreter's result object. If anything goes |
---|
4121 | * wrong, the result is an error message. |
---|
4122 | * |
---|
4123 | *---------------------------------------------------------------------- |
---|
4124 | */ |
---|
4125 | |
---|
4126 | static int |
---|
4127 | NamespaceQualifiersCmd( |
---|
4128 | ClientData dummy, /* Not used. */ |
---|
4129 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
4130 | int objc, /* Number of arguments. */ |
---|
4131 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
4132 | { |
---|
4133 | register char *name, *p; |
---|
4134 | int length; |
---|
4135 | |
---|
4136 | if (objc != 3) { |
---|
4137 | Tcl_WrongNumArgs(interp, 2, objv, "string"); |
---|
4138 | return TCL_ERROR; |
---|
4139 | } |
---|
4140 | |
---|
4141 | /* |
---|
4142 | * Find the end of the string, then work backward and find the start of |
---|
4143 | * the last "::" qualifier. |
---|
4144 | */ |
---|
4145 | |
---|
4146 | name = TclGetString(objv[2]); |
---|
4147 | for (p = name; *p != '\0'; p++) { |
---|
4148 | /* empty body */ |
---|
4149 | } |
---|
4150 | while (--p >= name) { |
---|
4151 | if ((*p == ':') && (p > name) && (*(p-1) == ':')) { |
---|
4152 | p -= 2; /* Back up over the :: */ |
---|
4153 | while ((p >= name) && (*p == ':')) { |
---|
4154 | p--; /* Back up over the preceeding : */ |
---|
4155 | } |
---|
4156 | break; |
---|
4157 | } |
---|
4158 | } |
---|
4159 | |
---|
4160 | if (p >= name) { |
---|
4161 | length = p-name+1; |
---|
4162 | Tcl_SetObjResult(interp, Tcl_NewStringObj(name, length)); |
---|
4163 | } |
---|
4164 | return TCL_OK; |
---|
4165 | } |
---|
4166 | |
---|
4167 | /* |
---|
4168 | *---------------------------------------------------------------------- |
---|
4169 | * |
---|
4170 | * NamespaceUnknownCmd -- |
---|
4171 | * |
---|
4172 | * Invoked to implement the "namespace unknown" command (TIP 181) that |
---|
4173 | * sets or queries a per-namespace unknown command handler. This handler |
---|
4174 | * is called when command lookup fails (current and global ns). The |
---|
4175 | * default handler for the global namespace is ::unknown. The default |
---|
4176 | * handler for other namespaces is to call the global namespace unknown |
---|
4177 | * handler. Passing an empty list results in resetting the handler to its |
---|
4178 | * default. |
---|
4179 | * |
---|
4180 | * namespace unknown ?handler? |
---|
4181 | * |
---|
4182 | * Results: |
---|
4183 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
4184 | * |
---|
4185 | * Side effects: |
---|
4186 | * If no handler is specified, returns a result in the interpreter's |
---|
4187 | * result object, otherwise it sets the unknown handler pointer in the |
---|
4188 | * current namespace to the script fragment provided. If anything goes |
---|
4189 | * wrong, the result is an error message. |
---|
4190 | * |
---|
4191 | *---------------------------------------------------------------------- |
---|
4192 | */ |
---|
4193 | |
---|
4194 | static int |
---|
4195 | NamespaceUnknownCmd( |
---|
4196 | ClientData dummy, /* Not used. */ |
---|
4197 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
4198 | int objc, /* Number of arguments. */ |
---|
4199 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
4200 | { |
---|
4201 | Tcl_Namespace *currNsPtr; |
---|
4202 | Tcl_Obj *resultPtr; |
---|
4203 | int rc; |
---|
4204 | |
---|
4205 | if (objc > 3) { |
---|
4206 | Tcl_WrongNumArgs(interp, 2, objv, "?script?"); |
---|
4207 | return TCL_ERROR; |
---|
4208 | } |
---|
4209 | |
---|
4210 | currNsPtr = TclGetCurrentNamespace(interp); |
---|
4211 | |
---|
4212 | if (objc == 2) { |
---|
4213 | /* |
---|
4214 | * Introspection - return the current namespace handler. |
---|
4215 | */ |
---|
4216 | |
---|
4217 | resultPtr = Tcl_GetNamespaceUnknownHandler(interp, currNsPtr); |
---|
4218 | if (resultPtr == NULL) { |
---|
4219 | TclNewObj(resultPtr); |
---|
4220 | } |
---|
4221 | Tcl_SetObjResult(interp, resultPtr); |
---|
4222 | } else { |
---|
4223 | rc = Tcl_SetNamespaceUnknownHandler(interp, currNsPtr, objv[2]); |
---|
4224 | if (rc == TCL_OK) { |
---|
4225 | Tcl_SetObjResult(interp, objv[2]); |
---|
4226 | } |
---|
4227 | return rc; |
---|
4228 | } |
---|
4229 | return TCL_OK; |
---|
4230 | } |
---|
4231 | |
---|
4232 | /* |
---|
4233 | *---------------------------------------------------------------------- |
---|
4234 | * |
---|
4235 | * Tcl_GetNamespaceUnknownHandler -- |
---|
4236 | * |
---|
4237 | * Returns the unknown command handler registered for the given |
---|
4238 | * namespace. |
---|
4239 | * |
---|
4240 | * Results: |
---|
4241 | * Returns the current unknown command handler, or NULL if none exists |
---|
4242 | * for the namespace. |
---|
4243 | * |
---|
4244 | * Side effects: |
---|
4245 | * None. |
---|
4246 | * |
---|
4247 | *---------------------------------------------------------------------- |
---|
4248 | */ |
---|
4249 | |
---|
4250 | Tcl_Obj * |
---|
4251 | Tcl_GetNamespaceUnknownHandler( |
---|
4252 | Tcl_Interp *interp, /* The interpreter in which the namespace |
---|
4253 | * exists. */ |
---|
4254 | Tcl_Namespace *nsPtr) /* The namespace. */ |
---|
4255 | { |
---|
4256 | Namespace *currNsPtr = (Namespace *)nsPtr; |
---|
4257 | |
---|
4258 | if (currNsPtr->unknownHandlerPtr == NULL && |
---|
4259 | currNsPtr == ((Interp *)interp)->globalNsPtr) { |
---|
4260 | /* |
---|
4261 | * Default handler for global namespace is "::unknown". For all other |
---|
4262 | * namespaces, it is NULL (which falls back on the global unknown |
---|
4263 | * handler). |
---|
4264 | */ |
---|
4265 | |
---|
4266 | TclNewLiteralStringObj(currNsPtr->unknownHandlerPtr, "::unknown"); |
---|
4267 | Tcl_IncrRefCount(currNsPtr->unknownHandlerPtr); |
---|
4268 | } |
---|
4269 | return currNsPtr->unknownHandlerPtr; |
---|
4270 | } |
---|
4271 | |
---|
4272 | /* |
---|
4273 | *---------------------------------------------------------------------- |
---|
4274 | * |
---|
4275 | * Tcl_SetNamespaceUnknownHandler -- |
---|
4276 | * |
---|
4277 | * Sets the unknown command handler for the given namespace to the |
---|
4278 | * command prefix passed. |
---|
4279 | * |
---|
4280 | * Results: |
---|
4281 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
4282 | * |
---|
4283 | * Side effects: |
---|
4284 | * Sets the namespace unknown command handler. If the passed in handler |
---|
4285 | * is NULL or an empty list, then the handler is reset to its default. If |
---|
4286 | * an error occurs, then an error message is left in the interpreter |
---|
4287 | * result. |
---|
4288 | * |
---|
4289 | *---------------------------------------------------------------------- |
---|
4290 | */ |
---|
4291 | |
---|
4292 | int |
---|
4293 | Tcl_SetNamespaceUnknownHandler( |
---|
4294 | Tcl_Interp *interp, /* Interpreter in which the namespace |
---|
4295 | * exists. */ |
---|
4296 | Tcl_Namespace *nsPtr, /* Namespace which is being updated. */ |
---|
4297 | Tcl_Obj *handlerPtr) /* The new handler, or NULL to reset. */ |
---|
4298 | { |
---|
4299 | int lstlen; |
---|
4300 | Namespace *currNsPtr = (Namespace *)nsPtr; |
---|
4301 | |
---|
4302 | if (currNsPtr->unknownHandlerPtr != NULL) { |
---|
4303 | /* |
---|
4304 | * Remove old handler first. |
---|
4305 | */ |
---|
4306 | |
---|
4307 | Tcl_DecrRefCount(currNsPtr->unknownHandlerPtr); |
---|
4308 | currNsPtr->unknownHandlerPtr = NULL; |
---|
4309 | } |
---|
4310 | |
---|
4311 | /* |
---|
4312 | * If NULL or an empty list is passed, then reset to the default |
---|
4313 | * handler. |
---|
4314 | */ |
---|
4315 | |
---|
4316 | if (handlerPtr == NULL) { |
---|
4317 | currNsPtr->unknownHandlerPtr = NULL; |
---|
4318 | } else if (TclListObjLength(interp, handlerPtr, &lstlen) != TCL_OK) { |
---|
4319 | /* |
---|
4320 | * Not a list. |
---|
4321 | */ |
---|
4322 | |
---|
4323 | return TCL_ERROR; |
---|
4324 | } else if (lstlen == 0) { |
---|
4325 | /* |
---|
4326 | * Empty list - reset to default. |
---|
4327 | */ |
---|
4328 | |
---|
4329 | currNsPtr->unknownHandlerPtr = NULL; |
---|
4330 | } else { |
---|
4331 | /* |
---|
4332 | * Increment ref count and store. The reference count is decremented |
---|
4333 | * either in the code above, or when the namespace is deleted. |
---|
4334 | */ |
---|
4335 | |
---|
4336 | Tcl_IncrRefCount(handlerPtr); |
---|
4337 | currNsPtr->unknownHandlerPtr = handlerPtr; |
---|
4338 | } |
---|
4339 | return TCL_OK; |
---|
4340 | } |
---|
4341 | |
---|
4342 | /* |
---|
4343 | *---------------------------------------------------------------------- |
---|
4344 | * |
---|
4345 | * NamespaceTailCmd -- |
---|
4346 | * |
---|
4347 | * Invoked to implement the "namespace tail" command that returns the |
---|
4348 | * trailing name at the end of a string with "::" namespace qualifiers. |
---|
4349 | * These qualifiers are namespace names separated by "::"s. For example, |
---|
4350 | * for "::foo::p" this command returns "p", and for "::" it returns "". |
---|
4351 | * This command is the complement of the "namespace qualifiers" command. |
---|
4352 | * Note that this command does not check whether the "namespace" names |
---|
4353 | * are, in fact, the names of currently defined namespaces. Handles the |
---|
4354 | * following syntax: |
---|
4355 | * |
---|
4356 | * namespace tail string |
---|
4357 | * |
---|
4358 | * Results: |
---|
4359 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
4360 | * |
---|
4361 | * Side effects: |
---|
4362 | * Returns a result in the interpreter's result object. If anything goes |
---|
4363 | * wrong, the result is an error message. |
---|
4364 | * |
---|
4365 | *---------------------------------------------------------------------- |
---|
4366 | */ |
---|
4367 | |
---|
4368 | static int |
---|
4369 | NamespaceTailCmd( |
---|
4370 | ClientData dummy, /* Not used. */ |
---|
4371 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
4372 | int objc, /* Number of arguments. */ |
---|
4373 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
4374 | { |
---|
4375 | register char *name, *p; |
---|
4376 | |
---|
4377 | if (objc != 3) { |
---|
4378 | Tcl_WrongNumArgs(interp, 2, objv, "string"); |
---|
4379 | return TCL_ERROR; |
---|
4380 | } |
---|
4381 | |
---|
4382 | /* |
---|
4383 | * Find the end of the string, then work backward and find the last "::" |
---|
4384 | * qualifier. |
---|
4385 | */ |
---|
4386 | |
---|
4387 | name = TclGetString(objv[2]); |
---|
4388 | for (p = name; *p != '\0'; p++) { |
---|
4389 | /* empty body */ |
---|
4390 | } |
---|
4391 | while (--p > name) { |
---|
4392 | if ((*p == ':') && (*(p-1) == ':')) { |
---|
4393 | p++; /* Just after the last "::" */ |
---|
4394 | break; |
---|
4395 | } |
---|
4396 | } |
---|
4397 | |
---|
4398 | if (p >= name) { |
---|
4399 | Tcl_SetObjResult(interp, Tcl_NewStringObj(p, -1)); |
---|
4400 | } |
---|
4401 | return TCL_OK; |
---|
4402 | } |
---|
4403 | |
---|
4404 | /* |
---|
4405 | *---------------------------------------------------------------------- |
---|
4406 | * |
---|
4407 | * NamespaceUpvarCmd -- |
---|
4408 | * |
---|
4409 | * Invoked to implement the "namespace upvar" command, that creates |
---|
4410 | * variables in the current scope linked to variables in another |
---|
4411 | * namespace. Handles the following syntax: |
---|
4412 | * |
---|
4413 | * namespace upvar ns otherVar myVar ?otherVar myVar ...? |
---|
4414 | * |
---|
4415 | * Results: |
---|
4416 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
4417 | * |
---|
4418 | * Side effects: |
---|
4419 | * Creates new variables in the current scope, linked to the |
---|
4420 | * corresponding variables in the stipulated nmamespace. If anything goes |
---|
4421 | * wrong, the result is an error message. |
---|
4422 | * |
---|
4423 | *---------------------------------------------------------------------- |
---|
4424 | */ |
---|
4425 | |
---|
4426 | static int |
---|
4427 | NamespaceUpvarCmd( |
---|
4428 | ClientData dummy, /* Not used. */ |
---|
4429 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
4430 | int objc, /* Number of arguments. */ |
---|
4431 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
4432 | { |
---|
4433 | Interp *iPtr = (Interp *) interp; |
---|
4434 | Tcl_Namespace *nsPtr, *savedNsPtr; |
---|
4435 | Var *otherPtr, *arrayPtr; |
---|
4436 | char *myName; |
---|
4437 | |
---|
4438 | if (objc < 5 || !(objc & 1)) { |
---|
4439 | Tcl_WrongNumArgs(interp, 2, objv, |
---|
4440 | "ns otherVar myVar ?otherVar myVar ...?"); |
---|
4441 | return TCL_ERROR; |
---|
4442 | } |
---|
4443 | |
---|
4444 | if (TclGetNamespaceFromObj(interp, objv[2], &nsPtr) != TCL_OK) { |
---|
4445 | return TCL_ERROR; |
---|
4446 | } |
---|
4447 | |
---|
4448 | objc -= 3; |
---|
4449 | objv += 3; |
---|
4450 | |
---|
4451 | for (; objc>0 ; objc-=2, objv+=2) { |
---|
4452 | /* |
---|
4453 | * Locate the other variable |
---|
4454 | */ |
---|
4455 | |
---|
4456 | savedNsPtr = (Tcl_Namespace *) iPtr->varFramePtr->nsPtr; |
---|
4457 | iPtr->varFramePtr->nsPtr = (Namespace *) nsPtr; |
---|
4458 | otherPtr = TclObjLookupVarEx(interp, objv[0], NULL, |
---|
4459 | (TCL_NAMESPACE_ONLY | TCL_LEAVE_ERR_MSG), "access", |
---|
4460 | /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr); |
---|
4461 | iPtr->varFramePtr->nsPtr = (Namespace *) savedNsPtr; |
---|
4462 | if (otherPtr == NULL) { |
---|
4463 | return TCL_ERROR; |
---|
4464 | } |
---|
4465 | |
---|
4466 | /* |
---|
4467 | * Create the new variable and link it to otherPtr. |
---|
4468 | */ |
---|
4469 | |
---|
4470 | myName = TclGetString(objv[1]); |
---|
4471 | if (TclPtrMakeUpvar(interp, otherPtr, myName, 0, -1) != TCL_OK) { |
---|
4472 | return TCL_ERROR; |
---|
4473 | } |
---|
4474 | } |
---|
4475 | |
---|
4476 | return TCL_OK; |
---|
4477 | } |
---|
4478 | |
---|
4479 | /* |
---|
4480 | *---------------------------------------------------------------------- |
---|
4481 | * |
---|
4482 | * NamespaceWhichCmd -- |
---|
4483 | * |
---|
4484 | * Invoked to implement the "namespace which" command that returns the |
---|
4485 | * fully-qualified name of a command or variable. If the specified |
---|
4486 | * command or variable does not exist, it returns "". Handles the |
---|
4487 | * following syntax: |
---|
4488 | * |
---|
4489 | * namespace which ?-command? ?-variable? name |
---|
4490 | * |
---|
4491 | * Results: |
---|
4492 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
4493 | * |
---|
4494 | * Side effects: |
---|
4495 | * Returns a result in the interpreter's result object. If anything goes |
---|
4496 | * wrong, the result is an error message. |
---|
4497 | * |
---|
4498 | *---------------------------------------------------------------------- |
---|
4499 | */ |
---|
4500 | |
---|
4501 | static int |
---|
4502 | NamespaceWhichCmd( |
---|
4503 | ClientData dummy, /* Not used. */ |
---|
4504 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
4505 | int objc, /* Number of arguments. */ |
---|
4506 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
4507 | { |
---|
4508 | static const char *opts[] = { |
---|
4509 | "-command", "-variable", NULL |
---|
4510 | }; |
---|
4511 | int lookupType = 0; |
---|
4512 | Tcl_Obj *resultPtr; |
---|
4513 | |
---|
4514 | if (objc < 3 || objc > 4) { |
---|
4515 | badArgs: |
---|
4516 | Tcl_WrongNumArgs(interp, 2, objv, "?-command? ?-variable? name"); |
---|
4517 | return TCL_ERROR; |
---|
4518 | } else if (objc == 4) { |
---|
4519 | /* |
---|
4520 | * Look for a flag controlling the lookup. |
---|
4521 | */ |
---|
4522 | |
---|
4523 | if (Tcl_GetIndexFromObj(interp, objv[2], opts, "option", 0, |
---|
4524 | &lookupType) != TCL_OK) { |
---|
4525 | /* |
---|
4526 | * Preserve old style of error message! |
---|
4527 | */ |
---|
4528 | |
---|
4529 | Tcl_ResetResult(interp); |
---|
4530 | goto badArgs; |
---|
4531 | } |
---|
4532 | } |
---|
4533 | |
---|
4534 | TclNewObj(resultPtr); |
---|
4535 | switch (lookupType) { |
---|
4536 | case 0: { /* -command */ |
---|
4537 | Tcl_Command cmd = Tcl_GetCommandFromObj(interp, objv[objc-1]); |
---|
4538 | |
---|
4539 | if (cmd != NULL) { |
---|
4540 | Tcl_GetCommandFullName(interp, cmd, resultPtr); |
---|
4541 | } |
---|
4542 | break; |
---|
4543 | } |
---|
4544 | case 1: { /* -variable */ |
---|
4545 | Tcl_Var var = Tcl_FindNamespaceVar(interp, |
---|
4546 | TclGetString(objv[objc-1]), NULL, /*flags*/ 0); |
---|
4547 | |
---|
4548 | if (var != NULL) { |
---|
4549 | Tcl_GetVariableFullName(interp, var, resultPtr); |
---|
4550 | } |
---|
4551 | break; |
---|
4552 | } |
---|
4553 | } |
---|
4554 | Tcl_SetObjResult(interp, resultPtr); |
---|
4555 | return TCL_OK; |
---|
4556 | } |
---|
4557 | |
---|
4558 | /* |
---|
4559 | *---------------------------------------------------------------------- |
---|
4560 | * |
---|
4561 | * FreeNsNameInternalRep -- |
---|
4562 | * |
---|
4563 | * Frees the resources associated with a nsName object's internal |
---|
4564 | * representation. |
---|
4565 | * |
---|
4566 | * Results: |
---|
4567 | * None. |
---|
4568 | * |
---|
4569 | * Side effects: |
---|
4570 | * Decrements the ref count of any Namespace structure pointed to by the |
---|
4571 | * nsName's internal representation. If there are no more references to |
---|
4572 | * the namespace, it's structure will be freed. |
---|
4573 | * |
---|
4574 | *---------------------------------------------------------------------- |
---|
4575 | */ |
---|
4576 | |
---|
4577 | static void |
---|
4578 | FreeNsNameInternalRep( |
---|
4579 | register Tcl_Obj *objPtr) /* nsName object with internal representation |
---|
4580 | * to free. */ |
---|
4581 | { |
---|
4582 | register ResolvedNsName *resNamePtr = (ResolvedNsName *) |
---|
4583 | objPtr->internalRep.twoPtrValue.ptr1; |
---|
4584 | Namespace *nsPtr; |
---|
4585 | |
---|
4586 | /* |
---|
4587 | * Decrement the reference count of the namespace. If there are no more |
---|
4588 | * references, free it up. |
---|
4589 | */ |
---|
4590 | |
---|
4591 | resNamePtr->refCount--; |
---|
4592 | if (resNamePtr->refCount == 0) { |
---|
4593 | |
---|
4594 | /* |
---|
4595 | * Decrement the reference count for the cached namespace. If the |
---|
4596 | * namespace is dead, and there are no more references to it, free |
---|
4597 | * it. |
---|
4598 | */ |
---|
4599 | |
---|
4600 | nsPtr = resNamePtr->nsPtr; |
---|
4601 | nsPtr->refCount--; |
---|
4602 | if ((nsPtr->refCount == 0) && (nsPtr->flags & NS_DEAD)) { |
---|
4603 | NamespaceFree(nsPtr); |
---|
4604 | } |
---|
4605 | ckfree((char *) resNamePtr); |
---|
4606 | } |
---|
4607 | } |
---|
4608 | |
---|
4609 | /* |
---|
4610 | *---------------------------------------------------------------------- |
---|
4611 | * |
---|
4612 | * DupNsNameInternalRep -- |
---|
4613 | * |
---|
4614 | * Initializes the internal representation of a nsName object to a copy |
---|
4615 | * of the internal representation of another nsName object. |
---|
4616 | * |
---|
4617 | * Results: |
---|
4618 | * None. |
---|
4619 | * |
---|
4620 | * Side effects: |
---|
4621 | * copyPtr's internal rep is set to refer to the same namespace |
---|
4622 | * referenced by srcPtr's internal rep. Increments the ref count of the |
---|
4623 | * ResolvedNsName structure used to hold the namespace reference. |
---|
4624 | * |
---|
4625 | *---------------------------------------------------------------------- |
---|
4626 | */ |
---|
4627 | |
---|
4628 | static void |
---|
4629 | DupNsNameInternalRep( |
---|
4630 | Tcl_Obj *srcPtr, /* Object with internal rep to copy. */ |
---|
4631 | register Tcl_Obj *copyPtr) /* Object with internal rep to set. */ |
---|
4632 | { |
---|
4633 | register ResolvedNsName *resNamePtr = (ResolvedNsName *) |
---|
4634 | srcPtr->internalRep.twoPtrValue.ptr1; |
---|
4635 | |
---|
4636 | copyPtr->internalRep.twoPtrValue.ptr1 = resNamePtr; |
---|
4637 | resNamePtr->refCount++; |
---|
4638 | copyPtr->typePtr = &nsNameType; |
---|
4639 | } |
---|
4640 | |
---|
4641 | /* |
---|
4642 | *---------------------------------------------------------------------- |
---|
4643 | * |
---|
4644 | * SetNsNameFromAny -- |
---|
4645 | * |
---|
4646 | * Attempt to generate a nsName internal representation for a Tcl object. |
---|
4647 | * |
---|
4648 | * Results: |
---|
4649 | * Returns TCL_OK if the value could be converted to a proper namespace |
---|
4650 | * reference. Otherwise, it returns TCL_ERROR, along with an error |
---|
4651 | * message in the interpreter's result object. |
---|
4652 | * |
---|
4653 | * Side effects: |
---|
4654 | * If successful, the object is made a nsName object. Its internal rep is |
---|
4655 | * set to point to a ResolvedNsName, which contains a cached pointer to |
---|
4656 | * the Namespace. Reference counts are kept on both the ResolvedNsName |
---|
4657 | * and the Namespace, so we can keep track of their usage and free them |
---|
4658 | * when appropriate. |
---|
4659 | * |
---|
4660 | *---------------------------------------------------------------------- |
---|
4661 | */ |
---|
4662 | |
---|
4663 | static int |
---|
4664 | SetNsNameFromAny( |
---|
4665 | Tcl_Interp *interp, /* Points to the namespace in which to resolve |
---|
4666 | * name. Also used for error reporting if not |
---|
4667 | * NULL. */ |
---|
4668 | register Tcl_Obj *objPtr) /* The object to convert. */ |
---|
4669 | { |
---|
4670 | const char *dummy; |
---|
4671 | Namespace *nsPtr, *dummy1Ptr, *dummy2Ptr; |
---|
4672 | register ResolvedNsName *resNamePtr; |
---|
4673 | const char *name = TclGetString(objPtr); |
---|
4674 | |
---|
4675 | TclGetNamespaceForQualName(interp, name, NULL, TCL_FIND_ONLY_NS, |
---|
4676 | &nsPtr, &dummy1Ptr, &dummy2Ptr, &dummy); |
---|
4677 | |
---|
4678 | /* |
---|
4679 | * If we found a namespace, then create a new ResolvedNsName structure |
---|
4680 | * that holds a reference to it. |
---|
4681 | */ |
---|
4682 | |
---|
4683 | if ((nsPtr == NULL) || (nsPtr->flags & NS_DYING)) { |
---|
4684 | /* |
---|
4685 | * Our failed lookup proves any previously cached nsName intrep is no |
---|
4686 | * longer valid. Get rid of it so we no longer waste memory storing |
---|
4687 | * it, nor time determining its invalidity again and again. |
---|
4688 | */ |
---|
4689 | |
---|
4690 | if (objPtr->typePtr == &nsNameType) { |
---|
4691 | TclFreeIntRep(objPtr); |
---|
4692 | objPtr->typePtr = NULL; |
---|
4693 | } |
---|
4694 | return TCL_ERROR; |
---|
4695 | } |
---|
4696 | |
---|
4697 | nsPtr->refCount++; |
---|
4698 | resNamePtr = (ResolvedNsName *) ckalloc(sizeof(ResolvedNsName)); |
---|
4699 | resNamePtr->nsPtr = nsPtr; |
---|
4700 | if ((name[0] == ':') && (name[1] == ':')) { |
---|
4701 | resNamePtr->refNsPtr = NULL; |
---|
4702 | } else { |
---|
4703 | resNamePtr->refNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp); |
---|
4704 | } |
---|
4705 | resNamePtr->refCount = 1; |
---|
4706 | TclFreeIntRep(objPtr); |
---|
4707 | objPtr->internalRep.twoPtrValue.ptr1 = resNamePtr; |
---|
4708 | objPtr->typePtr = &nsNameType; |
---|
4709 | return TCL_OK; |
---|
4710 | } |
---|
4711 | |
---|
4712 | /* |
---|
4713 | *---------------------------------------------------------------------- |
---|
4714 | * |
---|
4715 | * NamespaceEnsembleCmd -- |
---|
4716 | * |
---|
4717 | * Invoked to implement the "namespace ensemble" command that creates and |
---|
4718 | * manipulates ensembles built on top of namespaces. Handles the |
---|
4719 | * following syntax: |
---|
4720 | * |
---|
4721 | * namespace ensemble name ?dictionary? |
---|
4722 | * |
---|
4723 | * Results: |
---|
4724 | * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. |
---|
4725 | * |
---|
4726 | * Side effects: |
---|
4727 | * Creates the ensemble for the namespace if one did not previously |
---|
4728 | * exist. Alternatively, alters the way that the ensemble's subcommand => |
---|
4729 | * implementation prefix is configured. |
---|
4730 | * |
---|
4731 | *---------------------------------------------------------------------- |
---|
4732 | */ |
---|
4733 | |
---|
4734 | static int |
---|
4735 | NamespaceEnsembleCmd( |
---|
4736 | ClientData dummy, |
---|
4737 | Tcl_Interp *interp, |
---|
4738 | int objc, |
---|
4739 | Tcl_Obj *const objv[]) |
---|
4740 | { |
---|
4741 | Namespace *nsPtr; |
---|
4742 | Tcl_Command token; |
---|
4743 | static const char *subcommands[] = { |
---|
4744 | "configure", "create", "exists", NULL |
---|
4745 | }; |
---|
4746 | enum EnsSubcmds { |
---|
4747 | ENS_CONFIG, ENS_CREATE, ENS_EXISTS |
---|
4748 | }; |
---|
4749 | static const char *createOptions[] = { |
---|
4750 | "-command", "-map", "-prefixes", "-subcommands", "-unknown", NULL |
---|
4751 | }; |
---|
4752 | enum EnsCreateOpts { |
---|
4753 | CRT_CMD, CRT_MAP, CRT_PREFIX, CRT_SUBCMDS, CRT_UNKNOWN |
---|
4754 | }; |
---|
4755 | static const char *configOptions[] = { |
---|
4756 | "-map", "-namespace", "-prefixes", "-subcommands", "-unknown", NULL |
---|
4757 | }; |
---|
4758 | enum EnsConfigOpts { |
---|
4759 | CONF_MAP, CONF_NAMESPACE, CONF_PREFIX, CONF_SUBCMDS, CONF_UNKNOWN |
---|
4760 | }; |
---|
4761 | int index; |
---|
4762 | |
---|
4763 | nsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
4764 | if (nsPtr == NULL || nsPtr->flags & NS_DYING) { |
---|
4765 | if (!Tcl_InterpDeleted(interp)) { |
---|
4766 | Tcl_AppendResult(interp, |
---|
4767 | "tried to manipulate ensemble of deleted namespace", NULL); |
---|
4768 | } |
---|
4769 | return TCL_ERROR; |
---|
4770 | } |
---|
4771 | |
---|
4772 | if (objc < 3) { |
---|
4773 | Tcl_WrongNumArgs(interp, 2, objv, "subcommand ?arg ...?"); |
---|
4774 | return TCL_ERROR; |
---|
4775 | } |
---|
4776 | if (Tcl_GetIndexFromObj(interp, objv[2], subcommands, "subcommand", 0, |
---|
4777 | &index) != TCL_OK) { |
---|
4778 | return TCL_ERROR; |
---|
4779 | } |
---|
4780 | |
---|
4781 | switch ((enum EnsSubcmds) index) { |
---|
4782 | case ENS_CREATE: { |
---|
4783 | char *name; |
---|
4784 | Tcl_DictSearch search; |
---|
4785 | Tcl_Obj *listObj; |
---|
4786 | int done, len, allocatedMapFlag = 0; |
---|
4787 | /* |
---|
4788 | * Defaults |
---|
4789 | */ |
---|
4790 | Tcl_Obj *subcmdObj = NULL; |
---|
4791 | Tcl_Obj *mapObj = NULL; |
---|
4792 | int permitPrefix = 1; |
---|
4793 | Tcl_Obj *unknownObj = NULL; |
---|
4794 | |
---|
4795 | objv += 3; |
---|
4796 | objc -= 3; |
---|
4797 | |
---|
4798 | /* |
---|
4799 | * Work out what name to use for the command to create. If supplied, |
---|
4800 | * it is either fully specified or relative to the current namespace. |
---|
4801 | * If not supplied, it is exactly the name of the current namespace. |
---|
4802 | */ |
---|
4803 | |
---|
4804 | name = nsPtr->fullName; |
---|
4805 | |
---|
4806 | /* |
---|
4807 | * Parse the option list, applying type checks as we go. Note that we |
---|
4808 | * are not incrementing any reference counts in the objects at this |
---|
4809 | * stage, so the presence of an option multiple times won't cause any |
---|
4810 | * memory leaks. |
---|
4811 | */ |
---|
4812 | |
---|
4813 | for (; objc>1 ; objc-=2,objv+=2 ) { |
---|
4814 | if (Tcl_GetIndexFromObj(interp, objv[0], createOptions, "option", |
---|
4815 | 0, &index) != TCL_OK) { |
---|
4816 | if (allocatedMapFlag) { |
---|
4817 | Tcl_DecrRefCount(mapObj); |
---|
4818 | } |
---|
4819 | return TCL_ERROR; |
---|
4820 | } |
---|
4821 | switch ((enum EnsCreateOpts) index) { |
---|
4822 | case CRT_CMD: |
---|
4823 | name = TclGetString(objv[1]); |
---|
4824 | continue; |
---|
4825 | case CRT_SUBCMDS: |
---|
4826 | if (TclListObjLength(interp, objv[1], &len) != TCL_OK) { |
---|
4827 | if (allocatedMapFlag) { |
---|
4828 | Tcl_DecrRefCount(mapObj); |
---|
4829 | } |
---|
4830 | return TCL_ERROR; |
---|
4831 | } |
---|
4832 | subcmdObj = (len > 0 ? objv[1] : NULL); |
---|
4833 | continue; |
---|
4834 | case CRT_MAP: { |
---|
4835 | Tcl_Obj *patchedDict = NULL, *subcmdObj; |
---|
4836 | |
---|
4837 | /* |
---|
4838 | * Verify that the map is sensible. |
---|
4839 | */ |
---|
4840 | |
---|
4841 | if (Tcl_DictObjFirst(interp, objv[1], &search, |
---|
4842 | &subcmdObj, &listObj, &done) != TCL_OK) { |
---|
4843 | if (allocatedMapFlag) { |
---|
4844 | Tcl_DecrRefCount(mapObj); |
---|
4845 | } |
---|
4846 | return TCL_ERROR; |
---|
4847 | } |
---|
4848 | if (done) { |
---|
4849 | mapObj = NULL; |
---|
4850 | continue; |
---|
4851 | } |
---|
4852 | do { |
---|
4853 | Tcl_Obj **listv; |
---|
4854 | char *cmd; |
---|
4855 | |
---|
4856 | if (TclListObjGetElements(interp, listObj, &len, |
---|
4857 | &listv) != TCL_OK) { |
---|
4858 | Tcl_DictObjDone(&search); |
---|
4859 | if (patchedDict) { |
---|
4860 | Tcl_DecrRefCount(patchedDict); |
---|
4861 | } |
---|
4862 | if (allocatedMapFlag) { |
---|
4863 | Tcl_DecrRefCount(mapObj); |
---|
4864 | } |
---|
4865 | return TCL_ERROR; |
---|
4866 | } |
---|
4867 | if (len < 1) { |
---|
4868 | Tcl_SetResult(interp, |
---|
4869 | "ensemble subcommand implementations " |
---|
4870 | "must be non-empty lists", TCL_STATIC); |
---|
4871 | Tcl_DictObjDone(&search); |
---|
4872 | if (patchedDict) { |
---|
4873 | Tcl_DecrRefCount(patchedDict); |
---|
4874 | } |
---|
4875 | if (allocatedMapFlag) { |
---|
4876 | Tcl_DecrRefCount(mapObj); |
---|
4877 | } |
---|
4878 | return TCL_ERROR; |
---|
4879 | } |
---|
4880 | cmd = TclGetString(listv[0]); |
---|
4881 | if (!(cmd[0] == ':' && cmd[1] == ':')) { |
---|
4882 | Tcl_Obj *newList = Tcl_NewListObj(len, listv); |
---|
4883 | Tcl_Obj *newCmd = Tcl_NewStringObj(nsPtr->fullName,-1); |
---|
4884 | |
---|
4885 | if (nsPtr->parentPtr) { |
---|
4886 | Tcl_AppendStringsToObj(newCmd, "::", NULL); |
---|
4887 | } |
---|
4888 | Tcl_AppendObjToObj(newCmd, listv[0]); |
---|
4889 | Tcl_ListObjReplace(NULL, newList, 0, 1, 1, &newCmd); |
---|
4890 | if (patchedDict == NULL) { |
---|
4891 | patchedDict = Tcl_DuplicateObj(objv[1]); |
---|
4892 | } |
---|
4893 | Tcl_DictObjPut(NULL, patchedDict, subcmdObj, newList); |
---|
4894 | } |
---|
4895 | Tcl_DictObjNext(&search, &subcmdObj, &listObj, &done); |
---|
4896 | } while (!done); |
---|
4897 | |
---|
4898 | if (allocatedMapFlag) { |
---|
4899 | Tcl_DecrRefCount(mapObj); |
---|
4900 | } |
---|
4901 | mapObj = (patchedDict ? patchedDict : objv[1]); |
---|
4902 | if (patchedDict) { |
---|
4903 | allocatedMapFlag = 1; |
---|
4904 | } |
---|
4905 | continue; |
---|
4906 | } |
---|
4907 | case CRT_PREFIX: |
---|
4908 | if (Tcl_GetBooleanFromObj(interp, objv[1], |
---|
4909 | &permitPrefix) != TCL_OK) { |
---|
4910 | if (allocatedMapFlag) { |
---|
4911 | Tcl_DecrRefCount(mapObj); |
---|
4912 | } |
---|
4913 | return TCL_ERROR; |
---|
4914 | } |
---|
4915 | continue; |
---|
4916 | case CRT_UNKNOWN: |
---|
4917 | if (TclListObjLength(interp, objv[1], &len) != TCL_OK) { |
---|
4918 | if (allocatedMapFlag) { |
---|
4919 | Tcl_DecrRefCount(mapObj); |
---|
4920 | } |
---|
4921 | return TCL_ERROR; |
---|
4922 | } |
---|
4923 | unknownObj = (len > 0 ? objv[1] : NULL); |
---|
4924 | continue; |
---|
4925 | } |
---|
4926 | } |
---|
4927 | |
---|
4928 | /* |
---|
4929 | * Create the ensemble. Note that this might delete another ensemble |
---|
4930 | * linked to the same namespace, so we must be careful. However, we |
---|
4931 | * should be OK because we only link the namespace into the list once |
---|
4932 | * we've created it (and after any deletions have occurred.) |
---|
4933 | */ |
---|
4934 | |
---|
4935 | token = Tcl_CreateEnsemble(interp, name, NULL, |
---|
4936 | (permitPrefix ? TCL_ENSEMBLE_PREFIX : 0)); |
---|
4937 | Tcl_SetEnsembleSubcommandList(interp, token, subcmdObj); |
---|
4938 | Tcl_SetEnsembleMappingDict(interp, token, mapObj); |
---|
4939 | Tcl_SetEnsembleUnknownHandler(interp, token, unknownObj); |
---|
4940 | |
---|
4941 | /* |
---|
4942 | * Tricky! Must ensure that the result is not shared (command delete |
---|
4943 | * traces could have corrupted the pristine object that we started |
---|
4944 | * with). [Snit test rename-1.5] |
---|
4945 | */ |
---|
4946 | |
---|
4947 | Tcl_ResetResult(interp); |
---|
4948 | Tcl_GetCommandFullName(interp, token, Tcl_GetObjResult(interp)); |
---|
4949 | return TCL_OK; |
---|
4950 | } |
---|
4951 | |
---|
4952 | case ENS_EXISTS: |
---|
4953 | if (objc != 4) { |
---|
4954 | Tcl_WrongNumArgs(interp, 3, objv, "cmdname"); |
---|
4955 | return TCL_ERROR; |
---|
4956 | } |
---|
4957 | Tcl_SetObjResult(interp, Tcl_NewBooleanObj( |
---|
4958 | Tcl_FindEnsemble(interp, objv[3], 0) != NULL)); |
---|
4959 | return TCL_OK; |
---|
4960 | |
---|
4961 | case ENS_CONFIG: |
---|
4962 | if (objc < 4 || (objc != 5 && objc & 1)) { |
---|
4963 | Tcl_WrongNumArgs(interp, 3, objv, "cmdname ?opt? ?value? ..."); |
---|
4964 | return TCL_ERROR; |
---|
4965 | } |
---|
4966 | token = Tcl_FindEnsemble(interp, objv[3], TCL_LEAVE_ERR_MSG); |
---|
4967 | if (token == NULL) { |
---|
4968 | return TCL_ERROR; |
---|
4969 | } |
---|
4970 | |
---|
4971 | if (objc == 5) { |
---|
4972 | Tcl_Obj *resultObj = NULL; /* silence gcc 4 warning */ |
---|
4973 | |
---|
4974 | if (Tcl_GetIndexFromObj(interp, objv[4], configOptions, "option", |
---|
4975 | 0, &index) != TCL_OK) { |
---|
4976 | return TCL_ERROR; |
---|
4977 | } |
---|
4978 | switch ((enum EnsConfigOpts) index) { |
---|
4979 | case CONF_SUBCMDS: |
---|
4980 | Tcl_GetEnsembleSubcommandList(NULL, token, &resultObj); |
---|
4981 | if (resultObj != NULL) { |
---|
4982 | Tcl_SetObjResult(interp, resultObj); |
---|
4983 | } |
---|
4984 | break; |
---|
4985 | case CONF_MAP: |
---|
4986 | Tcl_GetEnsembleMappingDict(NULL, token, &resultObj); |
---|
4987 | if (resultObj != NULL) { |
---|
4988 | Tcl_SetObjResult(interp, resultObj); |
---|
4989 | } |
---|
4990 | break; |
---|
4991 | case CONF_NAMESPACE: { |
---|
4992 | Tcl_Namespace *namespacePtr = NULL; /* silence gcc 4 warning */ |
---|
4993 | |
---|
4994 | Tcl_GetEnsembleNamespace(NULL, token, &namespacePtr); |
---|
4995 | Tcl_SetResult(interp, ((Namespace *)namespacePtr)->fullName, |
---|
4996 | TCL_VOLATILE); |
---|
4997 | break; |
---|
4998 | } |
---|
4999 | case CONF_PREFIX: { |
---|
5000 | int flags = 0; /* silence gcc 4 warning */ |
---|
5001 | |
---|
5002 | Tcl_GetEnsembleFlags(NULL, token, &flags); |
---|
5003 | Tcl_SetObjResult(interp, |
---|
5004 | Tcl_NewBooleanObj(flags & TCL_ENSEMBLE_PREFIX)); |
---|
5005 | break; |
---|
5006 | } |
---|
5007 | case CONF_UNKNOWN: |
---|
5008 | Tcl_GetEnsembleUnknownHandler(NULL, token, &resultObj); |
---|
5009 | if (resultObj != NULL) { |
---|
5010 | Tcl_SetObjResult(interp, resultObj); |
---|
5011 | } |
---|
5012 | break; |
---|
5013 | } |
---|
5014 | return TCL_OK; |
---|
5015 | |
---|
5016 | } else if (objc == 4) { |
---|
5017 | /* |
---|
5018 | * Produce list of all information. |
---|
5019 | */ |
---|
5020 | |
---|
5021 | Tcl_Obj *resultObj, *tmpObj = NULL; /* silence gcc 4 warning */ |
---|
5022 | Tcl_Namespace *namespacePtr = NULL; /* silence gcc 4 warning */ |
---|
5023 | int flags = 0; /* silence gcc 4 warning */ |
---|
5024 | |
---|
5025 | TclNewObj(resultObj); |
---|
5026 | |
---|
5027 | /* -map option */ |
---|
5028 | Tcl_ListObjAppendElement(NULL, resultObj, |
---|
5029 | Tcl_NewStringObj(configOptions[CONF_MAP], -1)); |
---|
5030 | Tcl_GetEnsembleMappingDict(NULL, token, &tmpObj); |
---|
5031 | Tcl_ListObjAppendElement(NULL, resultObj, |
---|
5032 | (tmpObj != NULL) ? tmpObj : Tcl_NewObj()); |
---|
5033 | |
---|
5034 | /* -namespace option */ |
---|
5035 | Tcl_ListObjAppendElement(NULL, resultObj, |
---|
5036 | Tcl_NewStringObj(configOptions[CONF_NAMESPACE], -1)); |
---|
5037 | Tcl_GetEnsembleNamespace(NULL, token, &namespacePtr); |
---|
5038 | Tcl_ListObjAppendElement(NULL, resultObj, |
---|
5039 | Tcl_NewStringObj(((Namespace *)namespacePtr)->fullName, |
---|
5040 | -1)); |
---|
5041 | |
---|
5042 | /* -prefix option */ |
---|
5043 | Tcl_ListObjAppendElement(NULL, resultObj, |
---|
5044 | Tcl_NewStringObj(configOptions[CONF_PREFIX], -1)); |
---|
5045 | Tcl_GetEnsembleFlags(NULL, token, &flags); |
---|
5046 | Tcl_ListObjAppendElement(NULL, resultObj, |
---|
5047 | Tcl_NewBooleanObj(flags & TCL_ENSEMBLE_PREFIX)); |
---|
5048 | |
---|
5049 | /* -subcommands option */ |
---|
5050 | Tcl_ListObjAppendElement(NULL, resultObj, |
---|
5051 | Tcl_NewStringObj(configOptions[CONF_SUBCMDS], -1)); |
---|
5052 | Tcl_GetEnsembleSubcommandList(NULL, token, &tmpObj); |
---|
5053 | Tcl_ListObjAppendElement(NULL, resultObj, |
---|
5054 | (tmpObj != NULL) ? tmpObj : Tcl_NewObj()); |
---|
5055 | |
---|
5056 | /* -unknown option */ |
---|
5057 | Tcl_ListObjAppendElement(NULL, resultObj, |
---|
5058 | Tcl_NewStringObj(configOptions[CONF_UNKNOWN], -1)); |
---|
5059 | Tcl_GetEnsembleUnknownHandler(NULL, token, &tmpObj); |
---|
5060 | Tcl_ListObjAppendElement(NULL, resultObj, |
---|
5061 | (tmpObj != NULL) ? tmpObj : Tcl_NewObj()); |
---|
5062 | |
---|
5063 | Tcl_SetObjResult(interp, resultObj); |
---|
5064 | return TCL_OK; |
---|
5065 | } else { |
---|
5066 | Tcl_DictSearch search; |
---|
5067 | Tcl_Obj *listObj; |
---|
5068 | int done, len, allocatedMapFlag = 0; |
---|
5069 | Tcl_Obj *subcmdObj = NULL, *mapObj = NULL, |
---|
5070 | *unknownObj = NULL; /* Defaults, silence gcc 4 warnings */ |
---|
5071 | int permitPrefix, flags = 0; /* silence gcc 4 warning */ |
---|
5072 | |
---|
5073 | Tcl_GetEnsembleSubcommandList(NULL, token, &subcmdObj); |
---|
5074 | Tcl_GetEnsembleMappingDict(NULL, token, &mapObj); |
---|
5075 | Tcl_GetEnsembleUnknownHandler(NULL, token, &unknownObj); |
---|
5076 | Tcl_GetEnsembleFlags(NULL, token, &flags); |
---|
5077 | permitPrefix = (flags & TCL_ENSEMBLE_PREFIX) != 0; |
---|
5078 | |
---|
5079 | objv += 4; |
---|
5080 | objc -= 4; |
---|
5081 | |
---|
5082 | /* |
---|
5083 | * Parse the option list, applying type checks as we go. Note that |
---|
5084 | * we are not incrementing any reference counts in the objects at |
---|
5085 | * this stage, so the presence of an option multiple times won't |
---|
5086 | * cause any memory leaks. |
---|
5087 | */ |
---|
5088 | |
---|
5089 | for (; objc>0 ; objc-=2,objv+=2 ) { |
---|
5090 | if (Tcl_GetIndexFromObj(interp, objv[0], configOptions, |
---|
5091 | "option", 0, &index) != TCL_OK) { |
---|
5092 | if (allocatedMapFlag) { |
---|
5093 | Tcl_DecrRefCount(mapObj); |
---|
5094 | } |
---|
5095 | return TCL_ERROR; |
---|
5096 | } |
---|
5097 | switch ((enum EnsConfigOpts) index) { |
---|
5098 | case CONF_SUBCMDS: |
---|
5099 | if (TclListObjLength(interp, objv[1], &len) != TCL_OK) { |
---|
5100 | if (allocatedMapFlag) { |
---|
5101 | Tcl_DecrRefCount(mapObj); |
---|
5102 | } |
---|
5103 | return TCL_ERROR; |
---|
5104 | } |
---|
5105 | subcmdObj = (len > 0 ? objv[1] : NULL); |
---|
5106 | continue; |
---|
5107 | case CONF_MAP: { |
---|
5108 | Tcl_Obj *patchedDict = NULL, *subcmdObj; |
---|
5109 | |
---|
5110 | /* |
---|
5111 | * Verify that the map is sensible. |
---|
5112 | */ |
---|
5113 | |
---|
5114 | if (Tcl_DictObjFirst(interp, objv[1], &search, |
---|
5115 | &subcmdObj, &listObj, &done) != TCL_OK) { |
---|
5116 | if (allocatedMapFlag) { |
---|
5117 | Tcl_DecrRefCount(mapObj); |
---|
5118 | } |
---|
5119 | return TCL_ERROR; |
---|
5120 | } |
---|
5121 | if (done) { |
---|
5122 | mapObj = NULL; |
---|
5123 | continue; |
---|
5124 | } |
---|
5125 | do { |
---|
5126 | Tcl_Obj **listv; |
---|
5127 | char *cmd; |
---|
5128 | |
---|
5129 | if (TclListObjGetElements(interp, listObj, &len, |
---|
5130 | &listv) != TCL_OK) { |
---|
5131 | Tcl_DictObjDone(&search); |
---|
5132 | if (patchedDict) { |
---|
5133 | Tcl_DecrRefCount(patchedDict); |
---|
5134 | } |
---|
5135 | if (allocatedMapFlag) { |
---|
5136 | Tcl_DecrRefCount(mapObj); |
---|
5137 | } |
---|
5138 | return TCL_ERROR; |
---|
5139 | } |
---|
5140 | if (len < 1) { |
---|
5141 | Tcl_SetResult(interp, |
---|
5142 | "ensemble subcommand implementations " |
---|
5143 | "must be non-empty lists", TCL_STATIC); |
---|
5144 | Tcl_DictObjDone(&search); |
---|
5145 | if (patchedDict) { |
---|
5146 | Tcl_DecrRefCount(patchedDict); |
---|
5147 | } |
---|
5148 | if (allocatedMapFlag) { |
---|
5149 | Tcl_DecrRefCount(mapObj); |
---|
5150 | } |
---|
5151 | return TCL_ERROR; |
---|
5152 | } |
---|
5153 | cmd = TclGetString(listv[0]); |
---|
5154 | if (!(cmd[0] == ':' && cmd[1] == ':')) { |
---|
5155 | Tcl_Obj *newList = Tcl_NewListObj(len, listv); |
---|
5156 | Tcl_Obj *newCmd = |
---|
5157 | Tcl_NewStringObj(nsPtr->fullName, -1); |
---|
5158 | if (nsPtr->parentPtr) { |
---|
5159 | Tcl_AppendStringsToObj(newCmd, "::", NULL); |
---|
5160 | } |
---|
5161 | Tcl_AppendObjToObj(newCmd, listv[0]); |
---|
5162 | Tcl_ListObjReplace(NULL, newList, 0,1, 1,&newCmd); |
---|
5163 | if (patchedDict == NULL) { |
---|
5164 | patchedDict = Tcl_DuplicateObj(objv[1]); |
---|
5165 | } |
---|
5166 | Tcl_DictObjPut(NULL, patchedDict, subcmdObj, |
---|
5167 | newList); |
---|
5168 | } |
---|
5169 | Tcl_DictObjNext(&search, &subcmdObj, &listObj, &done); |
---|
5170 | } while (!done); |
---|
5171 | if (allocatedMapFlag) { |
---|
5172 | Tcl_DecrRefCount(mapObj); |
---|
5173 | } |
---|
5174 | mapObj = (patchedDict ? patchedDict : objv[1]); |
---|
5175 | if (patchedDict) { |
---|
5176 | allocatedMapFlag = 1; |
---|
5177 | } |
---|
5178 | continue; |
---|
5179 | } |
---|
5180 | case CONF_NAMESPACE: |
---|
5181 | if (allocatedMapFlag) { |
---|
5182 | Tcl_DecrRefCount(mapObj); |
---|
5183 | } |
---|
5184 | Tcl_AppendResult(interp, "option -namespace is read-only", |
---|
5185 | NULL); |
---|
5186 | return TCL_ERROR; |
---|
5187 | case CONF_PREFIX: |
---|
5188 | if (Tcl_GetBooleanFromObj(interp, objv[1], |
---|
5189 | &permitPrefix) != TCL_OK) { |
---|
5190 | if (allocatedMapFlag) { |
---|
5191 | Tcl_DecrRefCount(mapObj); |
---|
5192 | } |
---|
5193 | return TCL_ERROR; |
---|
5194 | } |
---|
5195 | continue; |
---|
5196 | case CONF_UNKNOWN: |
---|
5197 | if (TclListObjLength(interp, objv[1], &len) != TCL_OK) { |
---|
5198 | if (allocatedMapFlag) { |
---|
5199 | Tcl_DecrRefCount(mapObj); |
---|
5200 | } |
---|
5201 | return TCL_ERROR; |
---|
5202 | } |
---|
5203 | unknownObj = (len > 0 ? objv[1] : NULL); |
---|
5204 | continue; |
---|
5205 | } |
---|
5206 | } |
---|
5207 | |
---|
5208 | /* |
---|
5209 | * Update the namespace now that we've finished the parsing stage. |
---|
5210 | */ |
---|
5211 | |
---|
5212 | flags = (permitPrefix ? flags|TCL_ENSEMBLE_PREFIX |
---|
5213 | : flags&~TCL_ENSEMBLE_PREFIX); |
---|
5214 | Tcl_SetEnsembleSubcommandList(interp, token, subcmdObj); |
---|
5215 | Tcl_SetEnsembleMappingDict(interp, token, mapObj); |
---|
5216 | Tcl_SetEnsembleUnknownHandler(interp, token, unknownObj); |
---|
5217 | Tcl_SetEnsembleFlags(interp, token, flags); |
---|
5218 | return TCL_OK; |
---|
5219 | } |
---|
5220 | |
---|
5221 | default: |
---|
5222 | Tcl_Panic("unexpected ensemble command"); |
---|
5223 | } |
---|
5224 | return TCL_OK; |
---|
5225 | } |
---|
5226 | |
---|
5227 | /* |
---|
5228 | *---------------------------------------------------------------------- |
---|
5229 | * |
---|
5230 | * Tcl_CreateEnsemble -- |
---|
5231 | * |
---|
5232 | * Create a simple ensemble attached to the given namespace. |
---|
5233 | * |
---|
5234 | * Results: |
---|
5235 | * The token for the command created. |
---|
5236 | * |
---|
5237 | * Side effects: |
---|
5238 | * The ensemble is created and marked for compilation. |
---|
5239 | * |
---|
5240 | *---------------------------------------------------------------------- |
---|
5241 | */ |
---|
5242 | |
---|
5243 | Tcl_Command |
---|
5244 | Tcl_CreateEnsemble( |
---|
5245 | Tcl_Interp *interp, |
---|
5246 | const char *name, |
---|
5247 | Tcl_Namespace *namespacePtr, |
---|
5248 | int flags) |
---|
5249 | { |
---|
5250 | Namespace *nsPtr = (Namespace *) namespacePtr; |
---|
5251 | EnsembleConfig *ensemblePtr = (EnsembleConfig *) |
---|
5252 | ckalloc(sizeof(EnsembleConfig)); |
---|
5253 | Tcl_Obj *nameObj = NULL; |
---|
5254 | |
---|
5255 | if (nsPtr == NULL) { |
---|
5256 | nsPtr = (Namespace *) TclGetCurrentNamespace(interp); |
---|
5257 | } |
---|
5258 | |
---|
5259 | /* |
---|
5260 | * Make the name of the ensemble into a fully qualified name. This might |
---|
5261 | * allocate a temporary object. |
---|
5262 | */ |
---|
5263 | |
---|
5264 | if (!(name[0] == ':' && name[1] == ':')) { |
---|
5265 | nameObj = Tcl_NewStringObj(nsPtr->fullName, -1); |
---|
5266 | if (nsPtr->parentPtr == NULL) { |
---|
5267 | Tcl_AppendStringsToObj(nameObj, name, NULL); |
---|
5268 | } else { |
---|
5269 | Tcl_AppendStringsToObj(nameObj, "::", name, NULL); |
---|
5270 | } |
---|
5271 | Tcl_IncrRefCount(nameObj); |
---|
5272 | name = TclGetString(nameObj); |
---|
5273 | } |
---|
5274 | |
---|
5275 | ensemblePtr->nsPtr = nsPtr; |
---|
5276 | ensemblePtr->epoch = 0; |
---|
5277 | Tcl_InitHashTable(&ensemblePtr->subcommandTable, TCL_STRING_KEYS); |
---|
5278 | ensemblePtr->subcommandArrayPtr = NULL; |
---|
5279 | ensemblePtr->subcmdList = NULL; |
---|
5280 | ensemblePtr->subcommandDict = NULL; |
---|
5281 | ensemblePtr->flags = flags; |
---|
5282 | ensemblePtr->unknownHandler = NULL; |
---|
5283 | ensemblePtr->token = Tcl_CreateObjCommand(interp, name, |
---|
5284 | NsEnsembleImplementationCmd, ensemblePtr, DeleteEnsembleConfig); |
---|
5285 | ensemblePtr->next = (EnsembleConfig *) nsPtr->ensembles; |
---|
5286 | nsPtr->ensembles = (Tcl_Ensemble *) ensemblePtr; |
---|
5287 | |
---|
5288 | /* |
---|
5289 | * Trigger an eventual recomputation of the ensemble command set. Note |
---|
5290 | * that this is slightly tricky, as it means that we are not actually |
---|
5291 | * counting the number of namespace export actions, but it is the simplest |
---|
5292 | * way to go! |
---|
5293 | */ |
---|
5294 | |
---|
5295 | nsPtr->exportLookupEpoch++; |
---|
5296 | |
---|
5297 | if (flags & ENSEMBLE_COMPILE) { |
---|
5298 | ((Command *) ensemblePtr->token)->compileProc = TclCompileEnsemble; |
---|
5299 | } |
---|
5300 | |
---|
5301 | if (nameObj != NULL) { |
---|
5302 | TclDecrRefCount(nameObj); |
---|
5303 | } |
---|
5304 | return ensemblePtr->token; |
---|
5305 | } |
---|
5306 | |
---|
5307 | /* |
---|
5308 | *---------------------------------------------------------------------- |
---|
5309 | * |
---|
5310 | * Tcl_SetEnsembleSubcommandList -- |
---|
5311 | * |
---|
5312 | * Set the subcommand list for a particular ensemble. |
---|
5313 | * |
---|
5314 | * Results: |
---|
5315 | * Tcl result code (error if command token does not indicate an ensemble |
---|
5316 | * or the subcommand list - if non-NULL - is not a list). |
---|
5317 | * |
---|
5318 | * Side effects: |
---|
5319 | * The ensemble is updated and marked for recompilation. |
---|
5320 | * |
---|
5321 | *---------------------------------------------------------------------- |
---|
5322 | */ |
---|
5323 | |
---|
5324 | int |
---|
5325 | Tcl_SetEnsembleSubcommandList( |
---|
5326 | Tcl_Interp *interp, |
---|
5327 | Tcl_Command token, |
---|
5328 | Tcl_Obj *subcmdList) |
---|
5329 | { |
---|
5330 | Command *cmdPtr = (Command *) token; |
---|
5331 | EnsembleConfig *ensemblePtr; |
---|
5332 | Tcl_Obj *oldList; |
---|
5333 | |
---|
5334 | if (cmdPtr->objProc != NsEnsembleImplementationCmd) { |
---|
5335 | Tcl_AppendResult(interp, "command is not an ensemble", NULL); |
---|
5336 | return TCL_ERROR; |
---|
5337 | } |
---|
5338 | if (subcmdList != NULL) { |
---|
5339 | int length; |
---|
5340 | |
---|
5341 | if (TclListObjLength(interp, subcmdList, &length) != TCL_OK) { |
---|
5342 | return TCL_ERROR; |
---|
5343 | } |
---|
5344 | if (length < 1) { |
---|
5345 | subcmdList = NULL; |
---|
5346 | } |
---|
5347 | } |
---|
5348 | |
---|
5349 | ensemblePtr = cmdPtr->objClientData; |
---|
5350 | oldList = ensemblePtr->subcmdList; |
---|
5351 | ensemblePtr->subcmdList = subcmdList; |
---|
5352 | if (subcmdList != NULL) { |
---|
5353 | Tcl_IncrRefCount(subcmdList); |
---|
5354 | } |
---|
5355 | if (oldList != NULL) { |
---|
5356 | TclDecrRefCount(oldList); |
---|
5357 | } |
---|
5358 | |
---|
5359 | /* |
---|
5360 | * Trigger an eventual recomputation of the ensemble command set. Note |
---|
5361 | * that this is slightly tricky, as it means that we are not actually |
---|
5362 | * counting the number of namespace export actions, but it is the simplest |
---|
5363 | * way to go! |
---|
5364 | */ |
---|
5365 | |
---|
5366 | ensemblePtr->nsPtr->exportLookupEpoch++; |
---|
5367 | |
---|
5368 | /* |
---|
5369 | * Special hack to make compiling of [info exists] work when the |
---|
5370 | * dictionary is modified. |
---|
5371 | */ |
---|
5372 | |
---|
5373 | if (cmdPtr->compileProc != NULL) { |
---|
5374 | ((Interp *)interp)->compileEpoch++; |
---|
5375 | } |
---|
5376 | |
---|
5377 | return TCL_OK; |
---|
5378 | } |
---|
5379 | |
---|
5380 | /* |
---|
5381 | *---------------------------------------------------------------------- |
---|
5382 | * |
---|
5383 | * Tcl_SetEnsembleMappingDict -- |
---|
5384 | * |
---|
5385 | * Set the mapping dictionary for a particular ensemble. |
---|
5386 | * |
---|
5387 | * Results: |
---|
5388 | * Tcl result code (error if command token does not indicate an ensemble |
---|
5389 | * or the mapping - if non-NULL - is not a dict). |
---|
5390 | * |
---|
5391 | * Side effects: |
---|
5392 | * The ensemble is updated and marked for recompilation. |
---|
5393 | * |
---|
5394 | *---------------------------------------------------------------------- |
---|
5395 | */ |
---|
5396 | |
---|
5397 | int |
---|
5398 | Tcl_SetEnsembleMappingDict( |
---|
5399 | Tcl_Interp *interp, |
---|
5400 | Tcl_Command token, |
---|
5401 | Tcl_Obj *mapDict) |
---|
5402 | { |
---|
5403 | Command *cmdPtr = (Command *) token; |
---|
5404 | EnsembleConfig *ensemblePtr; |
---|
5405 | Tcl_Obj *oldDict; |
---|
5406 | |
---|
5407 | if (cmdPtr->objProc != NsEnsembleImplementationCmd) { |
---|
5408 | Tcl_AppendResult(interp, "command is not an ensemble", NULL); |
---|
5409 | return TCL_ERROR; |
---|
5410 | } |
---|
5411 | if (mapDict != NULL) { |
---|
5412 | int size, done; |
---|
5413 | Tcl_DictSearch search; |
---|
5414 | Tcl_Obj *valuePtr; |
---|
5415 | |
---|
5416 | if (Tcl_DictObjSize(interp, mapDict, &size) != TCL_OK) { |
---|
5417 | return TCL_ERROR; |
---|
5418 | } |
---|
5419 | |
---|
5420 | for (Tcl_DictObjFirst(NULL, mapDict, &search, NULL, &valuePtr, &done); |
---|
5421 | !done; Tcl_DictObjNext(&search, NULL, &valuePtr, &done)) { |
---|
5422 | Tcl_Obj *cmdPtr; |
---|
5423 | const char *bytes; |
---|
5424 | |
---|
5425 | if (Tcl_ListObjIndex(interp, valuePtr, 0, &cmdPtr) != TCL_OK) { |
---|
5426 | Tcl_DictObjDone(&search); |
---|
5427 | return TCL_ERROR; |
---|
5428 | } |
---|
5429 | bytes = TclGetString(cmdPtr); |
---|
5430 | if (bytes[0] != ':' || bytes[1] != ':') { |
---|
5431 | Tcl_AppendResult(interp, |
---|
5432 | "ensemble target is not a fully-qualified command", |
---|
5433 | NULL); |
---|
5434 | Tcl_DictObjDone(&search); |
---|
5435 | return TCL_ERROR; |
---|
5436 | } |
---|
5437 | } |
---|
5438 | |
---|
5439 | if (size < 1) { |
---|
5440 | mapDict = NULL; |
---|
5441 | } |
---|
5442 | } |
---|
5443 | |
---|
5444 | ensemblePtr = cmdPtr->objClientData; |
---|
5445 | oldDict = ensemblePtr->subcommandDict; |
---|
5446 | ensemblePtr->subcommandDict = mapDict; |
---|
5447 | if (mapDict != NULL) { |
---|
5448 | Tcl_IncrRefCount(mapDict); |
---|
5449 | } |
---|
5450 | if (oldDict != NULL) { |
---|
5451 | TclDecrRefCount(oldDict); |
---|
5452 | } |
---|
5453 | |
---|
5454 | /* |
---|
5455 | * Trigger an eventual recomputation of the ensemble command set. Note |
---|
5456 | * that this is slightly tricky, as it means that we are not actually |
---|
5457 | * counting the number of namespace export actions, but it is the simplest |
---|
5458 | * way to go! |
---|
5459 | */ |
---|
5460 | |
---|
5461 | ensemblePtr->nsPtr->exportLookupEpoch++; |
---|
5462 | |
---|
5463 | /* |
---|
5464 | * Special hack to make compiling of [info exists] work when the |
---|
5465 | * dictionary is modified. |
---|
5466 | */ |
---|
5467 | |
---|
5468 | if (cmdPtr->compileProc != NULL) { |
---|
5469 | ((Interp *)interp)->compileEpoch++; |
---|
5470 | } |
---|
5471 | |
---|
5472 | return TCL_OK; |
---|
5473 | } |
---|
5474 | |
---|
5475 | /* |
---|
5476 | *---------------------------------------------------------------------- |
---|
5477 | * |
---|
5478 | * Tcl_SetEnsembleUnknownHandler -- |
---|
5479 | * |
---|
5480 | * Set the unknown handler for a particular ensemble. |
---|
5481 | * |
---|
5482 | * Results: |
---|
5483 | * Tcl result code (error if command token does not indicate an ensemble |
---|
5484 | * or the unknown handler - if non-NULL - is not a list). |
---|
5485 | * |
---|
5486 | * Side effects: |
---|
5487 | * The ensemble is updated and marked for recompilation. |
---|
5488 | * |
---|
5489 | *---------------------------------------------------------------------- |
---|
5490 | */ |
---|
5491 | |
---|
5492 | int |
---|
5493 | Tcl_SetEnsembleUnknownHandler( |
---|
5494 | Tcl_Interp *interp, |
---|
5495 | Tcl_Command token, |
---|
5496 | Tcl_Obj *unknownList) |
---|
5497 | { |
---|
5498 | Command *cmdPtr = (Command *) token; |
---|
5499 | EnsembleConfig *ensemblePtr; |
---|
5500 | Tcl_Obj *oldList; |
---|
5501 | |
---|
5502 | if (cmdPtr->objProc != NsEnsembleImplementationCmd) { |
---|
5503 | Tcl_AppendResult(interp, "command is not an ensemble", NULL); |
---|
5504 | return TCL_ERROR; |
---|
5505 | } |
---|
5506 | if (unknownList != NULL) { |
---|
5507 | int length; |
---|
5508 | |
---|
5509 | if (TclListObjLength(interp, unknownList, &length) != TCL_OK) { |
---|
5510 | return TCL_ERROR; |
---|
5511 | } |
---|
5512 | if (length < 1) { |
---|
5513 | unknownList = NULL; |
---|
5514 | } |
---|
5515 | } |
---|
5516 | |
---|
5517 | ensemblePtr = cmdPtr->objClientData; |
---|
5518 | oldList = ensemblePtr->unknownHandler; |
---|
5519 | ensemblePtr->unknownHandler = unknownList; |
---|
5520 | if (unknownList != NULL) { |
---|
5521 | Tcl_IncrRefCount(unknownList); |
---|
5522 | } |
---|
5523 | if (oldList != NULL) { |
---|
5524 | TclDecrRefCount(oldList); |
---|
5525 | } |
---|
5526 | |
---|
5527 | /* |
---|
5528 | * Trigger an eventual recomputation of the ensemble command set. Note |
---|
5529 | * that this is slightly tricky, as it means that we are not actually |
---|
5530 | * counting the number of namespace export actions, but it is the simplest |
---|
5531 | * way to go! |
---|
5532 | */ |
---|
5533 | |
---|
5534 | ensemblePtr->nsPtr->exportLookupEpoch++; |
---|
5535 | |
---|
5536 | return TCL_OK; |
---|
5537 | } |
---|
5538 | |
---|
5539 | /* |
---|
5540 | *---------------------------------------------------------------------- |
---|
5541 | * |
---|
5542 | * Tcl_SetEnsembleFlags -- |
---|
5543 | * |
---|
5544 | * Set the flags for a particular ensemble. |
---|
5545 | * |
---|
5546 | * Results: |
---|
5547 | * Tcl result code (error if command token does not indicate an |
---|
5548 | * ensemble). |
---|
5549 | * |
---|
5550 | * Side effects: |
---|
5551 | * The ensemble is updated and marked for recompilation. |
---|
5552 | * |
---|
5553 | *---------------------------------------------------------------------- |
---|
5554 | */ |
---|
5555 | |
---|
5556 | int |
---|
5557 | Tcl_SetEnsembleFlags( |
---|
5558 | Tcl_Interp *interp, |
---|
5559 | Tcl_Command token, |
---|
5560 | int flags) |
---|
5561 | { |
---|
5562 | Command *cmdPtr = (Command *) token; |
---|
5563 | EnsembleConfig *ensemblePtr; |
---|
5564 | int wasCompiled; |
---|
5565 | |
---|
5566 | if (cmdPtr->objProc != NsEnsembleImplementationCmd) { |
---|
5567 | Tcl_AppendResult(interp, "command is not an ensemble", NULL); |
---|
5568 | return TCL_ERROR; |
---|
5569 | } |
---|
5570 | |
---|
5571 | ensemblePtr = cmdPtr->objClientData; |
---|
5572 | wasCompiled = ensemblePtr->flags & ENSEMBLE_COMPILE; |
---|
5573 | |
---|
5574 | /* |
---|
5575 | * This API refuses to set the ENS_DEAD flag... |
---|
5576 | */ |
---|
5577 | |
---|
5578 | ensemblePtr->flags &= ENS_DEAD; |
---|
5579 | ensemblePtr->flags |= flags & ~ENS_DEAD; |
---|
5580 | |
---|
5581 | /* |
---|
5582 | * Trigger an eventual recomputation of the ensemble command set. Note |
---|
5583 | * that this is slightly tricky, as it means that we are not actually |
---|
5584 | * counting the number of namespace export actions, but it is the simplest |
---|
5585 | * way to go! |
---|
5586 | */ |
---|
5587 | |
---|
5588 | ensemblePtr->nsPtr->exportLookupEpoch++; |
---|
5589 | |
---|
5590 | /* |
---|
5591 | * If the ENSEMBLE_COMPILE flag status was changed, install or remove the |
---|
5592 | * compiler function and bump the interpreter's compilation epoch so that |
---|
5593 | * bytecode gets regenerated. |
---|
5594 | */ |
---|
5595 | |
---|
5596 | if (flags & ENSEMBLE_COMPILE) { |
---|
5597 | if (!wasCompiled) { |
---|
5598 | ((Command*) ensemblePtr->token)->compileProc = TclCompileEnsemble; |
---|
5599 | ((Interp *) interp)->compileEpoch++; |
---|
5600 | } |
---|
5601 | } else { |
---|
5602 | if (wasCompiled) { |
---|
5603 | ((Command*) ensemblePtr->token)->compileProc = NULL; |
---|
5604 | ((Interp *) interp)->compileEpoch++; |
---|
5605 | } |
---|
5606 | } |
---|
5607 | |
---|
5608 | return TCL_OK; |
---|
5609 | } |
---|
5610 | |
---|
5611 | /* |
---|
5612 | *---------------------------------------------------------------------- |
---|
5613 | * |
---|
5614 | * Tcl_GetEnsembleSubcommandList -- |
---|
5615 | * |
---|
5616 | * Get the list of subcommands associated with a particular ensemble. |
---|
5617 | * |
---|
5618 | * Results: |
---|
5619 | * Tcl result code (error if command token does not indicate an |
---|
5620 | * ensemble). The list of subcommands is returned by updating the |
---|
5621 | * variable pointed to by the last parameter (NULL if this is to be |
---|
5622 | * derived from the mapping dictionary or the associated namespace's |
---|
5623 | * exported commands). |
---|
5624 | * |
---|
5625 | * Side effects: |
---|
5626 | * None |
---|
5627 | * |
---|
5628 | *---------------------------------------------------------------------- |
---|
5629 | */ |
---|
5630 | |
---|
5631 | int |
---|
5632 | Tcl_GetEnsembleSubcommandList( |
---|
5633 | Tcl_Interp *interp, |
---|
5634 | Tcl_Command token, |
---|
5635 | Tcl_Obj **subcmdListPtr) |
---|
5636 | { |
---|
5637 | Command *cmdPtr = (Command *) token; |
---|
5638 | EnsembleConfig *ensemblePtr; |
---|
5639 | |
---|
5640 | if (cmdPtr->objProc != NsEnsembleImplementationCmd) { |
---|
5641 | if (interp != NULL) { |
---|
5642 | Tcl_AppendResult(interp, "command is not an ensemble", NULL); |
---|
5643 | } |
---|
5644 | return TCL_ERROR; |
---|
5645 | } |
---|
5646 | |
---|
5647 | ensemblePtr = cmdPtr->objClientData; |
---|
5648 | *subcmdListPtr = ensemblePtr->subcmdList; |
---|
5649 | return TCL_OK; |
---|
5650 | } |
---|
5651 | |
---|
5652 | /* |
---|
5653 | *---------------------------------------------------------------------- |
---|
5654 | * |
---|
5655 | * Tcl_GetEnsembleMappingDict -- |
---|
5656 | * |
---|
5657 | * Get the command mapping dictionary associated with a particular |
---|
5658 | * ensemble. |
---|
5659 | * |
---|
5660 | * Results: |
---|
5661 | * Tcl result code (error if command token does not indicate an |
---|
5662 | * ensemble). The mapping dict is returned by updating the variable |
---|
5663 | * pointed to by the last parameter (NULL if none is installed). |
---|
5664 | * |
---|
5665 | * Side effects: |
---|
5666 | * None |
---|
5667 | * |
---|
5668 | *---------------------------------------------------------------------- |
---|
5669 | */ |
---|
5670 | |
---|
5671 | int |
---|
5672 | Tcl_GetEnsembleMappingDict( |
---|
5673 | Tcl_Interp *interp, |
---|
5674 | Tcl_Command token, |
---|
5675 | Tcl_Obj **mapDictPtr) |
---|
5676 | { |
---|
5677 | Command *cmdPtr = (Command *) token; |
---|
5678 | EnsembleConfig *ensemblePtr; |
---|
5679 | |
---|
5680 | if (cmdPtr->objProc != NsEnsembleImplementationCmd) { |
---|
5681 | if (interp != NULL) { |
---|
5682 | Tcl_AppendResult(interp, "command is not an ensemble", NULL); |
---|
5683 | } |
---|
5684 | return TCL_ERROR; |
---|
5685 | } |
---|
5686 | |
---|
5687 | ensemblePtr = cmdPtr->objClientData; |
---|
5688 | *mapDictPtr = ensemblePtr->subcommandDict; |
---|
5689 | return TCL_OK; |
---|
5690 | } |
---|
5691 | |
---|
5692 | /* |
---|
5693 | *---------------------------------------------------------------------- |
---|
5694 | * |
---|
5695 | * Tcl_GetEnsembleUnknownHandler -- |
---|
5696 | * |
---|
5697 | * Get the unknown handler associated with a particular ensemble. |
---|
5698 | * |
---|
5699 | * Results: |
---|
5700 | * Tcl result code (error if command token does not indicate an |
---|
5701 | * ensemble). The unknown handler is returned by updating the variable |
---|
5702 | * pointed to by the last parameter (NULL if no handler is installed). |
---|
5703 | * |
---|
5704 | * Side effects: |
---|
5705 | * None |
---|
5706 | * |
---|
5707 | *---------------------------------------------------------------------- |
---|
5708 | */ |
---|
5709 | |
---|
5710 | int |
---|
5711 | Tcl_GetEnsembleUnknownHandler( |
---|
5712 | Tcl_Interp *interp, |
---|
5713 | Tcl_Command token, |
---|
5714 | Tcl_Obj **unknownListPtr) |
---|
5715 | { |
---|
5716 | Command *cmdPtr = (Command *) token; |
---|
5717 | EnsembleConfig *ensemblePtr; |
---|
5718 | |
---|
5719 | if (cmdPtr->objProc != NsEnsembleImplementationCmd) { |
---|
5720 | if (interp != NULL) { |
---|
5721 | Tcl_AppendResult(interp, "command is not an ensemble", NULL); |
---|
5722 | } |
---|
5723 | return TCL_ERROR; |
---|
5724 | } |
---|
5725 | |
---|
5726 | ensemblePtr = cmdPtr->objClientData; |
---|
5727 | *unknownListPtr = ensemblePtr->unknownHandler; |
---|
5728 | return TCL_OK; |
---|
5729 | } |
---|
5730 | |
---|
5731 | /* |
---|
5732 | *---------------------------------------------------------------------- |
---|
5733 | * |
---|
5734 | * Tcl_GetEnsembleFlags -- |
---|
5735 | * |
---|
5736 | * Get the flags for a particular ensemble. |
---|
5737 | * |
---|
5738 | * Results: |
---|
5739 | * Tcl result code (error if command token does not indicate an |
---|
5740 | * ensemble). The flags are returned by updating the variable pointed to |
---|
5741 | * by the last parameter. |
---|
5742 | * |
---|
5743 | * Side effects: |
---|
5744 | * None |
---|
5745 | * |
---|
5746 | *---------------------------------------------------------------------- |
---|
5747 | */ |
---|
5748 | |
---|
5749 | int |
---|
5750 | Tcl_GetEnsembleFlags( |
---|
5751 | Tcl_Interp *interp, |
---|
5752 | Tcl_Command token, |
---|
5753 | int *flagsPtr) |
---|
5754 | { |
---|
5755 | Command *cmdPtr = (Command *) token; |
---|
5756 | EnsembleConfig *ensemblePtr; |
---|
5757 | |
---|
5758 | if (cmdPtr->objProc != NsEnsembleImplementationCmd) { |
---|
5759 | if (interp != NULL) { |
---|
5760 | Tcl_AppendResult(interp, "command is not an ensemble", NULL); |
---|
5761 | } |
---|
5762 | return TCL_ERROR; |
---|
5763 | } |
---|
5764 | |
---|
5765 | ensemblePtr = cmdPtr->objClientData; |
---|
5766 | *flagsPtr = ensemblePtr->flags; |
---|
5767 | return TCL_OK; |
---|
5768 | } |
---|
5769 | |
---|
5770 | /* |
---|
5771 | *---------------------------------------------------------------------- |
---|
5772 | * |
---|
5773 | * Tcl_GetEnsembleNamespace -- |
---|
5774 | * |
---|
5775 | * Get the namespace associated with a particular ensemble. |
---|
5776 | * |
---|
5777 | * Results: |
---|
5778 | * Tcl result code (error if command token does not indicate an |
---|
5779 | * ensemble). Namespace is returned by updating the variable pointed to |
---|
5780 | * by the last parameter. |
---|
5781 | * |
---|
5782 | * Side effects: |
---|
5783 | * None |
---|
5784 | * |
---|
5785 | *---------------------------------------------------------------------- |
---|
5786 | */ |
---|
5787 | |
---|
5788 | int |
---|
5789 | Tcl_GetEnsembleNamespace( |
---|
5790 | Tcl_Interp *interp, |
---|
5791 | Tcl_Command token, |
---|
5792 | Tcl_Namespace **namespacePtrPtr) |
---|
5793 | { |
---|
5794 | Command *cmdPtr = (Command *) token; |
---|
5795 | EnsembleConfig *ensemblePtr; |
---|
5796 | |
---|
5797 | if (cmdPtr->objProc != NsEnsembleImplementationCmd) { |
---|
5798 | if (interp != NULL) { |
---|
5799 | Tcl_AppendResult(interp, "command is not an ensemble", NULL); |
---|
5800 | } |
---|
5801 | return TCL_ERROR; |
---|
5802 | } |
---|
5803 | |
---|
5804 | ensemblePtr = cmdPtr->objClientData; |
---|
5805 | *namespacePtrPtr = (Tcl_Namespace *) ensemblePtr->nsPtr; |
---|
5806 | return TCL_OK; |
---|
5807 | } |
---|
5808 | |
---|
5809 | /* |
---|
5810 | *---------------------------------------------------------------------- |
---|
5811 | * |
---|
5812 | * Tcl_FindEnsemble -- |
---|
5813 | * |
---|
5814 | * Given a command name, get the ensemble token for it, allowing for |
---|
5815 | * [namespace import]s. [Bug 1017022] |
---|
5816 | * |
---|
5817 | * Results: |
---|
5818 | * The token for the ensemble command with the given name, or NULL if the |
---|
5819 | * command either does not exist or is not an ensemble (when an error |
---|
5820 | * message will be written into the interp if thats non-NULL). |
---|
5821 | * |
---|
5822 | * Side effects: |
---|
5823 | * None |
---|
5824 | * |
---|
5825 | *---------------------------------------------------------------------- |
---|
5826 | */ |
---|
5827 | |
---|
5828 | Tcl_Command |
---|
5829 | Tcl_FindEnsemble( |
---|
5830 | Tcl_Interp *interp, /* Where to do the lookup, and where to write |
---|
5831 | * the errors if TCL_LEAVE_ERR_MSG is set in |
---|
5832 | * the flags. */ |
---|
5833 | Tcl_Obj *cmdNameObj, /* Name of command to look up. */ |
---|
5834 | int flags) /* Either 0 or TCL_LEAVE_ERR_MSG; other flags |
---|
5835 | * are probably not useful. */ |
---|
5836 | { |
---|
5837 | Command *cmdPtr; |
---|
5838 | |
---|
5839 | cmdPtr = (Command *) |
---|
5840 | Tcl_FindCommand(interp, TclGetString(cmdNameObj), NULL, flags); |
---|
5841 | if (cmdPtr == NULL) { |
---|
5842 | return NULL; |
---|
5843 | } |
---|
5844 | |
---|
5845 | if (cmdPtr->objProc != NsEnsembleImplementationCmd) { |
---|
5846 | /* |
---|
5847 | * Reuse existing infrastructure for following import link chains |
---|
5848 | * rather than duplicating it. |
---|
5849 | */ |
---|
5850 | |
---|
5851 | cmdPtr = (Command *) TclGetOriginalCommand((Tcl_Command) cmdPtr); |
---|
5852 | |
---|
5853 | if (cmdPtr == NULL || cmdPtr->objProc != NsEnsembleImplementationCmd) { |
---|
5854 | if (flags & TCL_LEAVE_ERR_MSG) { |
---|
5855 | Tcl_AppendResult(interp, "\"", TclGetString(cmdNameObj), |
---|
5856 | "\" is not an ensemble command", NULL); |
---|
5857 | Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ENSEMBLE", |
---|
5858 | TclGetString(cmdNameObj), NULL); |
---|
5859 | } |
---|
5860 | return NULL; |
---|
5861 | } |
---|
5862 | } |
---|
5863 | |
---|
5864 | return (Tcl_Command) cmdPtr; |
---|
5865 | } |
---|
5866 | |
---|
5867 | /* |
---|
5868 | *---------------------------------------------------------------------- |
---|
5869 | * |
---|
5870 | * Tcl_IsEnsemble -- |
---|
5871 | * |
---|
5872 | * Simple test for ensemble-hood that takes into account imported |
---|
5873 | * ensemble commands as well. |
---|
5874 | * |
---|
5875 | * Results: |
---|
5876 | * Boolean value |
---|
5877 | * |
---|
5878 | * Side effects: |
---|
5879 | * None |
---|
5880 | * |
---|
5881 | *---------------------------------------------------------------------- |
---|
5882 | */ |
---|
5883 | |
---|
5884 | int |
---|
5885 | Tcl_IsEnsemble( |
---|
5886 | Tcl_Command token) |
---|
5887 | { |
---|
5888 | Command *cmdPtr = (Command *) token; |
---|
5889 | if (cmdPtr->objProc == NsEnsembleImplementationCmd) { |
---|
5890 | return 1; |
---|
5891 | } |
---|
5892 | cmdPtr = (Command *) TclGetOriginalCommand((Tcl_Command) cmdPtr); |
---|
5893 | if (cmdPtr == NULL || cmdPtr->objProc != NsEnsembleImplementationCmd) { |
---|
5894 | return 0; |
---|
5895 | } |
---|
5896 | return 1; |
---|
5897 | } |
---|
5898 | |
---|
5899 | /* |
---|
5900 | *---------------------------------------------------------------------- |
---|
5901 | * |
---|
5902 | * TclMakeEnsemble -- |
---|
5903 | * |
---|
5904 | * Create an ensemble from a table of implementation commands. The |
---|
5905 | * ensemble will be subject to (limited) compilation if any of the |
---|
5906 | * implementation commands are compilable. |
---|
5907 | * |
---|
5908 | * Results: |
---|
5909 | * Handle for the ensemble, or NULL if creation of it fails. |
---|
5910 | * |
---|
5911 | * Side effects: |
---|
5912 | * May advance bytecode compilation epoch. |
---|
5913 | * |
---|
5914 | *---------------------------------------------------------------------- |
---|
5915 | */ |
---|
5916 | |
---|
5917 | Tcl_Command |
---|
5918 | TclMakeEnsemble( |
---|
5919 | Tcl_Interp *interp, |
---|
5920 | const char *name, |
---|
5921 | const EnsembleImplMap map[]) |
---|
5922 | { |
---|
5923 | Tcl_Command ensemble; /* The overall ensemble. */ |
---|
5924 | Tcl_Namespace *tclNsPtr; /* Reference to the "::tcl" namespace. */ |
---|
5925 | Tcl_DString buf; |
---|
5926 | |
---|
5927 | tclNsPtr = Tcl_FindNamespace(interp, "::tcl", NULL, |
---|
5928 | TCL_CREATE_NS_IF_UNKNOWN); |
---|
5929 | if (tclNsPtr == NULL) { |
---|
5930 | Tcl_Panic("unable to find or create ::tcl namespace!"); |
---|
5931 | } |
---|
5932 | Tcl_DStringInit(&buf); |
---|
5933 | Tcl_DStringAppend(&buf, "::tcl::", -1); |
---|
5934 | Tcl_DStringAppend(&buf, name, -1); |
---|
5935 | tclNsPtr = Tcl_FindNamespace(interp, Tcl_DStringValue(&buf), NULL, |
---|
5936 | TCL_CREATE_NS_IF_UNKNOWN); |
---|
5937 | if (tclNsPtr == NULL) { |
---|
5938 | Tcl_Panic("unable to find or create %s namespace!", |
---|
5939 | Tcl_DStringValue(&buf)); |
---|
5940 | } |
---|
5941 | ensemble = Tcl_CreateEnsemble(interp, Tcl_DStringValue(&buf)+5, tclNsPtr, |
---|
5942 | TCL_ENSEMBLE_PREFIX); |
---|
5943 | Tcl_DStringAppend(&buf, "::", -1); |
---|
5944 | if (ensemble != NULL) { |
---|
5945 | Tcl_Obj *mapDict; |
---|
5946 | int i, compile = 0; |
---|
5947 | |
---|
5948 | TclNewObj(mapDict); |
---|
5949 | for (i=0 ; map[i].name != NULL ; i++) { |
---|
5950 | Tcl_Obj *fromObj, *toObj; |
---|
5951 | Command *cmdPtr; |
---|
5952 | |
---|
5953 | fromObj = Tcl_NewStringObj(map[i].name, -1); |
---|
5954 | TclNewStringObj(toObj, Tcl_DStringValue(&buf), |
---|
5955 | Tcl_DStringLength(&buf)); |
---|
5956 | Tcl_AppendToObj(toObj, map[i].name, -1); |
---|
5957 | Tcl_DictObjPut(NULL, mapDict, fromObj, toObj); |
---|
5958 | cmdPtr = (Command *) Tcl_CreateObjCommand(interp, |
---|
5959 | TclGetString(toObj), map[i].proc, NULL, NULL); |
---|
5960 | cmdPtr->compileProc = map[i].compileProc; |
---|
5961 | compile |= (map[i].compileProc != NULL); |
---|
5962 | } |
---|
5963 | Tcl_SetEnsembleMappingDict(interp, ensemble, mapDict); |
---|
5964 | if (compile) { |
---|
5965 | Tcl_SetEnsembleFlags(interp, ensemble, |
---|
5966 | TCL_ENSEMBLE_PREFIX | ENSEMBLE_COMPILE); |
---|
5967 | } |
---|
5968 | } |
---|
5969 | Tcl_DStringFree(&buf); |
---|
5970 | |
---|
5971 | return ensemble; |
---|
5972 | } |
---|
5973 | |
---|
5974 | /* |
---|
5975 | *---------------------------------------------------------------------- |
---|
5976 | * |
---|
5977 | * NsEnsembleImplementationCmd -- |
---|
5978 | * |
---|
5979 | * Implements an ensemble of commands (being those exported by a |
---|
5980 | * namespace other than the global namespace) as a command with the same |
---|
5981 | * (short) name as the namespace in the parent namespace. |
---|
5982 | * |
---|
5983 | * Results: |
---|
5984 | * A standard Tcl result code. Will be TCL_ERROR if the command is not an |
---|
5985 | * unambiguous prefix of any command exported by the ensemble's |
---|
5986 | * namespace. |
---|
5987 | * |
---|
5988 | * Side effects: |
---|
5989 | * Depends on the command within the namespace that gets executed. If the |
---|
5990 | * ensemble itself returns TCL_ERROR, a descriptive error message will be |
---|
5991 | * placed in the interpreter's result. |
---|
5992 | * |
---|
5993 | *---------------------------------------------------------------------- |
---|
5994 | */ |
---|
5995 | |
---|
5996 | static int |
---|
5997 | NsEnsembleImplementationCmd( |
---|
5998 | ClientData clientData, |
---|
5999 | Tcl_Interp *interp, |
---|
6000 | int objc, |
---|
6001 | Tcl_Obj *const objv[]) |
---|
6002 | { |
---|
6003 | EnsembleConfig *ensemblePtr = clientData; |
---|
6004 | /* The ensemble itself. */ |
---|
6005 | Tcl_Obj **tempObjv; /* Space used to construct the list of |
---|
6006 | * arguments to pass to the command that |
---|
6007 | * implements the ensemble subcommand. */ |
---|
6008 | int result; /* The result of the subcommand execution. */ |
---|
6009 | Tcl_Obj *prefixObj; /* An object containing the prefix words of |
---|
6010 | * the command that implements the |
---|
6011 | * subcommand. */ |
---|
6012 | Tcl_HashEntry *hPtr; /* Used for efficient lookup of fully |
---|
6013 | * specified but not yet cached command |
---|
6014 | * names. */ |
---|
6015 | Tcl_Obj **prefixObjv; /* The list of objects to substitute in as the |
---|
6016 | * target command prefix. */ |
---|
6017 | int prefixObjc; /* Size of prefixObjv of course! */ |
---|
6018 | int reparseCount = 0; /* Number of reparses. */ |
---|
6019 | |
---|
6020 | if (objc < 2) { |
---|
6021 | Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?argument ...?"); |
---|
6022 | return TCL_ERROR; |
---|
6023 | } |
---|
6024 | |
---|
6025 | restartEnsembleParse: |
---|
6026 | if (ensemblePtr->nsPtr->flags & NS_DYING) { |
---|
6027 | /* |
---|
6028 | * Don't know how we got here, but make things give up quickly. |
---|
6029 | */ |
---|
6030 | |
---|
6031 | if (!Tcl_InterpDeleted(interp)) { |
---|
6032 | Tcl_AppendResult(interp, |
---|
6033 | "ensemble activated for deleted namespace", NULL); |
---|
6034 | } |
---|
6035 | return TCL_ERROR; |
---|
6036 | } |
---|
6037 | |
---|
6038 | /* |
---|
6039 | * Determine if the table of subcommands is right. If so, we can just look |
---|
6040 | * up in there and go straight to dispatch. |
---|
6041 | */ |
---|
6042 | |
---|
6043 | if (ensemblePtr->epoch == ensemblePtr->nsPtr->exportLookupEpoch) { |
---|
6044 | /* |
---|
6045 | * Table of subcommands is still valid; therefore there might be a |
---|
6046 | * valid cache of discovered information which we can reuse. Do the |
---|
6047 | * check here, and if we're still valid, we can jump straight to the |
---|
6048 | * part where we do the invocation of the subcommand. |
---|
6049 | */ |
---|
6050 | |
---|
6051 | if (objv[1]->typePtr == &tclEnsembleCmdType) { |
---|
6052 | EnsembleCmdRep *ensembleCmd = objv[1]->internalRep.otherValuePtr; |
---|
6053 | |
---|
6054 | if (ensembleCmd->nsPtr == ensemblePtr->nsPtr && |
---|
6055 | ensembleCmd->epoch == ensemblePtr->epoch && |
---|
6056 | ensembleCmd->token == ensemblePtr->token) { |
---|
6057 | prefixObj = ensembleCmd->realPrefixObj; |
---|
6058 | Tcl_IncrRefCount(prefixObj); |
---|
6059 | goto runResultingSubcommand; |
---|
6060 | } |
---|
6061 | } |
---|
6062 | } else { |
---|
6063 | BuildEnsembleConfig(ensemblePtr); |
---|
6064 | ensemblePtr->epoch = ensemblePtr->nsPtr->exportLookupEpoch; |
---|
6065 | } |
---|
6066 | |
---|
6067 | /* |
---|
6068 | * Look in the hashtable for the subcommand name; this is the fastest way |
---|
6069 | * of all. |
---|
6070 | */ |
---|
6071 | |
---|
6072 | hPtr = Tcl_FindHashEntry(&ensemblePtr->subcommandTable, |
---|
6073 | TclGetString(objv[1])); |
---|
6074 | if (hPtr != NULL) { |
---|
6075 | char *fullName = Tcl_GetHashKey(&ensemblePtr->subcommandTable, hPtr); |
---|
6076 | |
---|
6077 | prefixObj = Tcl_GetHashValue(hPtr); |
---|
6078 | |
---|
6079 | /* |
---|
6080 | * Cache for later in the subcommand object. |
---|
6081 | */ |
---|
6082 | |
---|
6083 | MakeCachedEnsembleCommand(objv[1], ensemblePtr, fullName, prefixObj); |
---|
6084 | } else if (!(ensemblePtr->flags & TCL_ENSEMBLE_PREFIX)) { |
---|
6085 | /* |
---|
6086 | * Could not map, no prefixing, go to unknown/error handling. |
---|
6087 | */ |
---|
6088 | |
---|
6089 | goto unknownOrAmbiguousSubcommand; |
---|
6090 | } else { |
---|
6091 | /* |
---|
6092 | * If we've not already confirmed the command with the hash as part of |
---|
6093 | * building our export table, we need to scan the sorted array for |
---|
6094 | * matches. |
---|
6095 | */ |
---|
6096 | |
---|
6097 | char *subcmdName; /* Name of the subcommand, or unique prefix of |
---|
6098 | * it (will be an error for a non-unique |
---|
6099 | * prefix). */ |
---|
6100 | char *fullName = NULL; /* Full name of the subcommand. */ |
---|
6101 | int stringLength, i; |
---|
6102 | int tableLength = ensemblePtr->subcommandTable.numEntries; |
---|
6103 | |
---|
6104 | subcmdName = TclGetString(objv[1]); |
---|
6105 | stringLength = objv[1]->length; |
---|
6106 | for (i=0 ; i<tableLength ; i++) { |
---|
6107 | register int cmp = strncmp(subcmdName, |
---|
6108 | ensemblePtr->subcommandArrayPtr[i], |
---|
6109 | (unsigned) stringLength); |
---|
6110 | |
---|
6111 | if (cmp == 0) { |
---|
6112 | if (fullName != NULL) { |
---|
6113 | /* |
---|
6114 | * Since there's never the exact-match case to worry about |
---|
6115 | * (hash search filters this), getting here indicates that |
---|
6116 | * our subcommand is an ambiguous prefix of (at least) two |
---|
6117 | * exported subcommands, which is an error case. |
---|
6118 | */ |
---|
6119 | |
---|
6120 | goto unknownOrAmbiguousSubcommand; |
---|
6121 | } |
---|
6122 | fullName = ensemblePtr->subcommandArrayPtr[i]; |
---|
6123 | } else if (cmp < 0) { |
---|
6124 | /* |
---|
6125 | * Because we are searching a sorted table, we can now stop |
---|
6126 | * searching because we have gone past anything that could |
---|
6127 | * possibly match. |
---|
6128 | */ |
---|
6129 | |
---|
6130 | break; |
---|
6131 | } |
---|
6132 | } |
---|
6133 | if (fullName == NULL) { |
---|
6134 | /* |
---|
6135 | * The subcommand is not a prefix of anything, so bail out! |
---|
6136 | */ |
---|
6137 | |
---|
6138 | goto unknownOrAmbiguousSubcommand; |
---|
6139 | } |
---|
6140 | hPtr = Tcl_FindHashEntry(&ensemblePtr->subcommandTable, fullName); |
---|
6141 | if (hPtr == NULL) { |
---|
6142 | Tcl_Panic("full name %s not found in supposedly synchronized hash", |
---|
6143 | fullName); |
---|
6144 | } |
---|
6145 | prefixObj = Tcl_GetHashValue(hPtr); |
---|
6146 | |
---|
6147 | /* |
---|
6148 | * Cache for later in the subcommand object. |
---|
6149 | */ |
---|
6150 | |
---|
6151 | MakeCachedEnsembleCommand(objv[1], ensemblePtr, fullName, prefixObj); |
---|
6152 | } |
---|
6153 | |
---|
6154 | Tcl_IncrRefCount(prefixObj); |
---|
6155 | runResultingSubcommand: |
---|
6156 | |
---|
6157 | /* |
---|
6158 | * Do the real work of execution of the subcommand by building an array of |
---|
6159 | * objects (note that this is potentially not the same length as the |
---|
6160 | * number of arguments to this ensemble command), populating it and then |
---|
6161 | * feeding it back through the main command-lookup engine. In theory, we |
---|
6162 | * could look up the command in the namespace ourselves, as we already |
---|
6163 | * have the namespace in which it is guaranteed to exist, but we don't do |
---|
6164 | * that (the cacheing of the command object used should help with that.) |
---|
6165 | */ |
---|
6166 | |
---|
6167 | { |
---|
6168 | Interp *iPtr = (Interp *) interp; |
---|
6169 | int isRootEnsemble; |
---|
6170 | Tcl_Obj *copyObj; |
---|
6171 | |
---|
6172 | /* |
---|
6173 | * Get the prefix that we're rewriting to. To do this we need to |
---|
6174 | * ensure that the internal representation of the list does not change |
---|
6175 | * so that we can safely keep the internal representations of the |
---|
6176 | * elements in the list. |
---|
6177 | */ |
---|
6178 | |
---|
6179 | copyObj = TclListObjCopy(NULL, prefixObj); |
---|
6180 | TclListObjGetElements(NULL, copyObj, &prefixObjc, &prefixObjv); |
---|
6181 | |
---|
6182 | /* |
---|
6183 | * Record what arguments the script sent in so that things like |
---|
6184 | * Tcl_WrongNumArgs can give the correct error message. |
---|
6185 | */ |
---|
6186 | |
---|
6187 | isRootEnsemble = (iPtr->ensembleRewrite.sourceObjs == NULL); |
---|
6188 | if (isRootEnsemble) { |
---|
6189 | iPtr->ensembleRewrite.sourceObjs = objv; |
---|
6190 | iPtr->ensembleRewrite.numRemovedObjs = 2; |
---|
6191 | iPtr->ensembleRewrite.numInsertedObjs = prefixObjc; |
---|
6192 | } else { |
---|
6193 | int ni = iPtr->ensembleRewrite.numInsertedObjs; |
---|
6194 | |
---|
6195 | if (ni < 2) { |
---|
6196 | iPtr->ensembleRewrite.numRemovedObjs += 2 - ni; |
---|
6197 | iPtr->ensembleRewrite.numInsertedObjs += prefixObjc-1; |
---|
6198 | } else { |
---|
6199 | iPtr->ensembleRewrite.numInsertedObjs += prefixObjc-2; |
---|
6200 | } |
---|
6201 | } |
---|
6202 | |
---|
6203 | /* |
---|
6204 | * Allocate a workspace and build the list of arguments to pass to the |
---|
6205 | * target command in it. |
---|
6206 | */ |
---|
6207 | |
---|
6208 | tempObjv = (Tcl_Obj **) TclStackAlloc(interp, |
---|
6209 | (int) sizeof(Tcl_Obj *) * (objc - 2 + prefixObjc)); |
---|
6210 | memcpy(tempObjv, prefixObjv, sizeof(Tcl_Obj *) * prefixObjc); |
---|
6211 | memcpy(tempObjv+prefixObjc, objv+2, sizeof(Tcl_Obj *) * (objc-2)); |
---|
6212 | |
---|
6213 | /* |
---|
6214 | * Hand off to the target command. |
---|
6215 | */ |
---|
6216 | |
---|
6217 | result = Tcl_EvalObjv(interp, objc-2+prefixObjc, tempObjv, |
---|
6218 | TCL_EVAL_INVOKE); |
---|
6219 | |
---|
6220 | /* |
---|
6221 | * Clean up. |
---|
6222 | */ |
---|
6223 | |
---|
6224 | TclStackFree(interp, tempObjv); |
---|
6225 | Tcl_DecrRefCount(copyObj); |
---|
6226 | if (isRootEnsemble) { |
---|
6227 | iPtr->ensembleRewrite.sourceObjs = NULL; |
---|
6228 | iPtr->ensembleRewrite.numRemovedObjs = 0; |
---|
6229 | iPtr->ensembleRewrite.numInsertedObjs = 0; |
---|
6230 | } |
---|
6231 | } |
---|
6232 | Tcl_DecrRefCount(prefixObj); |
---|
6233 | return result; |
---|
6234 | |
---|
6235 | unknownOrAmbiguousSubcommand: |
---|
6236 | /* |
---|
6237 | * Have not been able to match the subcommand asked for with a real |
---|
6238 | * subcommand that we export. See whether a handler has been registered |
---|
6239 | * for dealing with this situation. Will only call (at most) once for any |
---|
6240 | * particular ensemble invocation. |
---|
6241 | */ |
---|
6242 | |
---|
6243 | if (ensemblePtr->unknownHandler != NULL && reparseCount++ < 1) { |
---|
6244 | int paramc, i; |
---|
6245 | Tcl_Obj **paramv, *unknownCmd, *ensObj; |
---|
6246 | |
---|
6247 | unknownCmd = Tcl_DuplicateObj(ensemblePtr->unknownHandler); |
---|
6248 | TclNewObj(ensObj); |
---|
6249 | Tcl_GetCommandFullName(interp, ensemblePtr->token, ensObj); |
---|
6250 | Tcl_ListObjAppendElement(NULL, unknownCmd, ensObj); |
---|
6251 | for (i=1 ; i<objc ; i++) { |
---|
6252 | Tcl_ListObjAppendElement(NULL, unknownCmd, objv[i]); |
---|
6253 | } |
---|
6254 | TclListObjGetElements(NULL, unknownCmd, ¶mc, ¶mv); |
---|
6255 | Tcl_Preserve(ensemblePtr); |
---|
6256 | Tcl_IncrRefCount(unknownCmd); |
---|
6257 | result = Tcl_EvalObjv(interp, paramc, paramv, 0); |
---|
6258 | if (result == TCL_OK) { |
---|
6259 | prefixObj = Tcl_GetObjResult(interp); |
---|
6260 | Tcl_IncrRefCount(prefixObj); |
---|
6261 | Tcl_DecrRefCount(unknownCmd); |
---|
6262 | Tcl_Release(ensemblePtr); |
---|
6263 | Tcl_ResetResult(interp); |
---|
6264 | if (ensemblePtr->flags & ENS_DEAD) { |
---|
6265 | Tcl_DecrRefCount(prefixObj); |
---|
6266 | Tcl_SetResult(interp, |
---|
6267 | "unknown subcommand handler deleted its ensemble", |
---|
6268 | TCL_STATIC); |
---|
6269 | return TCL_ERROR; |
---|
6270 | } |
---|
6271 | |
---|
6272 | /* |
---|
6273 | * Namespace is still there. Check if the result is a valid list. |
---|
6274 | * If it is, and it is non-empty, that list is what we are using |
---|
6275 | * as our replacement. |
---|
6276 | */ |
---|
6277 | |
---|
6278 | if (TclListObjLength(interp, prefixObj, &prefixObjc) != TCL_OK) { |
---|
6279 | Tcl_DecrRefCount(prefixObj); |
---|
6280 | Tcl_AddErrorInfo(interp, "\n while parsing result of " |
---|
6281 | "ensemble unknown subcommand handler"); |
---|
6282 | return TCL_ERROR; |
---|
6283 | } |
---|
6284 | if (prefixObjc > 0) { |
---|
6285 | goto runResultingSubcommand; |
---|
6286 | } |
---|
6287 | |
---|
6288 | /* |
---|
6289 | * Namespace alive & empty result => reparse. |
---|
6290 | */ |
---|
6291 | |
---|
6292 | Tcl_DecrRefCount(prefixObj); |
---|
6293 | goto restartEnsembleParse; |
---|
6294 | } |
---|
6295 | if (!Tcl_InterpDeleted(interp)) { |
---|
6296 | if (result != TCL_ERROR) { |
---|
6297 | char buf[TCL_INTEGER_SPACE]; |
---|
6298 | |
---|
6299 | Tcl_ResetResult(interp); |
---|
6300 | Tcl_SetResult(interp, |
---|
6301 | "unknown subcommand handler returned bad code: ", |
---|
6302 | TCL_STATIC); |
---|
6303 | switch (result) { |
---|
6304 | case TCL_RETURN: |
---|
6305 | Tcl_AppendResult(interp, "return", NULL); |
---|
6306 | break; |
---|
6307 | case TCL_BREAK: |
---|
6308 | Tcl_AppendResult(interp, "break", NULL); |
---|
6309 | break; |
---|
6310 | case TCL_CONTINUE: |
---|
6311 | Tcl_AppendResult(interp, "continue", NULL); |
---|
6312 | break; |
---|
6313 | default: |
---|
6314 | sprintf(buf, "%d", result); |
---|
6315 | Tcl_AppendResult(interp, buf, NULL); |
---|
6316 | } |
---|
6317 | Tcl_AddErrorInfo(interp, "\n result of " |
---|
6318 | "ensemble unknown subcommand handler: "); |
---|
6319 | Tcl_AddErrorInfo(interp, TclGetString(unknownCmd)); |
---|
6320 | } else { |
---|
6321 | Tcl_AddErrorInfo(interp, |
---|
6322 | "\n (ensemble unknown subcommand handler)"); |
---|
6323 | } |
---|
6324 | } |
---|
6325 | Tcl_DecrRefCount(unknownCmd); |
---|
6326 | Tcl_Release(ensemblePtr); |
---|
6327 | return TCL_ERROR; |
---|
6328 | } |
---|
6329 | |
---|
6330 | /* |
---|
6331 | * We cannot determine what subcommand to hand off to, so generate a |
---|
6332 | * (standard) failure message. Note the one odd case compared with |
---|
6333 | * standard ensemble-like command, which is where a namespace has no |
---|
6334 | * exported commands at all... |
---|
6335 | */ |
---|
6336 | |
---|
6337 | Tcl_ResetResult(interp); |
---|
6338 | Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ENSEMBLE", |
---|
6339 | TclGetString(objv[1]), NULL); |
---|
6340 | if (ensemblePtr->subcommandTable.numEntries == 0) { |
---|
6341 | Tcl_AppendResult(interp, "unknown subcommand \"",TclGetString(objv[1]), |
---|
6342 | "\": namespace ", ensemblePtr->nsPtr->fullName, |
---|
6343 | " does not export any commands", NULL); |
---|
6344 | Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "SUBCOMMAND", |
---|
6345 | TclGetString(objv[1]), NULL); |
---|
6346 | return TCL_ERROR; |
---|
6347 | } |
---|
6348 | Tcl_AppendResult(interp, "unknown ", |
---|
6349 | (ensemblePtr->flags & TCL_ENSEMBLE_PREFIX ? "or ambiguous " : ""), |
---|
6350 | "subcommand \"", TclGetString(objv[1]), "\": must be ", NULL); |
---|
6351 | if (ensemblePtr->subcommandTable.numEntries == 1) { |
---|
6352 | Tcl_AppendResult(interp, ensemblePtr->subcommandArrayPtr[0], NULL); |
---|
6353 | } else { |
---|
6354 | int i; |
---|
6355 | |
---|
6356 | for (i=0 ; i<ensemblePtr->subcommandTable.numEntries-1 ; i++) { |
---|
6357 | Tcl_AppendResult(interp, |
---|
6358 | ensemblePtr->subcommandArrayPtr[i], ", ", NULL); |
---|
6359 | } |
---|
6360 | Tcl_AppendResult(interp, "or ", |
---|
6361 | ensemblePtr->subcommandArrayPtr[i], NULL); |
---|
6362 | } |
---|
6363 | Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "SUBCOMMAND", |
---|
6364 | TclGetString(objv[1]), NULL); |
---|
6365 | return TCL_ERROR; |
---|
6366 | } |
---|
6367 | |
---|
6368 | /* |
---|
6369 | *---------------------------------------------------------------------- |
---|
6370 | * |
---|
6371 | * MakeCachedEnsembleCommand -- |
---|
6372 | * |
---|
6373 | * Cache what we've computed so far; it's not nice to repeatedly copy |
---|
6374 | * strings about. Note that to do this, we start by deleting any old |
---|
6375 | * representation that there was (though if it was an out of date |
---|
6376 | * ensemble rep, we can skip some of the deallocation process.) |
---|
6377 | * |
---|
6378 | * Results: |
---|
6379 | * None |
---|
6380 | * |
---|
6381 | * Side effects: |
---|
6382 | * Alters the internal representation of the first object parameter. |
---|
6383 | * |
---|
6384 | *---------------------------------------------------------------------- |
---|
6385 | */ |
---|
6386 | |
---|
6387 | static void |
---|
6388 | MakeCachedEnsembleCommand( |
---|
6389 | Tcl_Obj *objPtr, |
---|
6390 | EnsembleConfig *ensemblePtr, |
---|
6391 | const char *subcommandName, |
---|
6392 | Tcl_Obj *prefixObjPtr) |
---|
6393 | { |
---|
6394 | register EnsembleCmdRep *ensembleCmd; |
---|
6395 | int length; |
---|
6396 | |
---|
6397 | if (objPtr->typePtr == &tclEnsembleCmdType) { |
---|
6398 | ensembleCmd = objPtr->internalRep.otherValuePtr; |
---|
6399 | Tcl_DecrRefCount(ensembleCmd->realPrefixObj); |
---|
6400 | ensembleCmd->nsPtr->refCount--; |
---|
6401 | if ((ensembleCmd->nsPtr->refCount == 0) |
---|
6402 | && (ensembleCmd->nsPtr->flags & NS_DEAD)) { |
---|
6403 | NamespaceFree(ensembleCmd->nsPtr); |
---|
6404 | } |
---|
6405 | ckfree(ensembleCmd->fullSubcmdName); |
---|
6406 | } else { |
---|
6407 | /* |
---|
6408 | * Kill the old internal rep, and replace it with a brand new one of |
---|
6409 | * our own. |
---|
6410 | */ |
---|
6411 | |
---|
6412 | TclFreeIntRep(objPtr); |
---|
6413 | ensembleCmd = (EnsembleCmdRep *) ckalloc(sizeof(EnsembleCmdRep)); |
---|
6414 | objPtr->internalRep.otherValuePtr = ensembleCmd; |
---|
6415 | objPtr->typePtr = &tclEnsembleCmdType; |
---|
6416 | } |
---|
6417 | |
---|
6418 | /* |
---|
6419 | * Populate the internal rep. |
---|
6420 | */ |
---|
6421 | |
---|
6422 | ensembleCmd->nsPtr = ensemblePtr->nsPtr; |
---|
6423 | ensembleCmd->epoch = ensemblePtr->epoch; |
---|
6424 | ensembleCmd->token = ensemblePtr->token; |
---|
6425 | ensemblePtr->nsPtr->refCount++; |
---|
6426 | ensembleCmd->realPrefixObj = prefixObjPtr; |
---|
6427 | length = strlen(subcommandName)+1; |
---|
6428 | ensembleCmd->fullSubcmdName = ckalloc((unsigned) length); |
---|
6429 | memcpy(ensembleCmd->fullSubcmdName, subcommandName, (unsigned) length); |
---|
6430 | Tcl_IncrRefCount(ensembleCmd->realPrefixObj); |
---|
6431 | } |
---|
6432 | |
---|
6433 | /* |
---|
6434 | *---------------------------------------------------------------------- |
---|
6435 | * |
---|
6436 | * DeleteEnsembleConfig -- |
---|
6437 | * |
---|
6438 | * Destroys the data structure used to represent an ensemble. This is |
---|
6439 | * called when the ensemble's command is deleted (which happens |
---|
6440 | * automatically if the ensemble's namespace is deleted.) Maintainers |
---|
6441 | * should note that ensembles should be deleted by deleting their |
---|
6442 | * commands. |
---|
6443 | * |
---|
6444 | * Results: |
---|
6445 | * None. |
---|
6446 | * |
---|
6447 | * Side effects: |
---|
6448 | * Memory is (eventually) deallocated. |
---|
6449 | * |
---|
6450 | *---------------------------------------------------------------------- |
---|
6451 | */ |
---|
6452 | |
---|
6453 | static void |
---|
6454 | DeleteEnsembleConfig( |
---|
6455 | ClientData clientData) |
---|
6456 | { |
---|
6457 | EnsembleConfig *ensemblePtr = clientData; |
---|
6458 | Namespace *nsPtr = ensemblePtr->nsPtr; |
---|
6459 | Tcl_HashSearch search; |
---|
6460 | Tcl_HashEntry *hEnt; |
---|
6461 | |
---|
6462 | /* |
---|
6463 | * Unlink from the ensemble chain if it has not been marked as having been |
---|
6464 | * done already. |
---|
6465 | */ |
---|
6466 | |
---|
6467 | if (ensemblePtr->next != ensemblePtr) { |
---|
6468 | EnsembleConfig *ensPtr = (EnsembleConfig *) nsPtr->ensembles; |
---|
6469 | if (ensPtr == ensemblePtr) { |
---|
6470 | nsPtr->ensembles = (Tcl_Ensemble *) ensemblePtr->next; |
---|
6471 | } else { |
---|
6472 | while (ensPtr != NULL) { |
---|
6473 | if (ensPtr->next == ensemblePtr) { |
---|
6474 | ensPtr->next = ensemblePtr->next; |
---|
6475 | break; |
---|
6476 | } |
---|
6477 | ensPtr = ensPtr->next; |
---|
6478 | } |
---|
6479 | } |
---|
6480 | } |
---|
6481 | |
---|
6482 | /* |
---|
6483 | * Mark the namespace as dead so code that uses Tcl_Preserve() can tell |
---|
6484 | * whether disaster happened anyway. |
---|
6485 | */ |
---|
6486 | |
---|
6487 | ensemblePtr->flags |= ENS_DEAD; |
---|
6488 | |
---|
6489 | /* |
---|
6490 | * Kill the pointer-containing fields. |
---|
6491 | */ |
---|
6492 | |
---|
6493 | if (ensemblePtr->subcommandTable.numEntries != 0) { |
---|
6494 | ckfree((char *) ensemblePtr->subcommandArrayPtr); |
---|
6495 | } |
---|
6496 | hEnt = Tcl_FirstHashEntry(&ensemblePtr->subcommandTable, &search); |
---|
6497 | while (hEnt != NULL) { |
---|
6498 | Tcl_Obj *prefixObj = Tcl_GetHashValue(hEnt); |
---|
6499 | |
---|
6500 | Tcl_DecrRefCount(prefixObj); |
---|
6501 | hEnt = Tcl_NextHashEntry(&search); |
---|
6502 | } |
---|
6503 | Tcl_DeleteHashTable(&ensemblePtr->subcommandTable); |
---|
6504 | if (ensemblePtr->subcmdList != NULL) { |
---|
6505 | Tcl_DecrRefCount(ensemblePtr->subcmdList); |
---|
6506 | } |
---|
6507 | if (ensemblePtr->subcommandDict != NULL) { |
---|
6508 | Tcl_DecrRefCount(ensemblePtr->subcommandDict); |
---|
6509 | } |
---|
6510 | if (ensemblePtr->unknownHandler != NULL) { |
---|
6511 | Tcl_DecrRefCount(ensemblePtr->unknownHandler); |
---|
6512 | } |
---|
6513 | |
---|
6514 | /* |
---|
6515 | * Arrange for the structure to be reclaimed. Note that this is complex |
---|
6516 | * because we have to make sure that we can react sensibly when an |
---|
6517 | * ensemble is deleted during the process of initialising the ensemble |
---|
6518 | * (especially the unknown callback.) |
---|
6519 | */ |
---|
6520 | |
---|
6521 | Tcl_EventuallyFree(ensemblePtr, TCL_DYNAMIC); |
---|
6522 | } |
---|
6523 | |
---|
6524 | /* |
---|
6525 | *---------------------------------------------------------------------- |
---|
6526 | * |
---|
6527 | * BuildEnsembleConfig -- |
---|
6528 | * |
---|
6529 | * Create the internal data structures that describe how an ensemble |
---|
6530 | * looks, being a hash mapping from the full command name to the Tcl list |
---|
6531 | * that describes the implementation prefix words, and a sorted array of |
---|
6532 | * all the full command names to allow for reasonably efficient |
---|
6533 | * unambiguous prefix handling. |
---|
6534 | * |
---|
6535 | * Results: |
---|
6536 | * None. |
---|
6537 | * |
---|
6538 | * Side effects: |
---|
6539 | * Reallocates and rebuilds the hash table and array stored at the |
---|
6540 | * ensemblePtr argument. For large ensembles or large namespaces, this is |
---|
6541 | * a potentially expensive operation. |
---|
6542 | * |
---|
6543 | *---------------------------------------------------------------------- |
---|
6544 | */ |
---|
6545 | |
---|
6546 | static void |
---|
6547 | BuildEnsembleConfig( |
---|
6548 | EnsembleConfig *ensemblePtr) |
---|
6549 | { |
---|
6550 | Tcl_HashSearch search; /* Used for scanning the set of commands in |
---|
6551 | * the namespace that backs up this |
---|
6552 | * ensemble. */ |
---|
6553 | int i, j, isNew; |
---|
6554 | Tcl_HashTable *hash = &ensemblePtr->subcommandTable; |
---|
6555 | Tcl_HashEntry *hPtr; |
---|
6556 | |
---|
6557 | if (hash->numEntries != 0) { |
---|
6558 | /* |
---|
6559 | * Remove pre-existing table. |
---|
6560 | */ |
---|
6561 | |
---|
6562 | Tcl_HashSearch search; |
---|
6563 | |
---|
6564 | ckfree((char *) ensemblePtr->subcommandArrayPtr); |
---|
6565 | hPtr = Tcl_FirstHashEntry(hash, &search); |
---|
6566 | while (hPtr != NULL) { |
---|
6567 | Tcl_Obj *prefixObj = Tcl_GetHashValue(hPtr); |
---|
6568 | Tcl_DecrRefCount(prefixObj); |
---|
6569 | hPtr = Tcl_NextHashEntry(&search); |
---|
6570 | } |
---|
6571 | Tcl_DeleteHashTable(hash); |
---|
6572 | Tcl_InitHashTable(hash, TCL_STRING_KEYS); |
---|
6573 | } |
---|
6574 | |
---|
6575 | /* |
---|
6576 | * See if we've got an export list. If so, we will only export exactly |
---|
6577 | * those commands, which may be either implemented by the prefix in the |
---|
6578 | * subcommandDict or mapped directly onto the namespace's commands. |
---|
6579 | */ |
---|
6580 | |
---|
6581 | if (ensemblePtr->subcmdList != NULL) { |
---|
6582 | Tcl_Obj **subcmdv, *target, *cmdObj, *cmdPrefixObj; |
---|
6583 | int subcmdc; |
---|
6584 | |
---|
6585 | TclListObjGetElements(NULL, ensemblePtr->subcmdList, &subcmdc, |
---|
6586 | &subcmdv); |
---|
6587 | for (i=0 ; i<subcmdc ; i++) { |
---|
6588 | char *name = TclGetString(subcmdv[i]); |
---|
6589 | |
---|
6590 | hPtr = Tcl_CreateHashEntry(hash, name, &isNew); |
---|
6591 | |
---|
6592 | /* |
---|
6593 | * Skip non-unique cases. |
---|
6594 | */ |
---|
6595 | |
---|
6596 | if (!isNew) { |
---|
6597 | continue; |
---|
6598 | } |
---|
6599 | |
---|
6600 | /* |
---|
6601 | * Look in our dictionary (if present) for the command. |
---|
6602 | */ |
---|
6603 | |
---|
6604 | if (ensemblePtr->subcommandDict != NULL) { |
---|
6605 | Tcl_DictObjGet(NULL, ensemblePtr->subcommandDict, subcmdv[i], |
---|
6606 | &target); |
---|
6607 | if (target != NULL) { |
---|
6608 | Tcl_SetHashValue(hPtr, target); |
---|
6609 | Tcl_IncrRefCount(target); |
---|
6610 | continue; |
---|
6611 | } |
---|
6612 | } |
---|
6613 | |
---|
6614 | /* |
---|
6615 | * Not there, so map onto the namespace. Note in this case that we |
---|
6616 | * do not guarantee that the command is actually there; that is |
---|
6617 | * the programmer's responsibility (or [::unknown] of course). |
---|
6618 | */ |
---|
6619 | |
---|
6620 | cmdObj = Tcl_NewStringObj(ensemblePtr->nsPtr->fullName, -1); |
---|
6621 | if (ensemblePtr->nsPtr->parentPtr != NULL) { |
---|
6622 | Tcl_AppendStringsToObj(cmdObj, "::", name, NULL); |
---|
6623 | } else { |
---|
6624 | Tcl_AppendStringsToObj(cmdObj, name, NULL); |
---|
6625 | } |
---|
6626 | cmdPrefixObj = Tcl_NewListObj(1, &cmdObj); |
---|
6627 | Tcl_SetHashValue(hPtr, cmdPrefixObj); |
---|
6628 | Tcl_IncrRefCount(cmdPrefixObj); |
---|
6629 | } |
---|
6630 | } else if (ensemblePtr->subcommandDict != NULL) { |
---|
6631 | /* |
---|
6632 | * No subcmd list, but we do have a mapping dictionary so we should |
---|
6633 | * use the keys of that. Convert the dictionary's contents into the |
---|
6634 | * form required for the ensemble's internal hashtable. |
---|
6635 | */ |
---|
6636 | |
---|
6637 | Tcl_DictSearch dictSearch; |
---|
6638 | Tcl_Obj *keyObj, *valueObj; |
---|
6639 | int done; |
---|
6640 | |
---|
6641 | Tcl_DictObjFirst(NULL, ensemblePtr->subcommandDict, &dictSearch, |
---|
6642 | &keyObj, &valueObj, &done); |
---|
6643 | while (!done) { |
---|
6644 | char *name = TclGetString(keyObj); |
---|
6645 | |
---|
6646 | hPtr = Tcl_CreateHashEntry(hash, name, &isNew); |
---|
6647 | Tcl_SetHashValue(hPtr, valueObj); |
---|
6648 | Tcl_IncrRefCount(valueObj); |
---|
6649 | Tcl_DictObjNext(&dictSearch, &keyObj, &valueObj, &done); |
---|
6650 | } |
---|
6651 | } else { |
---|
6652 | /* |
---|
6653 | * Discover what commands are actually exported by the namespace. |
---|
6654 | * What we have is an array of patterns and a hash table whose keys |
---|
6655 | * are the command names exported by the namespace (the contents do |
---|
6656 | * not matter here.) We must find out what commands are actually |
---|
6657 | * exported by filtering each command in the namespace against each of |
---|
6658 | * the patterns in the export list. Note that we use an intermediate |
---|
6659 | * hash table to make memory management easier, and because that makes |
---|
6660 | * exact matching far easier too. |
---|
6661 | * |
---|
6662 | * Suggestion for future enhancement: compute the unique prefixes and |
---|
6663 | * place them in the hash too, which should make for even faster |
---|
6664 | * matching. |
---|
6665 | */ |
---|
6666 | |
---|
6667 | hPtr = Tcl_FirstHashEntry(&ensemblePtr->nsPtr->cmdTable, &search); |
---|
6668 | for (; hPtr!= NULL ; hPtr=Tcl_NextHashEntry(&search)) { |
---|
6669 | char *nsCmdName = /* Name of command in namespace. */ |
---|
6670 | Tcl_GetHashKey(&ensemblePtr->nsPtr->cmdTable, hPtr); |
---|
6671 | |
---|
6672 | for (i=0 ; i<ensemblePtr->nsPtr->numExportPatterns ; i++) { |
---|
6673 | if (Tcl_StringMatch(nsCmdName, |
---|
6674 | ensemblePtr->nsPtr->exportArrayPtr[i])) { |
---|
6675 | hPtr = Tcl_CreateHashEntry(hash, nsCmdName, &isNew); |
---|
6676 | |
---|
6677 | /* |
---|
6678 | * Remember, hash entries have a full reference to the |
---|
6679 | * substituted part of the command (as a list) as their |
---|
6680 | * content! |
---|
6681 | */ |
---|
6682 | |
---|
6683 | if (isNew) { |
---|
6684 | Tcl_Obj *cmdObj, *cmdPrefixObj; |
---|
6685 | |
---|
6686 | TclNewObj(cmdObj); |
---|
6687 | Tcl_AppendStringsToObj(cmdObj, |
---|
6688 | ensemblePtr->nsPtr->fullName, |
---|
6689 | (ensemblePtr->nsPtr->parentPtr ? "::" : ""), |
---|
6690 | nsCmdName, NULL); |
---|
6691 | cmdPrefixObj = Tcl_NewListObj(1, &cmdObj); |
---|
6692 | Tcl_SetHashValue(hPtr, cmdPrefixObj); |
---|
6693 | Tcl_IncrRefCount(cmdPrefixObj); |
---|
6694 | } |
---|
6695 | break; |
---|
6696 | } |
---|
6697 | } |
---|
6698 | } |
---|
6699 | } |
---|
6700 | |
---|
6701 | if (hash->numEntries == 0) { |
---|
6702 | ensemblePtr->subcommandArrayPtr = NULL; |
---|
6703 | return; |
---|
6704 | } |
---|
6705 | |
---|
6706 | /* |
---|
6707 | * Create a sorted array of all subcommands in the ensemble; hash tables |
---|
6708 | * are all very well for a quick look for an exact match, but they can't |
---|
6709 | * determine things like whether a string is a prefix of another (not |
---|
6710 | * without lots of preparation anyway) and they're no good for when we're |
---|
6711 | * generating the error message either. |
---|
6712 | * |
---|
6713 | * We do this by filling an array with the names (we use the hash keys |
---|
6714 | * directly to save a copy, since any time we change the array we change |
---|
6715 | * the hash too, and vice versa) and running quicksort over the array. |
---|
6716 | */ |
---|
6717 | |
---|
6718 | ensemblePtr->subcommandArrayPtr = (char **) |
---|
6719 | ckalloc(sizeof(char *) * hash->numEntries); |
---|
6720 | |
---|
6721 | /* |
---|
6722 | * Fill array from both ends as this makes us less likely to end up with |
---|
6723 | * performance problems in qsort(), which is good. Note that doing this |
---|
6724 | * makes this code much more opaque, but the naive alternatve: |
---|
6725 | * |
---|
6726 | * for (hPtr=Tcl_FirstHashEntry(hash,&search),i=0 ; |
---|
6727 | * hPtr!=NULL ; hPtr=Tcl_NextHashEntry(&search),i++) { |
---|
6728 | * ensemblePtr->subcommandArrayPtr[i] = Tcl_GetHashKey(hash, &hPtr); |
---|
6729 | * } |
---|
6730 | * |
---|
6731 | * can produce long runs of precisely ordered table entries when the |
---|
6732 | * commands in the namespace are declared in a sorted fashion (an ordering |
---|
6733 | * some people like) and the hashing functions (or the command names |
---|
6734 | * themselves) are fairly unfortunate. By filling from both ends, it |
---|
6735 | * requires active malice (and probably a debugger) to get qsort() to have |
---|
6736 | * awful runtime behaviour. |
---|
6737 | */ |
---|
6738 | |
---|
6739 | i = 0; |
---|
6740 | j = hash->numEntries; |
---|
6741 | hPtr = Tcl_FirstHashEntry(hash, &search); |
---|
6742 | while (hPtr != NULL) { |
---|
6743 | ensemblePtr->subcommandArrayPtr[i++] = Tcl_GetHashKey(hash, hPtr); |
---|
6744 | hPtr = Tcl_NextHashEntry(&search); |
---|
6745 | if (hPtr == NULL) { |
---|
6746 | break; |
---|
6747 | } |
---|
6748 | ensemblePtr->subcommandArrayPtr[--j] = Tcl_GetHashKey(hash, hPtr); |
---|
6749 | hPtr = Tcl_NextHashEntry(&search); |
---|
6750 | } |
---|
6751 | if (hash->numEntries > 1) { |
---|
6752 | qsort(ensemblePtr->subcommandArrayPtr, (unsigned)hash->numEntries, |
---|
6753 | sizeof(char *), NsEnsembleStringOrder); |
---|
6754 | } |
---|
6755 | } |
---|
6756 | |
---|
6757 | /* |
---|
6758 | *---------------------------------------------------------------------- |
---|
6759 | * |
---|
6760 | * NsEnsembleStringOrder -- |
---|
6761 | * |
---|
6762 | * Helper function to compare two pointers to two strings for use with |
---|
6763 | * qsort(). |
---|
6764 | * |
---|
6765 | * Results: |
---|
6766 | * -1 if the first string is smaller, 1 if the second string is smaller, |
---|
6767 | * and 0 if they are equal. |
---|
6768 | * |
---|
6769 | * Side effects: |
---|
6770 | * None. |
---|
6771 | * |
---|
6772 | *---------------------------------------------------------------------- |
---|
6773 | */ |
---|
6774 | |
---|
6775 | static int |
---|
6776 | NsEnsembleStringOrder( |
---|
6777 | const void *strPtr1, |
---|
6778 | const void *strPtr2) |
---|
6779 | { |
---|
6780 | return strcmp(*(const char **)strPtr1, *(const char **)strPtr2); |
---|
6781 | } |
---|
6782 | |
---|
6783 | /* |
---|
6784 | *---------------------------------------------------------------------- |
---|
6785 | * |
---|
6786 | * FreeEnsembleCmdRep -- |
---|
6787 | * |
---|
6788 | * Destroys the internal representation of a Tcl_Obj that has been |
---|
6789 | * holding information about a command in an ensemble. |
---|
6790 | * |
---|
6791 | * Results: |
---|
6792 | * None. |
---|
6793 | * |
---|
6794 | * Side effects: |
---|
6795 | * Memory is deallocated. If this held the last reference to a |
---|
6796 | * namespace's main structure, that main structure will also be |
---|
6797 | * destroyed. |
---|
6798 | * |
---|
6799 | *---------------------------------------------------------------------- |
---|
6800 | */ |
---|
6801 | |
---|
6802 | static void |
---|
6803 | FreeEnsembleCmdRep( |
---|
6804 | Tcl_Obj *objPtr) |
---|
6805 | { |
---|
6806 | EnsembleCmdRep *ensembleCmd = objPtr->internalRep.otherValuePtr; |
---|
6807 | |
---|
6808 | Tcl_DecrRefCount(ensembleCmd->realPrefixObj); |
---|
6809 | ckfree(ensembleCmd->fullSubcmdName); |
---|
6810 | ensembleCmd->nsPtr->refCount--; |
---|
6811 | if ((ensembleCmd->nsPtr->refCount == 0) |
---|
6812 | && (ensembleCmd->nsPtr->flags & NS_DEAD)) { |
---|
6813 | NamespaceFree(ensembleCmd->nsPtr); |
---|
6814 | } |
---|
6815 | ckfree((char *) ensembleCmd); |
---|
6816 | } |
---|
6817 | |
---|
6818 | /* |
---|
6819 | *---------------------------------------------------------------------- |
---|
6820 | * |
---|
6821 | * DupEnsembleCmdRep -- |
---|
6822 | * |
---|
6823 | * Makes one Tcl_Obj into a copy of another that is a subcommand of an |
---|
6824 | * ensemble. |
---|
6825 | * |
---|
6826 | * Results: |
---|
6827 | * None. |
---|
6828 | * |
---|
6829 | * Side effects: |
---|
6830 | * Memory is allocated, and the namespace that the ensemble is built on |
---|
6831 | * top of gains another reference. |
---|
6832 | * |
---|
6833 | *---------------------------------------------------------------------- |
---|
6834 | */ |
---|
6835 | |
---|
6836 | static void |
---|
6837 | DupEnsembleCmdRep( |
---|
6838 | Tcl_Obj *objPtr, |
---|
6839 | Tcl_Obj *copyPtr) |
---|
6840 | { |
---|
6841 | EnsembleCmdRep *ensembleCmd = objPtr->internalRep.otherValuePtr; |
---|
6842 | EnsembleCmdRep *ensembleCopy = (EnsembleCmdRep *) |
---|
6843 | ckalloc(sizeof(EnsembleCmdRep)); |
---|
6844 | int length = strlen(ensembleCmd->fullSubcmdName); |
---|
6845 | |
---|
6846 | copyPtr->typePtr = &tclEnsembleCmdType; |
---|
6847 | copyPtr->internalRep.otherValuePtr = ensembleCopy; |
---|
6848 | ensembleCopy->nsPtr = ensembleCmd->nsPtr; |
---|
6849 | ensembleCopy->epoch = ensembleCmd->epoch; |
---|
6850 | ensembleCopy->token = ensembleCmd->token; |
---|
6851 | ensembleCopy->nsPtr->refCount++; |
---|
6852 | ensembleCopy->realPrefixObj = ensembleCmd->realPrefixObj; |
---|
6853 | Tcl_IncrRefCount(ensembleCopy->realPrefixObj); |
---|
6854 | ensembleCopy->fullSubcmdName = ckalloc((unsigned) length+1); |
---|
6855 | memcpy(ensembleCopy->fullSubcmdName, ensembleCmd->fullSubcmdName, |
---|
6856 | (unsigned) length+1); |
---|
6857 | } |
---|
6858 | |
---|
6859 | /* |
---|
6860 | *---------------------------------------------------------------------- |
---|
6861 | * |
---|
6862 | * StringOfEnsembleCmdRep -- |
---|
6863 | * |
---|
6864 | * Creates a string representation of a Tcl_Obj that holds a subcommand |
---|
6865 | * of an ensemble. |
---|
6866 | * |
---|
6867 | * Results: |
---|
6868 | * None. |
---|
6869 | * |
---|
6870 | * Side effects: |
---|
6871 | * The object gains a string (UTF-8) representation. |
---|
6872 | * |
---|
6873 | *---------------------------------------------------------------------- |
---|
6874 | */ |
---|
6875 | |
---|
6876 | static void |
---|
6877 | StringOfEnsembleCmdRep( |
---|
6878 | Tcl_Obj *objPtr) |
---|
6879 | { |
---|
6880 | EnsembleCmdRep *ensembleCmd = objPtr->internalRep.otherValuePtr; |
---|
6881 | int length = strlen(ensembleCmd->fullSubcmdName); |
---|
6882 | |
---|
6883 | objPtr->length = length; |
---|
6884 | objPtr->bytes = ckalloc((unsigned) length+1); |
---|
6885 | memcpy(objPtr->bytes, ensembleCmd->fullSubcmdName, (unsigned) length+1); |
---|
6886 | } |
---|
6887 | |
---|
6888 | /* |
---|
6889 | *---------------------------------------------------------------------- |
---|
6890 | * |
---|
6891 | * Tcl_LogCommandInfo -- |
---|
6892 | * |
---|
6893 | * This function is invoked after an error occurs in an interpreter. It |
---|
6894 | * adds information to iPtr->errorInfo field to describe the command that |
---|
6895 | * was being executed when the error occurred. |
---|
6896 | * |
---|
6897 | * Results: |
---|
6898 | * None. |
---|
6899 | * |
---|
6900 | * Side effects: |
---|
6901 | * Information about the command is added to errorInfo and the line |
---|
6902 | * number stored internally in the interpreter is set. |
---|
6903 | * |
---|
6904 | *---------------------------------------------------------------------- |
---|
6905 | */ |
---|
6906 | |
---|
6907 | void |
---|
6908 | Tcl_LogCommandInfo( |
---|
6909 | Tcl_Interp *interp, /* Interpreter in which to log information. */ |
---|
6910 | const char *script, /* First character in script containing |
---|
6911 | * command (must be <= command). */ |
---|
6912 | const char *command, /* First character in command that generated |
---|
6913 | * the error. */ |
---|
6914 | int length) /* Number of bytes in command (-1 means use |
---|
6915 | * all bytes up to first null byte). */ |
---|
6916 | { |
---|
6917 | register const char *p; |
---|
6918 | Interp *iPtr = (Interp *) interp; |
---|
6919 | int overflow, limit = 150; |
---|
6920 | Var *varPtr, *arrayPtr; |
---|
6921 | |
---|
6922 | if (iPtr->flags & ERR_ALREADY_LOGGED) { |
---|
6923 | /* |
---|
6924 | * Someone else has already logged error information for this command; |
---|
6925 | * we shouldn't add anything more. |
---|
6926 | */ |
---|
6927 | |
---|
6928 | return; |
---|
6929 | } |
---|
6930 | |
---|
6931 | /* |
---|
6932 | * Compute the line number where the error occurred. |
---|
6933 | */ |
---|
6934 | |
---|
6935 | iPtr->errorLine = 1; |
---|
6936 | for (p = script; p != command; p++) { |
---|
6937 | if (*p == '\n') { |
---|
6938 | iPtr->errorLine++; |
---|
6939 | } |
---|
6940 | } |
---|
6941 | |
---|
6942 | overflow = (length > limit); |
---|
6943 | Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( |
---|
6944 | "\n %s\n\"%.*s%s\"", ((iPtr->errorInfo == NULL) |
---|
6945 | ? "while executing" : "invoked from within"), |
---|
6946 | (overflow ? limit : length), command, (overflow ? "..." : ""))); |
---|
6947 | |
---|
6948 | varPtr = TclObjLookupVarEx(interp, iPtr->eiVar, NULL, TCL_GLOBAL_ONLY, |
---|
6949 | NULL, 0, 0, &arrayPtr); |
---|
6950 | if ((varPtr == NULL) || !TclIsVarTraced(varPtr)) { |
---|
6951 | /* |
---|
6952 | * Should not happen. |
---|
6953 | */ |
---|
6954 | |
---|
6955 | return; |
---|
6956 | } else { |
---|
6957 | Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&iPtr->varTraces, |
---|
6958 | (char *) varPtr); |
---|
6959 | VarTrace *tracePtr = Tcl_GetHashValue(hPtr); |
---|
6960 | |
---|
6961 | if (tracePtr->traceProc != EstablishErrorInfoTraces) { |
---|
6962 | /* |
---|
6963 | * The most recent trace set on ::errorInfo is not the one the |
---|
6964 | * core itself puts on last. This means some other code is tracing |
---|
6965 | * the variable, and the additional trace(s) might be write traces |
---|
6966 | * that expect the timing of writes to ::errorInfo that existed |
---|
6967 | * Tcl releases before 8.5. To satisfy that compatibility need, we |
---|
6968 | * write the current -errorinfo value to the ::errorInfo variable. |
---|
6969 | */ |
---|
6970 | |
---|
6971 | Tcl_ObjSetVar2(interp, iPtr->eiVar, NULL, iPtr->errorInfo, |
---|
6972 | TCL_GLOBAL_ONLY); |
---|
6973 | } |
---|
6974 | } |
---|
6975 | } |
---|
6976 | |
---|
6977 | /* |
---|
6978 | * Local Variables: |
---|
6979 | * mode: c |
---|
6980 | * c-basic-offset: 4 |
---|
6981 | * fill-column: 78 |
---|
6982 | * End: |
---|
6983 | */ |
---|