]>
cvs.zerfleddert.de Git - micropolis/blob - src/tclx/src/tclxlib.c
4 * Tcl commands to load libraries of Tcl code.
5 *-----------------------------------------------------------------------------
6 * Copyright 1992 Karl Lehenbauer and Mark Diekhans.
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation for any purpose and without fee is hereby granted, provided
10 * that the above copyright notice appear in all copies. Karl Lehenbauer and
11 * Mark Diekhans make no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without express or
14 *-----------------------------------------------------------------------------
15 * $Id: tclXlib.c,v 2.0 1992/10/16 04:50:55 markd Rel $
16 *-----------------------------------------------------------------------------
19 /*-----------------------------------------------------------------------------
21 * The following data structures are used by the Tcl library code. All
22 * structures are kept in the global array TCLENV, so that Tcl procs may be
23 * written to access them.
25 * o fileId - This is a small string used to uniquely identify a file, it is
26 * in the form "@$dev:$inode", where dev and inode are the values obtained
29 * o TCLENV(fileId} filePath - This entry translates a file id to an
30 * file name, which may be an absolute path to a file or the name of
31 * a file to find by searching a path.
33 * o TCLENV(PKG:$packageName) {$fileId $offset $length} - This entry
34 * translates a package name into a fileId of the file containing the
35 * package and the byte and offset length of the package within the file.
37 * o TCLENV(PROC:$proc) {P $packageName} - This form of a procedure entry
38 * translates a procedure into a package name.
40 * o TCLENV(PROC:$proc) {F $fileName} 0 - This form of a procedure entry
41 * translates a procedure into a file name. The file name may be an
42 * absolute path to the file or a file to be found by searching TCLPATH
44 *-----------------------------------------------------------------------------
48 typedef char fileId_t
[64];
51 * Prototypes of internal functions.
54 EvalFilePart
_ANSI_ARGS_((Tcl_Interp
*interp
,
60 MakeAbsFile
_ANSI_ARGS_((Tcl_Interp
*interp
,
66 GenerateFileId
_ANSI_ARGS_((Tcl_Interp
*interp
,
71 SetTCLENVFileIdEntry
_ANSI_ARGS_((Tcl_Interp
*interp
,
76 CheckTCLENVFileIdEntry
_ANSI_ARGS_((Tcl_Interp
*interp
,
80 GetTCLENVFileIdEntry
_ANSI_ARGS_((Tcl_Interp
*interp
,
84 SetTCLENVPkgEntry
_ANSI_ARGS_((Tcl_Interp
*interp
,
91 GetTCLENVPkgEntry
_ANSI_ARGS_((Tcl_Interp
*interp
,
95 unsigned *lengthPtr
));
98 SetTCLENVProcEntry
_ANSI_ARGS_((Tcl_Interp
*interp
,
104 GetTCLENVProcEntry
_ANSI_ARGS_((Tcl_Interp
*interp
,
107 char **locationPtr
));
110 ProcessIndexFile
_ANSI_ARGS_((Tcl_Interp
*interp
,
112 char *tndxFilePath
));
115 BuildPackageIndex
_ANSI_ARGS_((Tcl_Interp
*interp
,
116 char *tlibFilePath
));
119 LoadPackageIndex
_ANSI_ARGS_((Tcl_Interp
*interp
,
125 LoadOusterIndex
_ANSI_ARGS_((Tcl_Interp
*interp
,
130 LoadDirIndexes
_ANSI_ARGS_((Tcl_Interp
*interp
,
134 LoadPackageIndexes
_ANSI_ARGS_((Tcl_Interp
*interp
,
138 LoadProc
_ANSI_ARGS_((Tcl_Interp
*interp
,
144 *-----------------------------------------------------------------------------
148 * Read in a byte range of a file and evaulate it.
151 * o interp (I) - A pointer to the interpreter, error returned in result.
152 * o fileName (I) - The file to evaulate.
153 * o offset (I) - Byte offset into the file of the area to evaluate
154 * o length (I) - Number of bytes to evaulate..
157 * A standard Tcl result.
158 *-----------------------------------------------------------------------------
161 EvalFilePart (Tcl_Interp
*interp
, char *fileName
, long offset
, unsigned length
)
163 Interp
*iPtr
= (Interp
*) interp
;
166 char *oldScriptFile
, *cmdBuffer
;
169 if (fileName
[0] == '~')
170 if ((fileName
= Tcl_TildeSubst (interp
, fileName
)) == NULL
)
173 fileNum
= open (fileName
, O_RDONLY
, 0);
175 Tcl_AppendResult (interp
, "open failed on: ", fileName
, ": ",
176 Tcl_UnixError (interp
), (char *) NULL
);
179 if (fstat (fileNum
, &statBuf
) == -1)
182 if ((statBuf
.st_size
< offset
+ length
) || (offset
< 0)) {
183 Tcl_AppendResult (interp
, "range to eval outside of file bounds \"",
184 fileName
, "\"", (char *) NULL
);
188 if (lseek (fileNum
, offset
, 0) < 0)
191 cmdBuffer
= ckalloc (length
+ 1);
192 if (read (fileNum
, cmdBuffer
, length
) != length
)
195 cmdBuffer
[length
] = '\0';
197 if (close (fileNum
) != 0)
200 oldScriptFile
= iPtr
->scriptFile
;
201 iPtr
->scriptFile
= fileName
;
203 result
= Tcl_Eval (interp
, cmdBuffer
, 0, (char **) NULL
);
205 iPtr
->scriptFile
= oldScriptFile
;
208 if (result
!= TCL_ERROR
)
212 * An error occured. Record information telling where it came from.
216 sprintf (buf
, "\n (file \"%.50s\" line %d)", fileName
,
218 Tcl_AddErrorInfo(interp
, buf
);
223 * Errors accessing the file once its opened are handled here.
226 Tcl_AppendResult (interp
, "error accessing: ", fileName
, ": ",
227 Tcl_UnixError (interp
), (char *) NULL
);
234 *-----------------------------------------------------------------------------
238 * Convert a file name to an absolute path. This handles tilde substitution
239 * and preappend the current directory name if the path is relative.
242 * o interp (I) - A pointer to the interpreter, error returned in result.
243 * o fileName (I) - File name (should not start with a "/").
244 * o buffer (O) - Buffer to store string in, if it will fit.
245 * o bufferSize (I) - Size of buffer.
247 * A pointer to the file name. If the string would fit in buffer, then
248 * a pointer to buffer is returned, otherwise a dynamicaly allocated file
249 * name. NULL is returned if an error occured.
250 *-----------------------------------------------------------------------------
253 MakeAbsFile (Tcl_Interp
*interp
, char *fileName
, char *buffer
, int bufferSize
)
255 char curDir
[MAXPATHLEN
+1];
259 if (fileName
[0] == '~') {
260 fileName
= Tcl_TildeSubst (interp
, fileName
);
261 if (fileName
== NULL
)
263 pathLen
= strlen (fileName
);
264 if (pathLen
< bufferSize
)
267 pathName
= ckalloc (pathLen
+ 1);
268 strcpy (pathName
, fileName
);
273 if (getwd (curDir
) == NULL
) {
274 Tcl_AppendResult (interp
, "error getting working directory name: ",
275 curDir
, (char *) NULL
);
278 if (getcwd (curDir
, MAXPATHLEN
) == 0) {
279 Tcl_AppendResult (interp
, "error getting working directory name: ",
280 Tcl_UnixError (interp
), (char *) NULL
);
283 pathLen
= strlen (curDir
) + strlen (fileName
) + 1; /* For `/' */
284 if (pathLen
< bufferSize
)
287 pathName
= ckalloc (pathLen
+ 1);
288 strcpy (pathName
, curDir
);
289 strcat (pathName
, "/");
290 strcat (pathName
, fileName
);
296 *-----------------------------------------------------------------------------
300 * Given a path to a file, generate its file Id, in the form:
305 * o interp (I) - A pointer to the interpreter, error returned in result.
306 * o filepath (I) - Absolute path to the file.
307 * o fileId (O) - File id is returned here.
309 * TCL_OK or TCL_ERROR.
310 *-----------------------------------------------------------------------------
313 GenerateFileId (Tcl_Interp
*interp
, char *filePath
, fileId_t fileId
)
315 struct stat statInfo
;
317 if (stat (filePath
, &statInfo
) < 0) {
318 Tcl_AppendResult (interp
, "stat of \"", filePath
, "\" failed: ",
319 Tcl_UnixError (interp
), (char *) NULL
);
323 sprintf (fileId
, "@%d:%d", statInfo
.st_dev
, statInfo
.st_ino
);
329 *-----------------------------------------------------------------------------
331 * SetTCLENVFileIdEntry --
333 * Set a file entry in the TCLENV array for a file path in the form:
335 * TCLENV(@dev:inode) filepath
337 * This entry translates a dev:info into a full file path.
340 * o interp (I) - A pointer to the interpreter, error returned in result.
341 * o fileId (I) - The file Id for the file.
342 * o filepath (I) - Absolute path to the file.
344 * TCL_OK or TCL_ERROR.
345 *-----------------------------------------------------------------------------
348 SetTCLENVFileIdEntry (Tcl_Interp
*interp
, fileId_t fileId
, char *filePath
)
351 if (Tcl_SetVar2 (interp
, "TCLENV", fileId
, filePath
,
352 TCL_GLOBAL_ONLY
| TCL_LEAVE_ERR_MSG
) == NULL
)
359 *-----------------------------------------------------------------------------
361 * CheckTCLENVFileIdEntry --
363 * Check if there is a file entry in for the specified file.
366 * o interp (I) - A pointer to the interpreter.
367 * o filePath (I) - Absolute path to the library file.
369 * TRUE is returned if the entry exists, FALSE if it doesn't.
370 *-----------------------------------------------------------------------------
373 CheckTCLENVFileIdEntry (Tcl_Interp
*interp
, char *filePath
)
378 * If we can't generate the Id (stat failed), then just say it doesn't
379 * exists, other, complain later when an attempt is made to process it.
381 if (GenerateFileId (interp
, filePath
, fileId
) != TCL_OK
) {
382 Tcl_ResetResult (interp
);
386 if (Tcl_GetVar2 (interp
, "TCLENV", fileId
, TCL_GLOBAL_ONLY
) == NULL
)
393 *-----------------------------------------------------------------------------
395 * GetTCLENVFileIdEntry --
397 * Translate a file id into a file path.
400 * o interp (I) - A pointer to the interpreter.
401 * o fileId (I) - The file identifier, in the form: "@$dev:$inode"
403 * A pointer to the absolute path to the library file is returned
404 * here. This pointer remains valid until the TCLENV entry is changed,
406 *-----------------------------------------------------------------------------
409 GetTCLENVFileIdEntry (Tcl_Interp
*interp
, fileId_t fileId
)
413 filePath
= Tcl_GetVar2 (interp
, "TCLENV", fileId
, TCL_GLOBAL_ONLY
);
414 if (filePath
== NULL
) {
415 Tcl_AppendResult (interp
, "TCLENV file id entry not found for: \"",
416 fileId
, "\"", (char *) NULL
);
424 *-----------------------------------------------------------------------------
426 * SetTCLENVPkgEntry --
428 * Set the package entry in the TCLENV array for a package in the form:
430 * TCLENV(PKG:$packageName) [list $fileId $offset $length]
432 * Duplicate package names are rejected.
435 * o interp (I) - A pointer to the interpreter, error returned in result.
436 * o packageName (I) - Package name.
437 * o fileId (I) - File id for the file.
438 * o offset (I) - String containing the numeric start of the package.
439 * o length (I) - Strign containing the numeric length of the package.
441 * TCL_OK,r TCL_ERROR of TCL_CONTINUE if the package name is already defined
442 * and should be skipped.
443 *-----------------------------------------------------------------------------
446 SetTCLENVPkgEntry (Tcl_Interp
*interp
, char *packageName
, fileId_t fileId
, char *offset
, char *length
)
449 char indexBuffer
[64], *indexPtr
;
450 char *pkgDataArgv
[3], *dataStr
, *setResult
;
452 nameLen
= strlen (packageName
) + 5; /* includes "PKG:" and '\0' */
453 if (nameLen
<= sizeof (indexBuffer
))
454 indexPtr
= indexBuffer
;
456 indexPtr
= ckalloc (nameLen
);
458 strcpy (indexPtr
, "PKG:");
459 strcpy (indexPtr
+ 4, packageName
);
462 * Check for duplicate package name.
464 if (Tcl_GetVar2 (interp
, "TCLENV", indexPtr
, TCL_GLOBAL_ONLY
) != NULL
) {
465 if (indexPtr
!= indexBuffer
)
470 pkgDataArgv
[0] = fileId
;
471 pkgDataArgv
[1] = offset
;
472 pkgDataArgv
[2] = length
;
473 dataStr
= Tcl_Merge (3, pkgDataArgv
);
475 setResult
= Tcl_SetVar2 (interp
, "TCLENV", indexPtr
, dataStr
,
476 TCL_GLOBAL_ONLY
| TCL_LEAVE_ERR_MSG
);
478 if (indexPtr
!= indexBuffer
)
481 return (setResult
== NULL
) ? TCL_ERROR
: TCL_OK
;
485 *-----------------------------------------------------------------------------
487 * GetTCLENVPkgEntry --
489 * Get the package entry in the TCLENV array for a package.
492 * o interp (I) - A pointer to the interpreter, error returned in result.
493 * o packageName (I) - Package name to find.
494 * o fileId (O) - The fileId for the library file is returned here.
495 * o offsetPtr (O) - Start of the package in the library.
496 * o lengthPtr (O) - Length of the package in the library.
498 * TCL_OK or TCL_ERROR.
499 *-----------------------------------------------------------------------------
502 GetTCLENVPkgEntry (Tcl_Interp
*interp
, char *packageName
, fileId_t fileId
, long *offsetPtr
, unsigned *lengthPtr
)
504 int nameLen
, pkgDataArgc
;
505 char indexBuffer
[64], *indexPtr
;
506 char *dataStr
, **pkgDataArgv
= NULL
;
507 register char *srcPtr
, *destPtr
;
509 nameLen
= strlen (packageName
) + 5; /* includes "PKG:" and '\0' */
510 if (nameLen
<= sizeof (indexBuffer
))
511 indexPtr
= indexBuffer
;
513 indexPtr
= ckalloc (nameLen
);
515 strcpy (indexPtr
, "PKG:");
516 strcpy (indexPtr
+ 4, packageName
);
518 dataStr
= Tcl_GetVar2 (interp
, "TCLENV", indexPtr
, TCL_GLOBAL_ONLY
);
519 if (dataStr
== NULL
) {
520 Tcl_AppendResult (interp
, "entry not found in TCLENV for package \"",
521 packageName
, "\"", (char *) NULL
);
522 if (indexPtr
!= indexBuffer
)
528 * Extract the data from the array entry.
531 if (Tcl_SplitList (interp
, dataStr
, &pkgDataArgc
,
532 &pkgDataArgv
) != TCL_OK
)
534 if (pkgDataArgc
!= 3)
536 if (strlen (pkgDataArgv
[0]) >= sizeof (fileId_t
))
538 strcpy (fileId
, pkgDataArgv
[0]);
539 if (!Tcl_StrToLong (pkgDataArgv
[1], 0, offsetPtr
))
541 if (!Tcl_StrToUnsigned (pkgDataArgv
[2], 0, lengthPtr
))
544 ckfree (pkgDataArgv
);
545 if (indexPtr
!= indexBuffer
)
550 * Exit point when an invalid entry is found.
553 if (pkgDataArgv
!= NULL
)
554 ckfree (pkgDataArgv
);
555 Tcl_ResetResult (interp
);
556 Tcl_AppendResult (interp
, "invalid entry for package library: TCLENV(",
557 indexPtr
,") is \"", dataStr
, "\"", (char *) NULL
);
558 if (indexPtr
!= indexBuffer
)
564 *-----------------------------------------------------------------------------
566 * SetTCLENVProcEntry --
568 * Set the proc entry in the TCLENV array for a package in the form:
570 * TCLENV(PROC:$proc) [list P $packageName]
572 * TCLENV(PROC:$proc) [list F $fileId]
575 * o interp (I) - A pointer to the interpreter, error returned in result.
576 * o procName (I) - The Tcl proc name.
577 * o type (I) - "P" for a package entry or "F" for a file entry.
578 * o location (I) - Either the package name or file name containing the
581 * TCL_OK or TCL_ERROR.
582 *-----------------------------------------------------------------------------
585 SetTCLENVProcEntry (Tcl_Interp
*interp
, char *procName
, char *type
, char *location
)
588 char indexBuffer
[64], *indexPtr
;
589 char *procDataArgv
[2], *dataStr
, *setResult
;
591 nameLen
= strlen (procName
) + 6; /* includes "PROC:" and '\0' */
592 if (nameLen
<= sizeof (indexBuffer
))
593 indexPtr
= indexBuffer
;
595 indexPtr
= ckalloc (nameLen
);
597 strcpy (indexPtr
, "PROC:");
598 strcpy (indexPtr
+ 5, procName
);
600 procDataArgv
[0] = type
;
601 procDataArgv
[1] = location
;
602 dataStr
= Tcl_Merge (2, procDataArgv
);
604 setResult
= Tcl_SetVar2 (interp
, "TCLENV", indexPtr
, dataStr
,
605 TCL_GLOBAL_ONLY
| TCL_LEAVE_ERR_MSG
);
607 if (indexPtr
!= indexBuffer
)
610 return (setResult
== NULL
) ? TCL_ERROR
: TCL_OK
;
614 *-----------------------------------------------------------------------------
616 * GetTCLENVProcEntry --
618 * Get the proc entry in the TCLENV array for a package.
621 * o interp (I) - A pointer to the interpreter, error returned in result.
622 * o procName (I) - The Tcl proc name.
623 * o typePtr (O) - 'P' for a package entry or 'F' for a file entry. This
624 * is a single character result.
625 * o location (O) - Either the package name or the file name. It is
626 * dynamically allocated and must be freed when finished. NULL is
627 * return if the procedure is not found.
629 * TCL_OK or TCL_ERROR.
630 *-----------------------------------------------------------------------------
633 GetTCLENVProcEntry (Tcl_Interp
*interp
, char *procName
, char *typePtr
, char **locationPtr
)
635 int nameLen
, procDataArgc
;
636 char indexBuffer
[64], *indexPtr
;
637 char *dataStr
, *setResult
, **procDataArgv
;
638 register char *srcPtr
, *destPtr
;
640 nameLen
= strlen (procName
) + 6; /* includes "PROC:" and '\0' */
641 if (nameLen
<= sizeof (indexBuffer
))
642 indexPtr
= indexBuffer
;
644 indexPtr
= ckalloc (nameLen
);
646 strcpy (indexPtr
, "PROC:");
647 strcpy (indexPtr
+ 5, procName
);
649 dataStr
= Tcl_GetVar2 (interp
, "TCLENV", indexPtr
, TCL_GLOBAL_ONLY
);
650 if (dataStr
== NULL
) {
651 if (indexPtr
!= indexBuffer
)
658 * Extract the data from the array entry.
661 if (Tcl_SplitList (interp
, dataStr
, &procDataArgc
,
662 &procDataArgv
) != TCL_OK
)
664 if ((procDataArgc
!= 2) || (procDataArgv
[0][1] != '\0'))
666 if (!((procDataArgv
[0][0] == 'F') || (procDataArgv
[0][0] == 'P')))
668 *typePtr
= procDataArgv
[0][0];
671 * Now do a nasty trick to save a malloc. Since procDataArgv contains
672 * the string, just move the string to the top and type cast.
674 destPtr
= (char *) procDataArgv
;
675 srcPtr
= procDataArgv
[1];
676 while (*srcPtr
!= '\0')
677 *(destPtr
++) = *(srcPtr
++);
679 *locationPtr
= (char *) procDataArgv
;
681 if (indexPtr
!= indexBuffer
)
686 * Exit point when an invalid entry is found.
689 if (procDataArgv
!= NULL
)
690 ckfree (procDataArgv
);
691 Tcl_ResetResult (interp
);
692 Tcl_AppendResult (interp
, "invalid entry for procedure: TCLENV(",
693 indexPtr
,") is \"", dataStr
, "\"", (char *) NULL
);
694 if (indexPtr
!= indexBuffer
)
700 *-----------------------------------------------------------------------------
702 * ProcessIndexFile --
704 * Open and process a package library index file (.tndx). Creates an
707 * TCLENV(PKG:$packageName) [list $fileId $start $len]
709 * for each package and a entry in the from
711 * TCLENV(PROC:$proc) [list P $packageName]
713 * for each entry procedure in a package. If the package is already defined,
717 * o interp (I) - A pointer to the interpreter, error returned in result.
718 * o tlibFilePath (I) - Absolute path name to the library file.
719 * o tndxFilePath (I) - Absolute path name to the library file index.
721 * TCL_OK or TCL_ERROR.
722 *-----------------------------------------------------------------------------
725 ProcessIndexFile (Tcl_Interp
*interp
, char *tlibFilePath
, char *tndxFilePath
)
729 dynamicBuf_t lineBuffer
;
730 int lineArgc
, idx
, result
;
731 char **lineArgv
= NULL
;
733 if (GenerateFileId (interp
, tlibFilePath
, fileId
) != TCL_OK
)
736 indexFilePtr
= fopen (tndxFilePath
, "r");
737 if (indexFilePtr
== NULL
) {
738 Tcl_AppendResult (interp
, "open failed on: ", tndxFilePath
, ": ",
739 Tcl_UnixError (interp
), (char *) NULL
);
743 Tcl_DynBufInit (&lineBuffer
);
746 switch (Tcl_DynamicFgets (&lineBuffer
, indexFilePtr
, FALSE
)) {
750 Tcl_AppendResult (interp
, Tcl_UnixError (interp
), (char *) NULL
);
753 if ((Tcl_SplitList (interp
, lineBuffer
.ptr
, &lineArgc
,
754 &lineArgv
) != TCL_OK
) || (lineArgc
< 4))
758 * lineArgv [0] is the package name.
759 * lineArgv [1] is the package offset in the library.
760 * lineArgv [2] is the package length in the library.
761 * lineArgv [3-n] are the entry procedures for the package.
763 result
= SetTCLENVPkgEntry (interp
, lineArgv
[0], fileId
, lineArgv
[1],
765 if (result
== TCL_ERROR
)
769 * If the package is not duplicated, add the procedures.
771 if (result
!= TCL_CONTINUE
) {
772 for (idx
= 3; idx
< lineArgc
; idx
++) {
773 if (SetTCLENVProcEntry (interp
, lineArgv
[idx
], "P",
774 lineArgv
[0]) != TCL_OK
)
783 fclose (indexFilePtr
);
784 Tcl_DynBufFree (&lineBuffer
);
786 if (SetTCLENVFileIdEntry (interp
, fileId
, tlibFilePath
) != TCL_OK
)
792 * Handle format error in library input line.
795 Tcl_ResetResult (interp
);
796 Tcl_AppendResult (interp
, "format error in library index \"",
797 tndxFilePath
, "\" (", lineBuffer
.ptr
, ")",
802 * Error exit here, releasing resources and closing the file.
805 if (lineArgv
!= NULL
)
807 Tcl_DynBufFree (&lineBuffer
);
808 fclose (indexFilePtr
);
813 *-----------------------------------------------------------------------------
815 * BuildPackageIndex --
817 * Call the "buildpackageindex" Tcl procedure to rebuild a package index.
818 * If the procedure has not been loaded, then load it. It MUST have an
819 * proc record setup by autoload.
822 * o interp (I) - A pointer to the interpreter, error returned in result.
823 * o tlibFilePath (I) - Absolute path name to the library file.
825 * TCL_OK or TCL_ERROR.
826 *-----------------------------------------------------------------------------
829 BuildPackageIndex (Tcl_Interp
*interp
, char *tlibFilePath
)
831 char *cmdPtr
, *initCmd
;
834 * Load buildpackageindex if it is not loaded
836 if (TclFindProc ((Interp
*) interp
, "buildpackageindex") == NULL
) {
838 cmdPtr
= "demand_load buildpackageindex";
840 if (Tcl_Eval (interp
, cmdPtr
, 0, (char **) NULL
) != TCL_OK
)
843 if (!STREQU (interp
->result
, "1")) {
844 Tcl_ResetResult (interp
);
846 "can not find \"buildpackageindex\" on \"TCLPATH\"";
849 Tcl_ResetResult (interp
);
853 * Build the package index.
855 initCmd
= "buildpackageindex ";
857 cmdPtr
= ckalloc (strlen (initCmd
) + strlen (tlibFilePath
) + 1);
858 strcpy (cmdPtr
, initCmd
);
859 strcat (cmdPtr
, tlibFilePath
);
861 if (Tcl_Eval (interp
, cmdPtr
, 0, (char **) NULL
) != TCL_OK
) {
866 Tcl_ResetResult (interp
);
871 *-----------------------------------------------------------------------------
873 * LoadPackageIndex --
875 * Load a package .tndx file. Rebuild .tlib if non-existant or out of
876 * date. An entry is made in the TCLENV array indicating that this file
880 * o interp (I) - A pointer to the interpreter, error returned in result.
881 * o tlibFilePath (I) - Absolute path name to the library file.
882 * o pathLen (I) - Length of tlibFilePath.
883 * o dirLen (I) - The length of the leading directory path in the name.
885 * TCL_OK or TCL_ERROR.
886 *-----------------------------------------------------------------------------
889 LoadPackageIndex (Tcl_Interp
*interp
, char *tlibFilePath
, int pathLen
, int dirLen
)
891 char *tndxFilePath
, tndxPathBuf
[64], *msg
;
892 struct stat tlibStat
;
893 struct stat tndxStat
;
895 if (pathLen
< sizeof (tndxPathBuf
))
896 tndxFilePath
= tndxPathBuf
;
898 tndxFilePath
= ckalloc (pathLen
+ 1);
899 strcpy (tndxFilePath
, tlibFilePath
);
900 tndxFilePath
[pathLen
- 3] = 'n';
901 tndxFilePath
[pathLen
- 2] = 'd';
902 tndxFilePath
[pathLen
- 1] = 'x';
905 * Get library's modification time. If the file can't be accessed, set
906 * time so the library does not get built. Other code will report the
909 if (stat (tlibFilePath
, &tlibStat
) < 0)
910 tlibStat
.st_mtime
= MAXINT
;
913 * Get the time for the index. If the file does not exists or is
914 * out of date, rebuild it.
917 if ((stat (tndxFilePath
, &tndxStat
) < 0) ||
918 (tndxStat
.st_mtime
< tlibStat
.st_mtime
)) {
919 if (BuildPackageIndex (interp
, tlibFilePath
) != TCL_OK
)
923 if (ProcessIndexFile (interp
, tlibFilePath
, tndxFilePath
) != TCL_OK
)
925 if (tndxFilePath
!= tndxPathBuf
)
926 ckfree (tndxFilePath
);
930 if (tndxFilePath
!= tndxPathBuf
)
931 ckfree (tndxFilePath
);
932 msg
= ckalloc (strlen (tlibFilePath
) + 60);
933 strcpy (msg
, "\n while loading Tcl package library index \"");
934 strcat (msg
, tlibFilePath
);
936 Tcl_AddErrorInfo (interp
, msg
);
942 *-----------------------------------------------------------------------------
946 * Load a standard Tcl index (tclIndex). An entry is made in the TCLENV
947 * array indicating that this file has been loaded.
950 * o interp (I) - A pointer to the interpreter, error returned in result.
951 * o indexFilePath (I) - Absolute path name to the tclIndex file.
952 * o dirLen (I) - The length of the directory component of indexFilePath.
954 * TCL_OK or TCL_ERROR.
955 *-----------------------------------------------------------------------------
958 LoadOusterIndex (Tcl_Interp
*interp
, char *indexFilePath
, int dirLen
)
962 dynamicBuf_t lineBuffer
;
963 int lineArgc
, result
, filePathLen
;
964 char **lineArgv
= NULL
, *filePath
, filePathBuf
[64], *msg
;
966 indexFilePtr
= fopen (indexFilePath
, "r");
967 if (indexFilePtr
== NULL
) {
968 Tcl_AppendResult (interp
, "open failed on: ", indexFilePath
, ": ",
969 Tcl_UnixError (interp
), (char *) NULL
);
973 Tcl_DynBufInit (&lineBuffer
);
976 switch (Tcl_DynamicFgets (&lineBuffer
, indexFilePtr
, FALSE
)) {
980 Tcl_AppendResult (interp
, "read filed on: ", indexFilePath
, ": ",
981 Tcl_UnixError (interp
), (char *) NULL
);
984 if ((lineBuffer
.ptr
[0] == '\0') || (lineBuffer
.ptr
[0] == '#'))
987 if (Tcl_SplitList (interp
, lineBuffer
.ptr
, &lineArgc
,
988 &lineArgv
) != TCL_OK
)
990 if (! ((lineArgc
== 0) || (lineArgc
== 2)))
994 filePathLen
= strlen (lineArgv
[1]) + dirLen
+ 1;
995 if (filePathLen
< sizeof (filePathBuf
))
996 filePath
= filePathBuf
;
998 filePath
= ckalloc (filePathLen
+ 1);
999 strncpy (filePath
, indexFilePath
, dirLen
+ 1);
1000 strcpy (filePath
+ dirLen
+ 1, lineArgv
[1]);
1002 result
= SetTCLENVProcEntry (interp
, lineArgv
[0], "F", filePath
);
1004 if (filePath
!= filePathBuf
)
1006 if (result
!= TCL_OK
)
1014 Tcl_DynBufFree (&lineBuffer
);
1015 fclose (indexFilePtr
);
1017 if (GenerateFileId (interp
, indexFilePath
, fileId
) != TCL_OK
)
1019 if (SetTCLENVFileIdEntry (interp
, fileId
, indexFilePath
) != TCL_OK
)
1025 * Handle format error in library input line. If data is already in the
1026 * result, its assumed to be the error that brought us here.
1029 if (interp
->result
[0] != '\0')
1030 Tcl_AppendResult (interp
, "\n", (char *) NULL
);
1031 Tcl_AppendResult (interp
, "format error in library index \"",
1032 indexFilePath
, "\" (", lineBuffer
.ptr
, ")",
1036 * Error exit here, releasing resources and closing the file.
1039 if (lineArgv
!= NULL
)
1041 Tcl_DynBufFree (&lineBuffer
);
1042 fclose (indexFilePtr
);
1044 msg
= ckalloc (strlen (indexFilePath
) + 45);
1045 strcpy (msg
, "\n while loading Tcl procedure index \"");
1046 strcat (msg
, indexFilePath
);
1048 Tcl_AddErrorInfo (interp
, msg
);
1054 *-----------------------------------------------------------------------------
1058 * Load the indexes for all package library (.tlib) or a Ousterhout
1059 * "tclIndex" file in a directory. Nonexistent or unreadable directories
1063 * o interp (I) - A pointer to the interpreter, error returned in result.
1064 * o dirName (I) - The absolute path name of the directory to search for
1067 * A standard Tcl result.
1068 *-----------------------------------------------------------------------------
1071 LoadDirIndexes (Tcl_Interp
*interp
, char *dirName
)
1074 struct dirent
*entryPtr
;
1075 int dirLen
, nameLen
;
1076 char *filePath
= NULL
;
1077 int filePathSize
= 0;
1079 dirLen
= strlen (dirName
);
1081 dirPtr
= opendir (dirName
);
1083 return TCL_OK
; /* Skip directory */
1086 entryPtr
= readdir (dirPtr
);
1087 if (entryPtr
== NULL
)
1089 nameLen
= strlen (entryPtr
->d_name
);
1091 if ((nameLen
> 5) &&
1092 ((STREQU (entryPtr
->d_name
+ nameLen
- 5, ".tlib")) ||
1093 (STREQU (entryPtr
->d_name
, "tclIndex")))) {
1096 * Expand the filePath buffer if necessary (always allow extra).
1098 if ((nameLen
+ dirLen
+ 2) > filePathSize
) {
1099 if (filePath
!= NULL
)
1101 filePathSize
= nameLen
+ dirLen
+ 2 + 16;
1102 filePath
= ckalloc (filePathSize
);
1103 strcpy (filePath
, dirName
);
1104 filePath
[dirLen
] = '/';
1106 strcpy (filePath
+ dirLen
+ 1, entryPtr
->d_name
);
1109 * Skip index if it has been loaded before or if it can't be
1112 if (CheckTCLENVFileIdEntry (interp
, filePath
) ||
1113 (access (filePath
, R_OK
) < 0))
1116 if (entryPtr
->d_name
[nameLen
- 5] == '.') {
1117 if (LoadPackageIndex (interp
, filePath
, dirLen
+ nameLen
+ 1,
1121 if (LoadOusterIndex (interp
, filePath
, dirLen
) != TCL_OK
)
1127 if (filePath
!= NULL
)
1133 if (filePath
!= NULL
)
1141 *-----------------------------------------------------------------------------
1143 * LoadPackageIndexes --
1145 * Loads the all indexes for all package libraries (.tlib)* or a
1146 * Ousterhout "tclIndex" files found in all directories in the path.
1147 * If an index has already been loaded, it will not be reloaded.
1148 * Non-existent or unreadable directories are skipped.
1151 * A standard Tcl result. Tcl array variable TCLENV is updated to
1152 * indicate the procedures that were defined in the library.
1154 *-----------------------------------------------------------------------------
1157 LoadPackageIndexes (Tcl_Interp
*interp
, char *path
)
1159 char *dirName
, dirNameBuf
[64];
1160 int idx
, dirLen
, pathArgc
, status
;
1163 if (Tcl_SplitList (interp
, path
, &pathArgc
, &pathArgv
) != TCL_OK
)
1166 for (idx
= 0; idx
< pathArgc
; idx
++) {
1168 * Get the absolute dir name. if the conversion fails (most likely
1169 * invalid "~") or thje directory cann't be read, skip it.
1171 dirName
= pathArgv
[idx
];
1172 if (dirName
[0] != '/') {
1173 dirName
= MakeAbsFile (interp
, dirName
, dirNameBuf
,
1174 sizeof (dirNameBuf
));
1175 if (dirName
== NULL
)
1178 if (access (dirName
, X_OK
) == 0)
1179 status
= LoadDirIndexes (interp
, dirName
);
1183 if ((dirName
!= pathArgv
[idx
]) && (dirName
!= dirNameBuf
))
1185 if (status
!= TCL_OK
)
1198 *-----------------------------------------------------------------------------
1202 * Attempt to load a procedure (or command) by checking the TCLENV
1203 * array for its location (either in a file or package library).
1206 * o interp (I) - A pointer to the interpreter, error returned in result.
1207 * o procName (I) - The name of the procedure (or command) to load
1209 * o foundPtr (O) - TRUE is returned if the procedure or command was
1210 * loaded, FALSE if it was not.
1212 * A standard Tcl result.
1214 *-----------------------------------------------------------------------------
1217 LoadProc (Tcl_Interp
*interp
, char *procName
, int *foundPtr
)
1219 Interp
*iPtr
= (Interp
*) interp
;
1220 char type
, *location
, *filePath
, *cmdPtr
, cmdBuf
[80];
1225 Tcl_HashEntry
*cmdEntryPtr
;
1227 if (GetTCLENVProcEntry (interp
, procName
, &type
, &location
) != TCL_OK
)
1229 if (location
== NULL
) {
1235 * If this is a file entry (type = 'F'), location is a file name or
1236 * absolute file path. If it's an absolute path, just eval it, otherwise
1237 * load the source using the "load" procdure (still in Tcl). If this is a
1238 * package entry, location is a package name. Source part of the package
1239 * library (Must look up the file, offset and length in the package entry
1243 if (location
[0] == '/') {
1244 result
= Tcl_EvalFile (interp
, location
);
1246 cmdLen
= strlen (location
) + 5;
1247 if (cmdLen
< sizeof (cmdBuf
))
1250 cmdPtr
= ckalloc (cmdLen
+ 1);
1251 strcpy (cmdPtr
, "load ");
1252 strcat (cmdPtr
, location
);
1254 result
= Tcl_Eval (interp
, cmdPtr
, 0, NULL
);
1255 if (cmdPtr
!= cmdBuf
)
1259 result
= GetTCLENVPkgEntry (interp
, location
, fileId
, &offset
,
1261 if (result
== TCL_OK
) {
1262 filePath
= GetTCLENVFileIdEntry (interp
, fileId
);
1263 if (filePath
== NULL
)
1267 if (result
== TCL_OK
)
1268 result
= EvalFilePart (interp
, filePath
, offset
, length
);
1275 * If we are ok to this point, make sure that the procedure or command is
1278 if (result
== TCL_OK
) {
1279 cmdEntryPtr
= Tcl_FindHashEntry (&iPtr
->commandTable
, procName
);
1280 *foundPtr
= (cmdEntryPtr
!= NULL
);
1287 *-----------------------------------------------------------------------------
1289 * Tcl_LoadlibindexCmd --
1291 * This procedure is invoked to process the "Loadlibindex" Tcl command:
1293 * loadlibindex libfile
1295 * which loads the index for a package library (.tlib) or a Ousterhout
1299 * A standard Tcl result. Tcl array variable TCLENV is updated to
1300 * indicate the procedures that were defined in the library.
1302 *-----------------------------------------------------------------------------
1305 Tcl_LoadlibindexCmd (
1312 char *pathName
, pathNameBuf
[64];
1313 int pathLen
, dirLen
;
1316 Tcl_AppendResult (interp
, tclXWrongArgs
, argv
[0], " libFile",
1321 pathName
= argv
[1];
1322 if (pathName
[0] != '/') {
1323 pathName
= MakeAbsFile (interp
, pathName
, pathNameBuf
,
1324 sizeof (pathNameBuf
));
1325 if (pathName
== NULL
)
1330 * Find the length of the directory name. Validate that we have a .tlib
1331 * extension or file name is "tclIndex" and call the routine to process
1332 * the specific type of index.
1334 pathLen
= strlen (pathName
);
1335 for (dirLen
= pathLen
- 1; pathName
[dirLen
] != '/'; dirLen
--)
1338 if ((pathLen
> 5) && (pathName
[pathLen
- 5] == '.')) {
1339 if (!STREQU (pathName
+ pathLen
- 5, ".tlib"))
1341 if (LoadPackageIndex (interp
, pathName
, pathLen
, dirLen
) != TCL_OK
)
1344 if (!STREQU (pathName
+ dirLen
, "/tclIndex"))
1346 if (LoadOusterIndex (interp
, pathName
, dirLen
) != TCL_OK
)
1349 if ((pathName
!= argv
[1]) && (pathName
!= pathNameBuf
))
1354 Tcl_AppendResult (interp
, "invalid library name, must have an extension ",
1355 "of \".tlib\" or the name \"tclIndex\", got \"",
1356 argv
[1], "\"", (char *) NULL
);
1359 if ((pathName
!= argv
[1]) && (pathName
!= pathNameBuf
))
1365 *-----------------------------------------------------------------------------
1367 * Tcl_Demand_loadCmd --
1369 * This procedure is invoked to process the "demand_load" Tcl command:
1373 * which searchs the TCLENV tables for the specified procedure. If it
1374 * is not found, an attempt is made to load unloaded libraries, first
1375 * the variable "TCLPATH" is searched. If the procedure is not defined
1376 * after that, then "auto_path" is searched.
1379 * A standard Tcl result.
1381 *-----------------------------------------------------------------------------
1384 Tcl_Demand_loadCmd (
1395 Tcl_AppendResult (interp
, tclXWrongArgs
, argv
[0], " procedure",
1400 if (LoadProc (interp
, argv
[1], &found
) != TCL_OK
)
1403 interp
->result
= "1";
1408 * Slow path, load the libraries indices on "TCLPATH".
1410 path
= Tcl_GetVar (interp
, "TCLPATH", TCL_GLOBAL_ONLY
);
1412 if (LoadPackageIndexes (interp
, path
) != TCL_OK
)
1414 if (LoadProc (interp
, argv
[1], &found
) != TCL_OK
)
1417 interp
->result
= "1";
1423 * Final gasp, check the "auto_path"
1425 path
= Tcl_GetVar (interp
, "auto_path", TCL_GLOBAL_ONLY
);
1427 if (LoadPackageIndexes (interp
, path
) != TCL_OK
)
1429 if (LoadProc (interp
, argv
[1], &found
) != TCL_OK
)
1432 interp
->result
= "1";
1438 * Procedure or command was not found.
1440 interp
->result
= "0";
1444 msg
= ckalloc (strlen (argv
[1]) + 35);
1445 strcpy (msg
, "\n while demand loading \"");
1446 strcat (msg
, argv
[1]);
1448 Tcl_AddErrorInfo (interp
, msg
);