5 #include "jimautoconf.h"
6 #include "jim-subcmd.h"
8 /* -----------------------------------------------------------------------------
10 * ---------------------------------------------------------------------------*/
12 int Jim_PackageProvide(Jim_Interp
*interp
, const char *name
, const char *ver
, int flags
)
14 /* If the package was already provided returns an error. */
15 Jim_HashEntry
*he
= Jim_FindHashEntry(&interp
->packages
, name
);
17 /* An empty result means the automatic entry. This can be replaced */
18 if (he
&& *(const char *)he
->u
.val
) {
19 if (flags
& JIM_ERRMSG
) {
20 Jim_SetResultFormatted(interp
, "package \"%s\" was already provided", name
);
25 Jim_DeleteHashEntry(&interp
->packages
, name
);
27 Jim_AddHashEntry(&interp
->packages
, name
, (char *)ver
);
31 static char *JimFindPackage(Jim_Interp
*interp
, char **prefixes
, int prefixc
, const char *pkgName
)
34 char *buf
= Jim_Alloc(JIM_PATH_LEN
);
36 for (i
= 0; i
< prefixc
; i
++) {
37 if (prefixes
[i
] == NULL
)
40 /* Loadable modules are tried first */
42 snprintf(buf
, JIM_PATH_LEN
, "%s/%s.so", prefixes
[i
], pkgName
);
43 if (access(buf
, R_OK
) == 0) {
47 if (strcmp(prefixes
[i
], ".") == 0) {
48 snprintf(buf
, JIM_PATH_LEN
, "%s.tcl", pkgName
);
51 snprintf(buf
, JIM_PATH_LEN
, "%s/%s.tcl", prefixes
[i
], pkgName
);
54 if (access(buf
, R_OK
) == 0) {
62 /* Search for a suitable package under every dir specified by JIM_LIBPATH,
63 * and load it if possible. If a suitable package was loaded with success
64 * JIM_OK is returned, otherwise JIM_ERR is returned. */
65 static int JimLoadPackage(Jim_Interp
*interp
, const char *name
, int flags
)
67 Jim_Obj
*libPathObjPtr
;
68 char **prefixes
, *path
;
69 int prefixc
, i
, retCode
= JIM_ERR
;
71 libPathObjPtr
= Jim_GetGlobalVariableStr(interp
, JIM_LIBPATH
, JIM_NONE
);
72 if (libPathObjPtr
== NULL
) {
77 Jim_IncrRefCount(libPathObjPtr
);
78 prefixc
= Jim_ListLength(interp
, libPathObjPtr
);
81 prefixes
= Jim_Alloc(sizeof(char *) * prefixc
);
82 for (i
= 0; i
< prefixc
; i
++) {
83 Jim_Obj
*prefixObjPtr
;
85 if (Jim_ListIndex(interp
, libPathObjPtr
, i
, &prefixObjPtr
, JIM_NONE
) != JIM_OK
) {
89 prefixes
[i
] = Jim_StrDup(Jim_String(prefixObjPtr
));
92 /* Scan every directory for the the first match */
93 path
= JimFindPackage(interp
, prefixes
, prefixc
, name
);
95 char *p
= strrchr(path
, '.');
97 /* Note: Even if the file fails to load, we consider the package loaded.
98 * This prevents issues with recursion.
99 * Use a dummy version of "" to signify this case.
101 Jim_PackageProvide(interp
, name
, "", 0);
103 /* Try to load/source it */
104 if (p
&& strcmp(p
, ".tcl") == 0) {
105 retCode
= Jim_EvalFileGlobal(interp
, path
);
109 retCode
= Jim_LoadLibrary(interp
, path
);
112 if (retCode
!= JIM_OK
) {
113 /* Upon failure, remove the dummy entry */
114 Jim_DeleteHashEntry(&interp
->packages
, name
);
118 for (i
= 0; i
< prefixc
; i
++)
119 Jim_Free(prefixes
[i
]);
122 Jim_DecrRefCount(interp
, libPathObjPtr
);
126 int Jim_PackageRequire(Jim_Interp
*interp
, const char *name
, int flags
)
130 /* Start with an empty error string */
131 Jim_SetResultString(interp
, "", 0);
133 he
= Jim_FindHashEntry(&interp
->packages
, name
);
135 /* Try to load the package. */
136 int retcode
= JimLoadPackage(interp
, name
, flags
);
137 if (retcode
!= JIM_OK
) {
138 if (flags
& JIM_ERRMSG
) {
141 Jim_GetString(Jim_GetResult(interp
), &len
);
142 Jim_SetResultFormatted(interp
, "%#s%sCan't load package %s",
143 Jim_GetResult(interp
), len
? "\n" : "", name
);
148 /* In case the package did no 'package provide' */
149 Jim_PackageProvide(interp
, name
, "1.0", 0);
151 /* Now it must exist */
152 he
= Jim_FindHashEntry(&interp
->packages
, name
);
155 Jim_SetResultString(interp
, he
->u
.val
, -1);
160 *----------------------------------------------------------------------
162 * package provide name ?version?
164 * This procedure is invoked to declare that a particular version
165 * of a particular package is now present in an interpreter. There
166 * must not be any other version of this package already
167 * provided in the interpreter.
170 * Returns JIM_OK and sets the package version (or 1.0 if not specified).
172 *----------------------------------------------------------------------
174 static int package_cmd_provide(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
176 const char *version
= "1.0";
179 version
= Jim_String(argv
[1]);
181 return Jim_PackageProvide(interp
, Jim_String(argv
[0]), version
, JIM_ERRMSG
);
185 *----------------------------------------------------------------------
187 * package require name ?version?
189 * This procedure is load a given package.
190 * Note that the version is ignored.
193 * Returns JIM_OK and sets the package version.
195 *----------------------------------------------------------------------
197 static int package_cmd_require(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
199 /* package require failing is important enough to add to the stack */
200 interp
->addStackTrace
++;
202 return Jim_PackageRequire(interp
, Jim_String(argv
[0]), JIM_ERRMSG
);
206 *----------------------------------------------------------------------
210 * Returns a list of known packages
213 * Returns JIM_OK and sets a list of known packages.
215 *----------------------------------------------------------------------
217 static int package_cmd_list(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
219 Jim_HashTableIterator
*htiter
;
221 Jim_Obj
*listObjPtr
= Jim_NewListObj(interp
, NULL
, 0);
223 htiter
= Jim_GetHashTableIterator(&interp
->packages
);
224 while ((he
= Jim_NextHashEntry(htiter
)) != NULL
) {
225 Jim_ListAppendElement(interp
, listObjPtr
, Jim_NewStringObj(interp
, he
->key
, -1));
227 Jim_FreeHashTableIterator(htiter
);
229 Jim_SetResult(interp
, listObjPtr
);
234 static const jim_subcmd_type package_command_table
[] = {
236 .args
= "name ?version?",
237 .function
= package_cmd_provide
,
240 .description
= "Indicates that the current script provides the given package"},
242 .args
= "name ?version?",
243 .function
= package_cmd_require
,
246 .description
= "Loads the given package by looking in standard places"},
248 .function
= package_cmd_list
,
251 .description
= "Lists all known packages"},
255 int Jim_packageInit(Jim_Interp
*interp
)
257 Jim_CreateCommand(interp
, "package", Jim_SubCmdProc
, (void *)package_command_table
, NULL
);