It was possible to create a bad ref
[jimtcl.git] / jim-package.c
blobfd8314d1fdb3d4b9c393ec9f33fd46d9bb1b2b65
1 #include <unistd.h>
2 #include <string.h>
4 #include <jim.h>
5 #include <jim-subcmd.h>
7 /* -----------------------------------------------------------------------------
8 * Packages handling
9 * ---------------------------------------------------------------------------*/
11 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
13 /* If the package was already provided returns an error. */
14 if (Jim_FindHashEntry(&interp->packages, name) != NULL) {
15 if (flags & JIM_ERRMSG) {
16 Jim_SetResultFormatted(interp, "package \"%s\" was already provided", name);
18 return JIM_ERR;
20 Jim_AddHashEntry(&interp->packages, name, (char *)ver);
21 return JIM_OK;
24 static char *JimFindPackage(Jim_Interp *interp, char **prefixes, int prefixc, const char *pkgName)
26 int i;
28 for (i = 0; i < prefixc; i++) {
29 /* REVISIT: Move off stack */
30 char buf[JIM_PATH_LEN];
32 if (prefixes[i] == NULL)
33 continue;
35 if (strcmp(prefixes[i], ".") == 0) {
36 snprintf(buf, sizeof(buf), "%s.tcl", pkgName);
38 else {
39 snprintf(buf, sizeof(buf), "%s/%s.tcl", prefixes[i], pkgName);
42 if (access(buf, R_OK) == 0) {
43 return Jim_StrDup(buf);
46 #ifdef jim_ext_load
47 snprintf(buf, sizeof(buf), "%s/%s.so", prefixes[i], pkgName);
48 if (access(buf, R_OK) == 0) {
49 return Jim_StrDup(buf);
51 #endif
53 return NULL;
56 /* Search for a suitable package under every dir specified by JIM_LIBPATH,
57 * and load it if possible. If a suitable package was loaded with success
58 * JIM_OK is returned, otherwise JIM_ERR is returned. */
59 static int JimLoadPackage(Jim_Interp *interp, const char *name, int flags)
61 Jim_Obj *libPathObjPtr;
62 char **prefixes, *path;
63 int prefixc, i, retCode = JIM_ERR;
65 libPathObjPtr = Jim_GetGlobalVariableStr(interp, JIM_LIBPATH, JIM_NONE);
66 if (libPathObjPtr == NULL) {
67 prefixc = 0;
68 libPathObjPtr = NULL;
70 else {
71 Jim_IncrRefCount(libPathObjPtr);
72 prefixc = Jim_ListLength(interp, libPathObjPtr);
75 prefixes = Jim_Alloc(sizeof(char *) * prefixc);
76 for (i = 0; i < prefixc; i++) {
77 Jim_Obj *prefixObjPtr;
79 if (Jim_ListIndex(interp, libPathObjPtr, i, &prefixObjPtr, JIM_NONE) != JIM_OK) {
80 prefixes[i] = NULL;
81 continue;
83 prefixes[i] = Jim_StrDup(Jim_GetString(prefixObjPtr, NULL));
86 /* Scan every directory for the the first match */
87 path = JimFindPackage(interp, prefixes, prefixc, name);
88 if (path != NULL) {
89 char *p = strrchr(path, '.');
91 /* Try to load/source it */
92 if (p && strcmp(p, ".tcl") == 0) {
93 retCode = Jim_EvalFile(interp, path);
95 #ifdef jim_ext_load
96 else {
97 retCode = Jim_LoadLibrary(interp, path);
99 #endif
100 Jim_Free(path);
102 else {
103 retCode = JIM_ERR;
105 for (i = 0; i < prefixc; i++)
106 Jim_Free(prefixes[i]);
107 Jim_Free(prefixes);
108 if (libPathObjPtr)
109 Jim_DecrRefCount(interp, libPathObjPtr);
110 return retCode;
113 int Jim_PackageRequire(Jim_Interp *interp, const char *name, int flags)
115 Jim_HashEntry *he;
116 int retcode = 0;
117 const char *version;
119 /* Start with an empty error string */
120 Jim_SetResultString(interp, "", 0);
122 he = Jim_FindHashEntry(&interp->packages, name);
123 if (he == NULL) {
124 /* Try to load the package. */
125 retcode = JimLoadPackage(interp, name, flags);
126 if (retcode != JIM_OK) {
127 if (flags & JIM_ERRMSG) {
128 int len;
130 Jim_GetString(Jim_GetResult(interp), &len);
131 Jim_SetResultFormatted(interp, "%#s%sCan't load package %s",
132 Jim_GetResult(interp), len ? "\n" : "", name);
134 return retcode;
136 else {
137 he = Jim_FindHashEntry(&interp->packages, name);
138 if (he == NULL) {
139 /* Did not call package provide, so we do it for them */
140 Jim_PackageProvide(interp, name, "1.0", 0);
142 version = "1.0";
144 else {
145 version = he->val;
149 else {
150 version = he->val;
152 Jim_SetResultString(interp, version, -1);
153 return retcode;
157 *----------------------------------------------------------------------
159 * package provide name ?version?
161 * This procedure is invoked to declare that a particular version
162 * of a particular package is now present in an interpreter. There
163 * must not be any other version of this package already
164 * provided in the interpreter.
166 * Results:
167 * Returns JIM_OK and sets the package version (or 1.0 if not specified).
169 *----------------------------------------------------------------------
171 static int package_cmd_provide(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
173 const char *version = "1.0";
175 if (argc == 2) {
176 version = Jim_GetString(argv[1], NULL);
178 return Jim_PackageProvide(interp, Jim_GetString(argv[0], NULL), version, JIM_ERRMSG);
182 *----------------------------------------------------------------------
184 * package require name ?version?
186 * This procedure is load a given package.
187 * Note that the version is ignored.
189 * Results:
190 * Returns JIM_OK and sets the package version.
192 *----------------------------------------------------------------------
194 static int package_cmd_require(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
196 /* package require failing is important enough to add to the stack */
197 interp->addStackTrace++;
199 return Jim_PackageRequire(interp, Jim_GetString(argv[0], NULL), JIM_ERRMSG);
203 *----------------------------------------------------------------------
205 * package list
207 * Returns a list of known packages
209 * Results:
210 * Returns JIM_OK and sets a list of known packages.
212 *----------------------------------------------------------------------
214 static int package_cmd_list(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
216 Jim_HashTableIterator *htiter;
217 Jim_HashEntry *he;
218 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
220 htiter = Jim_GetHashTableIterator(&interp->packages);
221 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
222 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, he->key, -1));
224 Jim_FreeHashTableIterator(htiter);
226 Jim_SetResult(interp, listObjPtr);
228 return JIM_OK;
231 static const jim_subcmd_type package_command_table[] = {
232 {.cmd = "provide",
233 .args = "name ?version?",
234 .function = package_cmd_provide,
235 .minargs = 1,
236 .maxargs = 2,
237 .description = "Indicates that the current script provides the given package"},
238 {.cmd = "require",
239 .args = "name ?version?",
240 .function = package_cmd_require,
241 .minargs = 1,
242 .maxargs = 2,
243 .description = "Loads the given package by looking in standard places"},
244 {.cmd = "list",
245 .function = package_cmd_list,
246 .minargs = 0,
247 .maxargs = 0,
248 .description = "Lists all known packages"},
252 int Jim_packageInit(Jim_Interp *interp)
254 Jim_CreateCommand(interp, "package", Jim_SubCmdProc, (void *)package_command_table, NULL);
255 return JIM_OK;