Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtksh/tcl/tclLoad.c
1810 views
1
/*
2
* tclLoad.c --
3
*
4
* This file provides the generic portion (those that are the same
5
* on all platforms) of Tcl's dynamic loading facilities.
6
*
7
* Copyright (c) 1995 Sun Microsystems, Inc.
8
*
9
* See the file "license.terms" for information on usage and redistribution
10
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11
*
12
* SCCS: @(#) tclLoad.c 1.15 96/10/12 17:05:58
13
*/
14
15
#include "tclInt.h"
16
17
/*
18
* The following structure describes a package that has been loaded
19
* either dynamically (with the "load" command) or statically (as
20
* indicated by a call to Tcl_PackageLoaded). All such packages
21
* are linked together into a single list for the process. Packages
22
* are never unloaded, so these structures are never freed.
23
*/
24
25
typedef struct LoadedPackage {
26
char *fileName; /* Name of the file from which the
27
* package was loaded. An empty string
28
* means the package is loaded statically.
29
* Malloc-ed. */
30
char *packageName; /* Name of package prefix for the package,
31
* properly capitalized (first letter UC,
32
* others LC), no "_", as in "Net".
33
* Malloc-ed. */
34
Tcl_PackageInitProc *initProc;
35
/* Initialization procedure to call to
36
* incorporate this package into a trusted
37
* interpreter. */
38
Tcl_PackageInitProc *safeInitProc;
39
/* Initialization procedure to call to
40
* incorporate this package into a safe
41
* interpreter (one that will execute
42
* untrusted scripts). NULL means the
43
* package can't be used in unsafe
44
* interpreters. */
45
struct LoadedPackage *nextPtr;
46
/* Next in list of all packages loaded into
47
* this application process. NULL means
48
* end of list. */
49
} LoadedPackage;
50
51
static LoadedPackage *firstPackagePtr = NULL;
52
/* First in list of all packages loaded into
53
* this process. */
54
55
/*
56
* The following structure represents a particular package that has
57
* been incorporated into a particular interpreter (by calling its
58
* initialization procedure). There is a list of these structures for
59
* each interpreter, with an AssocData value (key "load") for the
60
* interpreter that points to the first package (if any).
61
*/
62
63
typedef struct InterpPackage {
64
LoadedPackage *pkgPtr; /* Points to detailed information about
65
* package. */
66
struct InterpPackage *nextPtr;
67
/* Next package in this interpreter, or
68
* NULL for end of list. */
69
} InterpPackage;
70
71
/*
72
* Prototypes for procedures that are private to this file:
73
*/
74
75
static void LoadCleanupProc _ANSI_ARGS_((ClientData clientData,
76
Tcl_Interp *interp));
77
static void LoadExitProc _ANSI_ARGS_((ClientData clientData));
78
79
/*
80
*----------------------------------------------------------------------
81
*
82
* Tcl_LoadCmd --
83
*
84
* This procedure is invoked to process the "load" Tcl command.
85
* See the user documentation for details on what it does.
86
*
87
* Results:
88
* A standard Tcl result.
89
*
90
* Side effects:
91
* See the user documentation.
92
*
93
*----------------------------------------------------------------------
94
*/
95
96
int
97
Tcl_LoadCmd(dummy, interp, argc, argv)
98
ClientData dummy; /* Not used. */
99
Tcl_Interp *interp; /* Current interpreter. */
100
int argc; /* Number of arguments. */
101
char **argv; /* Argument strings. */
102
{
103
Tcl_Interp *target;
104
LoadedPackage *pkgPtr, *defaultPtr;
105
Tcl_DString pkgName, initName, safeInitName, fileName;
106
Tcl_PackageInitProc *initProc, *safeInitProc;
107
InterpPackage *ipFirstPtr, *ipPtr;
108
int code, c, gotPkgName, namesMatch, filesMatch;
109
char *p, *fullFileName, *p1, *p2;
110
111
if ((argc < 2) || (argc > 4)) {
112
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
113
" fileName ?packageName? ?interp?\"", (char *) NULL);
114
return TCL_ERROR;
115
}
116
fullFileName = Tcl_TranslateFileName(interp, argv[1], &fileName);
117
if (fullFileName == NULL) {
118
return TCL_ERROR;
119
}
120
Tcl_DStringInit(&pkgName);
121
Tcl_DStringInit(&initName);
122
Tcl_DStringInit(&safeInitName);
123
if ((argc >= 3) && (argv[2][0] != 0)) {
124
gotPkgName = 1;
125
} else {
126
gotPkgName = 0;
127
}
128
if ((fullFileName[0] == 0) && !gotPkgName) {
129
interp->result = "must specify either file name or package name";
130
code = TCL_ERROR;
131
goto done;
132
}
133
134
/*
135
* Figure out which interpreter we're going to load the package into.
136
*/
137
138
target = interp;
139
if (argc == 4) {
140
target = Tcl_GetSlave(interp, argv[3]);
141
if (target == NULL) {
142
Tcl_AppendResult(interp, "couldn't find slave interpreter named \"",
143
argv[3], "\"", (char *) NULL);
144
return TCL_ERROR;
145
}
146
}
147
148
/*
149
* Scan through the packages that are currently loaded to see if the
150
* package we want is already loaded. We'll use a loaded package if
151
* it meets any of the following conditions:
152
* - Its name and file match the once we're looking for.
153
* - Its file matches, and we weren't given a name.
154
* - Its name matches, the file name was specified as empty, and there
155
* is only no statically loaded package with the same name.
156
*/
157
158
defaultPtr = NULL;
159
for (pkgPtr = firstPackagePtr; pkgPtr != NULL; pkgPtr = pkgPtr->nextPtr) {
160
if (!gotPkgName) {
161
namesMatch = 0;
162
} else {
163
namesMatch = 1;
164
for (p1 = argv[2], p2 = pkgPtr->packageName; ; p1++, p2++) {
165
if ((isupper(UCHAR(*p1)) ? tolower(UCHAR(*p1)) : *p1)
166
!= (isupper(UCHAR(*p2)) ? tolower(UCHAR(*p2)) : *p2)) {
167
namesMatch = 0;
168
break;
169
}
170
if (*p1 == 0) {
171
break;
172
}
173
}
174
}
175
filesMatch = (strcmp(pkgPtr->fileName, fullFileName) == 0);
176
if (filesMatch && (namesMatch || !gotPkgName)) {
177
break;
178
}
179
if (namesMatch && (fullFileName[0] == 0)) {
180
defaultPtr = pkgPtr;
181
}
182
if (filesMatch && !namesMatch && (fullFileName[0] != 0)) {
183
/*
184
* Can't have two different packages loaded from the same
185
* file.
186
*/
187
188
Tcl_AppendResult(interp, "file \"", fullFileName,
189
"\" is already loaded for package \"",
190
pkgPtr->packageName, "\"", (char *) NULL);
191
code = TCL_ERROR;
192
goto done;
193
}
194
}
195
if (pkgPtr == NULL) {
196
pkgPtr = defaultPtr;
197
}
198
199
/*
200
* Scan through the list of packages already loaded in the target
201
* interpreter. If the package we want is already loaded there,
202
* then there's nothing for us to to.
203
*/
204
205
if (pkgPtr != NULL) {
206
ipFirstPtr = (InterpPackage *) Tcl_GetAssocData(target, "tclLoad",
207
(Tcl_InterpDeleteProc **) NULL);
208
for (ipPtr = ipFirstPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
209
if (ipPtr->pkgPtr == pkgPtr) {
210
code = TCL_OK;
211
goto done;
212
}
213
}
214
}
215
216
if (pkgPtr == NULL) {
217
/*
218
* The desired file isn't currently loaded, so load it. It's an
219
* error if the desired package is a static one.
220
*/
221
222
if (fullFileName[0] == 0) {
223
Tcl_AppendResult(interp, "package \"", argv[2],
224
"\" isn't loaded statically", (char *) NULL);
225
code = TCL_ERROR;
226
goto done;
227
}
228
229
/*
230
* Figure out the module name if it wasn't provided explicitly.
231
*/
232
233
if (gotPkgName) {
234
Tcl_DStringAppend(&pkgName, argv[2], -1);
235
} else {
236
if (!TclGuessPackageName(fullFileName, &pkgName)) {
237
int pargc;
238
char **pargv, *pkgGuess;
239
240
/*
241
* The platform-specific code couldn't figure out the
242
* module name. Make a guess by taking the last element
243
* of the file name, stripping off any leading "lib",
244
* and then using all of the alphabetic and underline
245
* characters that follow that.
246
*/
247
248
Tcl_SplitPath(fullFileName, &pargc, &pargv);
249
pkgGuess = pargv[pargc-1];
250
if ((pkgGuess[0] == 'l') && (pkgGuess[1] == 'i')
251
&& (pkgGuess[2] == 'b')) {
252
pkgGuess += 3;
253
}
254
for (p = pkgGuess; isalpha(UCHAR(*p)) || (*p == '_'); p++) {
255
/* Empty loop body. */
256
}
257
if (p == pkgGuess) {
258
ckfree((char *)pargv);
259
Tcl_AppendResult(interp,
260
"couldn't figure out package name for ",
261
fullFileName, (char *) NULL);
262
code = TCL_ERROR;
263
goto done;
264
}
265
Tcl_DStringAppend(&pkgName, pkgGuess, (p - pkgGuess));
266
ckfree((char *)pargv);
267
}
268
}
269
270
/*
271
* Fix the capitalization in the package name so that the first
272
* character is in caps but the others are all lower-case.
273
*/
274
275
p = Tcl_DStringValue(&pkgName);
276
c = UCHAR(*p);
277
if (c != 0) {
278
if (islower(c)) {
279
*p = (char) toupper(c);
280
}
281
p++;
282
while (1) {
283
c = UCHAR(*p);
284
if (c == 0) {
285
break;
286
}
287
if (isupper(c)) {
288
*p = (char) tolower(c);
289
}
290
p++;
291
}
292
}
293
294
/*
295
* Compute the names of the two initialization procedures,
296
* based on the package name.
297
*/
298
299
Tcl_DStringAppend(&initName, Tcl_DStringValue(&pkgName), -1);
300
Tcl_DStringAppend(&initName, "_Init", 5);
301
Tcl_DStringAppend(&safeInitName, Tcl_DStringValue(&pkgName), -1);
302
Tcl_DStringAppend(&safeInitName, "_SafeInit", 9);
303
304
/*
305
* Call platform-specific code to load the package and find the
306
* two initialization procedures.
307
*/
308
309
code = TclLoadFile(interp, fullFileName, Tcl_DStringValue(&initName),
310
Tcl_DStringValue(&safeInitName), &initProc, &safeInitProc);
311
if (code != TCL_OK) {
312
goto done;
313
}
314
if (initProc == NULL) {
315
Tcl_AppendResult(interp, "couldn't find procedure ",
316
Tcl_DStringValue(&initName), (char *) NULL);
317
code = TCL_ERROR;
318
goto done;
319
}
320
321
/*
322
* Create a new record to describe this package.
323
*/
324
325
if (firstPackagePtr == NULL) {
326
Tcl_CreateExitHandler(LoadExitProc, (ClientData) NULL);
327
}
328
pkgPtr = (LoadedPackage *) ckalloc(sizeof(LoadedPackage));
329
pkgPtr->fileName = (char *) ckalloc((unsigned)
330
(strlen(fullFileName) + 1));
331
strcpy(pkgPtr->fileName, fullFileName);
332
pkgPtr->packageName = (char *) ckalloc((unsigned)
333
(Tcl_DStringLength(&pkgName) + 1));
334
strcpy(pkgPtr->packageName, Tcl_DStringValue(&pkgName));
335
pkgPtr->initProc = initProc;
336
pkgPtr->safeInitProc = safeInitProc;
337
pkgPtr->nextPtr = firstPackagePtr;
338
firstPackagePtr = pkgPtr;
339
}
340
341
/*
342
* Invoke the package's initialization procedure (either the
343
* normal one or the safe one, depending on whether or not the
344
* interpreter is safe).
345
*/
346
347
if (Tcl_IsSafe(target)) {
348
if (pkgPtr->safeInitProc != NULL) {
349
code = (*pkgPtr->safeInitProc)(target);
350
} else {
351
Tcl_AppendResult(interp,
352
"can't use package in a safe interpreter: ",
353
"no ", pkgPtr->packageName, "_SafeInit procedure",
354
(char *) NULL);
355
code = TCL_ERROR;
356
goto done;
357
}
358
} else {
359
code = (*pkgPtr->initProc)(target);
360
}
361
if ((code == TCL_ERROR) && (target != interp)) {
362
/*
363
* An error occurred, so transfer error information from the
364
* destination interpreter back to our interpreter. Must clear
365
* interp's result before calling Tcl_AddErrorInfo, since
366
* Tcl_AddErrorInfo will store the interp's result in errorInfo
367
* before appending target's $errorInfo; we've already got
368
* everything we need in target's $errorInfo.
369
*/
370
371
Tcl_ResetResult(interp);
372
Tcl_AddErrorInfo(interp, Tcl_GetVar2(target,
373
"errorInfo", (char *) NULL, TCL_GLOBAL_ONLY));
374
Tcl_SetVar2(interp, "errorCode", (char *) NULL,
375
Tcl_GetVar2(target, "errorCode", (char *) NULL,
376
TCL_GLOBAL_ONLY), TCL_GLOBAL_ONLY);
377
Tcl_SetResult(interp, target->result, TCL_VOLATILE);
378
}
379
380
/*
381
* Record the fact that the package has been loaded in the
382
* target interpreter.
383
*/
384
385
if (code == TCL_OK) {
386
/*
387
* Refetch ipFirstPtr: loading the package may have introduced
388
* additional static packages at the head of the linked list!
389
*/
390
391
ipFirstPtr = (InterpPackage *) Tcl_GetAssocData(target, "tclLoad",
392
(Tcl_InterpDeleteProc **) NULL);
393
ipPtr = (InterpPackage *) ckalloc(sizeof(InterpPackage));
394
ipPtr->pkgPtr = pkgPtr;
395
ipPtr->nextPtr = ipFirstPtr;
396
Tcl_SetAssocData(target, "tclLoad", LoadCleanupProc,
397
(ClientData) ipPtr);
398
}
399
400
done:
401
Tcl_DStringFree(&pkgName);
402
Tcl_DStringFree(&initName);
403
Tcl_DStringFree(&safeInitName);
404
Tcl_DStringFree(&fileName);
405
return code;
406
}
407
408
/*
409
*----------------------------------------------------------------------
410
*
411
* Tcl_StaticPackage --
412
*
413
* This procedure is invoked to indicate that a particular
414
* package has been linked statically with an application.
415
*
416
* Results:
417
* None.
418
*
419
* Side effects:
420
* Once this procedure completes, the package becomes loadable
421
* via the "load" command with an empty file name.
422
*
423
*----------------------------------------------------------------------
424
*/
425
426
void
427
Tcl_StaticPackage(interp, pkgName, initProc, safeInitProc)
428
Tcl_Interp *interp; /* If not NULL, it means that the
429
* package has already been loaded
430
* into the given interpreter by
431
* calling the appropriate init proc. */
432
char *pkgName; /* Name of package (must be properly
433
* capitalized: first letter upper
434
* case, others lower case). */
435
Tcl_PackageInitProc *initProc; /* Procedure to call to incorporate
436
* this package into a trusted
437
* interpreter. */
438
Tcl_PackageInitProc *safeInitProc; /* Procedure to call to incorporate
439
* this package into a safe interpreter
440
* (one that will execute untrusted
441
* scripts). NULL means the package
442
* can't be used in safe
443
* interpreters. */
444
{
445
LoadedPackage *pkgPtr;
446
InterpPackage *ipPtr, *ipFirstPtr;
447
448
/*
449
* Check to see if someone else has already reported this package as
450
* statically loaded. If this call is redundant then just return.
451
*/
452
453
for (pkgPtr = firstPackagePtr; pkgPtr != NULL; pkgPtr = pkgPtr->nextPtr) {
454
if ((pkgPtr->initProc == initProc)
455
&& (pkgPtr->safeInitProc == safeInitProc)
456
&& (strcmp(pkgPtr->packageName, pkgName) == 0)) {
457
return;
458
}
459
}
460
461
if (firstPackagePtr == NULL) {
462
Tcl_CreateExitHandler(LoadExitProc, (ClientData) NULL);
463
}
464
pkgPtr = (LoadedPackage *) ckalloc(sizeof(LoadedPackage));
465
pkgPtr->fileName = (char *) ckalloc((unsigned) 1);
466
pkgPtr->fileName[0] = 0;
467
pkgPtr->packageName = (char *) ckalloc((unsigned)
468
(strlen(pkgName) + 1));
469
strcpy(pkgPtr->packageName, pkgName);
470
pkgPtr->initProc = initProc;
471
pkgPtr->safeInitProc = safeInitProc;
472
pkgPtr->nextPtr = firstPackagePtr;
473
firstPackagePtr = pkgPtr;
474
475
if (interp != NULL) {
476
ipFirstPtr = (InterpPackage *) Tcl_GetAssocData(interp, "tclLoad",
477
(Tcl_InterpDeleteProc **) NULL);
478
ipPtr = (InterpPackage *) ckalloc(sizeof(InterpPackage));
479
ipPtr->pkgPtr = pkgPtr;
480
ipPtr->nextPtr = ipFirstPtr;
481
Tcl_SetAssocData(interp, "tclLoad", LoadCleanupProc,
482
(ClientData) ipPtr);
483
}
484
}
485
486
/*
487
*----------------------------------------------------------------------
488
*
489
* TclGetLoadedPackages --
490
*
491
* This procedure returns information about all of the files
492
* that are loaded (either in a particular intepreter, or
493
* for all interpreters).
494
*
495
* Results:
496
* The return value is a standard Tcl completion code. If
497
* successful, a list of lists is placed in interp->result.
498
* Each sublist corresponds to one loaded file; its first
499
* element is the name of the file (or an empty string for
500
* something that's statically loaded) and the second element
501
* is the name of the package in that file.
502
*
503
* Side effects:
504
* None.
505
*
506
*----------------------------------------------------------------------
507
*/
508
509
int
510
TclGetLoadedPackages(interp, targetName)
511
Tcl_Interp *interp; /* Interpreter in which to return
512
* information or error message. */
513
char *targetName; /* Name of target interpreter or NULL.
514
* If NULL, return info about all interps;
515
* otherwise, just return info about this
516
* interpreter. */
517
{
518
Tcl_Interp *target;
519
LoadedPackage *pkgPtr;
520
InterpPackage *ipPtr;
521
char *prefix;
522
523
if (targetName == NULL) {
524
/*
525
* Return information about all of the available packages.
526
*/
527
528
prefix = "{";
529
for (pkgPtr = firstPackagePtr; pkgPtr != NULL;
530
pkgPtr = pkgPtr->nextPtr) {
531
Tcl_AppendResult(interp, prefix, (char *) NULL);
532
Tcl_AppendElement(interp, pkgPtr->fileName);
533
Tcl_AppendElement(interp, pkgPtr->packageName);
534
Tcl_AppendResult(interp, "}", (char *) NULL);
535
prefix = " {";
536
}
537
return TCL_OK;
538
}
539
540
/*
541
* Return information about only the packages that are loaded in
542
* a given interpreter.
543
*/
544
545
target = Tcl_GetSlave(interp, targetName);
546
if (target == NULL) {
547
Tcl_AppendResult(interp, "couldn't find slave interpreter named \"",
548
targetName, "\"", (char *) NULL);
549
return TCL_ERROR;
550
}
551
ipPtr = (InterpPackage *) Tcl_GetAssocData(target, "tclLoad",
552
(Tcl_InterpDeleteProc **) NULL);
553
prefix = "{";
554
for ( ; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
555
pkgPtr = ipPtr->pkgPtr;
556
Tcl_AppendResult(interp, prefix, (char *) NULL);
557
Tcl_AppendElement(interp, pkgPtr->fileName);
558
Tcl_AppendElement(interp, pkgPtr->packageName);
559
Tcl_AppendResult(interp, "}", (char *) NULL);
560
prefix = " {";
561
}
562
return TCL_OK;
563
}
564
565
/*
566
*----------------------------------------------------------------------
567
*
568
* LoadCleanupProc --
569
*
570
* This procedure is called to delete all of the InterpPackage
571
* structures for an interpreter when the interpreter is deleted.
572
* It gets invoked via the Tcl AssocData mechanism.
573
*
574
* Results:
575
* None.
576
*
577
* Side effects:
578
* Storage for all of the InterpPackage procedures for interp
579
* get deleted.
580
*
581
*----------------------------------------------------------------------
582
*/
583
584
static void
585
LoadCleanupProc(clientData, interp)
586
ClientData clientData; /* Pointer to first InterpPackage structure
587
* for interp. */
588
Tcl_Interp *interp; /* Interpreter that is being deleted. */
589
{
590
InterpPackage *ipPtr, *nextPtr;
591
592
ipPtr = (InterpPackage *) clientData;
593
while (ipPtr != NULL) {
594
nextPtr = ipPtr->nextPtr;
595
ckfree((char *) ipPtr);
596
ipPtr = nextPtr;
597
}
598
}
599
600
/*
601
*----------------------------------------------------------------------
602
*
603
* LoadExitProc --
604
*
605
* This procedure is invoked just before the application exits.
606
* It frees all of the LoadedPackage structures.
607
*
608
* Results:
609
* None.
610
*
611
* Side effects:
612
* Memory is freed.
613
*
614
*----------------------------------------------------------------------
615
*/
616
617
static void
618
LoadExitProc(clientData)
619
ClientData clientData; /* Not used. */
620
{
621
LoadedPackage *pkgPtr;
622
623
while (firstPackagePtr != NULL) {
624
pkgPtr = firstPackagePtr;
625
firstPackagePtr = pkgPtr->nextPtr;
626
ckfree(pkgPtr->fileName);
627
ckfree(pkgPtr->packageName);
628
ckfree((char *) pkgPtr);
629
}
630
}
631
632