3 #include "jimautoconf.h"
4 #include <jim-subcmd.h>
12 /* All Tcl packages have a fixed, dummy version */
13 static const char *package_version_1
= "1.0";
15 /* -----------------------------------------------------------------------------
17 * ---------------------------------------------------------------------------*/
19 int Jim_PackageProvide(Jim_Interp
*interp
, const char *name
, const char *ver
, int flags
)
21 /* If the package was already provided returns an error. */
22 Jim_HashEntry
*he
= Jim_FindHashEntry(&interp
->packages
, name
);
24 /* An empty result means the automatic entry. This can be replaced */
25 if (he
&& *(const char *)he
->u
.val
) {
26 if (flags
& JIM_ERRMSG
) {
27 Jim_SetResultFormatted(interp
, "package \"%s\" was already provided", name
);
31 Jim_ReplaceHashEntry(&interp
->packages
, name
, (char *)ver
);
36 * Searches along a of paths for the given package.
38 * Returns the allocated path to the package file if found,
39 * or NULL if not found.
41 static char *JimFindPackage(Jim_Interp
*interp
, Jim_Obj
*prefixListObj
, const char *pkgName
)
44 char *buf
= Jim_Alloc(JIM_PATH_LEN
);
45 int prefixc
= Jim_ListLength(interp
, prefixListObj
);
47 for (i
= 0; i
< prefixc
; i
++) {
48 Jim_Obj
*prefixObjPtr
= Jim_ListGetIndex(interp
, prefixListObj
, i
);
49 const char *prefix
= Jim_String(prefixObjPtr
);
51 /* Loadable modules are tried first */
53 snprintf(buf
, JIM_PATH_LEN
, "%s/%s.so", prefix
, pkgName
);
54 if (access(buf
, R_OK
) == 0) {
58 if (strcmp(prefix
, ".") == 0) {
59 snprintf(buf
, JIM_PATH_LEN
, "%s.tcl", pkgName
);
62 snprintf(buf
, JIM_PATH_LEN
, "%s/%s.tcl", prefix
, pkgName
);
65 if (access(buf
, R_OK
) == 0) {
73 /* Search for a suitable package under every dir specified by JIM_LIBPATH,
74 * and load it if possible. If a suitable package was loaded with success
75 * JIM_OK is returned, otherwise JIM_ERR is returned. */
76 static int JimLoadPackage(Jim_Interp
*interp
, const char *name
, int flags
)
78 int retCode
= JIM_ERR
;
79 Jim_Obj
*libPathObjPtr
= Jim_GetGlobalVariableStr(interp
, JIM_LIBPATH
, JIM_NONE
);
83 /* Scan every directory for the the first match */
84 path
= JimFindPackage(interp
, libPathObjPtr
, name
);
88 /* Note: Even if the file fails to load, we consider the package loaded.
89 * This prevents issues with recursion.
90 * Use a dummy version of "" to signify this case.
92 Jim_PackageProvide(interp
, name
, "", 0);
94 /* Try to load/source it */
95 p
= strrchr(path
, '.');
97 if (p
&& strcmp(p
, ".tcl") == 0) {
98 Jim_IncrRefCount(libPathObjPtr
);
99 retCode
= Jim_EvalFileGlobal(interp
, path
);
100 Jim_DecrRefCount(interp
, libPathObjPtr
);
104 retCode
= Jim_LoadLibrary(interp
, path
);
107 if (retCode
!= JIM_OK
) {
108 /* Upon failure, remove the dummy entry */
109 Jim_DeleteHashEntry(&interp
->packages
, name
);
119 int Jim_PackageRequire(Jim_Interp
*interp
, const char *name
, int flags
)
123 /* Start with an empty error string */
124 Jim_SetEmptyResult(interp
);
126 he
= Jim_FindHashEntry(&interp
->packages
, name
);
128 /* Try to load the package. */
129 int retcode
= JimLoadPackage(interp
, name
, flags
);
130 if (retcode
!= JIM_OK
) {
131 if (flags
& JIM_ERRMSG
) {
132 int len
= Jim_Length(Jim_GetResult(interp
));
133 Jim_SetResultFormatted(interp
, "%#s%sCan't load package %s",
134 Jim_GetResult(interp
), len
? "\n" : "", name
);
139 /* In case the package did not 'package provide' */
140 Jim_PackageProvide(interp
, name
, package_version_1
, 0);
142 /* Now it must exist */
143 he
= Jim_FindHashEntry(&interp
->packages
, name
);
146 Jim_SetResultString(interp
, he
->u
.val
, -1);
151 *----------------------------------------------------------------------
153 * package provide name ?version?
155 * This procedure is invoked to declare that
156 * a particular package is now present in an interpreter.
157 * The package must not already be provided in the interpreter.
160 * Returns JIM_OK and sets the results to the version (defaults to "1.0")
162 *----------------------------------------------------------------------
164 static int package_cmd_provide(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
166 return Jim_PackageProvide(interp
, Jim_String(argv
[0]),
167 argc
> 1 ? Jim_String(argv
[1]) : package_version_1
, JIM_ERRMSG
);
171 *----------------------------------------------------------------------
173 * package require name ?version?
175 * This procedure is load a given package.
176 * Note that the version is ignored.
179 * Returns JIM_OK and sets the package version.
181 *----------------------------------------------------------------------
183 static int package_cmd_require(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
185 /* package require failing is important enough to add to the stack */
186 interp
->addStackTrace
++;
188 return Jim_PackageRequire(interp
, Jim_String(argv
[0]), JIM_ERRMSG
);
192 *----------------------------------------------------------------------
196 * Returns a list of known packages
199 * Returns JIM_OK and sets a list of known packages.
201 *----------------------------------------------------------------------
203 static int package_cmd_list(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
205 Jim_HashTableIterator
*htiter
;
207 Jim_Obj
*listObjPtr
= Jim_NewListObj(interp
, NULL
, 0);
209 htiter
= Jim_GetHashTableIterator(&interp
->packages
);
210 while ((he
= Jim_NextHashEntry(htiter
)) != NULL
) {
211 Jim_ListAppendElement(interp
, listObjPtr
, Jim_NewStringObj(interp
, he
->key
, -1));
213 Jim_FreeHashTableIterator(htiter
);
215 Jim_SetResult(interp
, listObjPtr
);
220 static const jim_subcmd_type package_command_table
[] = {
227 /* Description: Indicates that the current script provides the given package */
235 /* Description: Loads the given package by looking in standard places */
243 /* Description: Lists all known packages */
250 int Jim_packageInit(Jim_Interp
*interp
)
252 Jim_CreateCommand(interp
, "package", Jim_SubCmdProc
, (void *)package_command_table
, NULL
);