tests: Don't stop on the first error
[jimtcl.git] / jim-package.c
blobd9f50a93f418d58755f4e664ca262d62bdf4a902
1 #include <string.h>
3 #include "jimautoconf.h"
4 #include <jim-subcmd.h>
6 #ifdef HAVE_UNISTD_H
7 #include <unistd.h>
8 #else
9 #define R_OK 4
10 #endif
12 /* All packages have a fixed, dummy version */
13 static const char *package_version_1 = "1.0";
15 /* -----------------------------------------------------------------------------
16 * Packages handling
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);
29 return JIM_ERR;
31 if (he) {
32 Jim_DeleteHashEntry(&interp->packages, name);
34 Jim_AddHashEntry(&interp->packages, name, (char *)ver);
35 return JIM_OK;
38 static char *JimFindPackage(Jim_Interp *interp, char **prefixes, int prefixc, const char *pkgName)
40 int i;
41 char *buf = Jim_Alloc(JIM_PATH_LEN);
43 for (i = 0; i < prefixc; i++) {
44 if (prefixes[i] == NULL)
45 continue;
47 /* Loadable modules are tried first */
48 #ifdef jim_ext_load
49 snprintf(buf, JIM_PATH_LEN, "%s/%s.so", prefixes[i], pkgName);
50 if (access(buf, R_OK) == 0) {
51 return buf;
53 #endif
54 if (strcmp(prefixes[i], ".") == 0) {
55 snprintf(buf, JIM_PATH_LEN, "%s.tcl", pkgName);
57 else {
58 snprintf(buf, JIM_PATH_LEN, "%s/%s.tcl", prefixes[i], pkgName);
61 if (access(buf, R_OK) == 0) {
62 return buf;
65 Jim_Free(buf);
66 return NULL;
69 /* Search for a suitable package under every dir specified by JIM_LIBPATH,
70 * and load it if possible. If a suitable package was loaded with success
71 * JIM_OK is returned, otherwise JIM_ERR is returned. */
72 static int JimLoadPackage(Jim_Interp *interp, const char *name, int flags)
74 Jim_Obj *libPathObjPtr;
75 char **prefixes, *path;
76 int prefixc, i, retCode = JIM_ERR;
78 libPathObjPtr = Jim_GetGlobalVariableStr(interp, JIM_LIBPATH, JIM_NONE);
79 if (libPathObjPtr == NULL) {
80 prefixc = 0;
81 libPathObjPtr = NULL;
83 else {
84 Jim_IncrRefCount(libPathObjPtr);
85 prefixc = Jim_ListLength(interp, libPathObjPtr);
88 prefixes = Jim_Alloc(sizeof(char *) * prefixc);
89 for (i = 0; i < prefixc; i++) {
90 Jim_Obj *prefixObjPtr;
92 if (Jim_ListIndex(interp, libPathObjPtr, i, &prefixObjPtr, JIM_NONE) != JIM_OK) {
93 prefixes[i] = NULL;
94 continue;
96 prefixes[i] = Jim_StrDup(Jim_String(prefixObjPtr));
99 /* Scan every directory for the the first match */
100 path = JimFindPackage(interp, prefixes, prefixc, name);
101 if (path != NULL) {
102 char *p = strrchr(path, '.');
104 /* Note: Even if the file fails to load, we consider the package loaded.
105 * This prevents issues with recursion.
106 * Use a dummy version of "" to signify this case.
108 Jim_PackageProvide(interp, name, "", 0);
110 /* Try to load/source it */
111 if (p && strcmp(p, ".tcl") == 0) {
112 retCode = Jim_EvalFileGlobal(interp, path);
114 #ifdef jim_ext_load
115 else {
116 retCode = Jim_LoadLibrary(interp, path);
118 #endif
119 if (retCode != JIM_OK) {
120 /* Upon failure, remove the dummy entry */
121 Jim_DeleteHashEntry(&interp->packages, name);
123 Jim_Free(path);
125 for (i = 0; i < prefixc; i++)
126 Jim_Free(prefixes[i]);
127 Jim_Free(prefixes);
128 if (libPathObjPtr)
129 Jim_DecrRefCount(interp, libPathObjPtr);
130 return retCode;
133 int Jim_PackageRequire(Jim_Interp *interp, const char *name, int flags)
135 Jim_HashEntry *he;
137 /* Start with an empty error string */
138 Jim_SetResultString(interp, "", 0);
140 he = Jim_FindHashEntry(&interp->packages, name);
141 if (he == NULL) {
142 /* Try to load the package. */
143 int retcode = JimLoadPackage(interp, name, flags);
144 if (retcode != JIM_OK) {
145 if (flags & JIM_ERRMSG) {
146 int len;
148 Jim_GetString(Jim_GetResult(interp), &len);
149 Jim_SetResultFormatted(interp, "%#s%sCan't load package %s",
150 Jim_GetResult(interp), len ? "\n" : "", name);
152 return retcode;
155 /* In case the package did no 'package provide' */
156 Jim_PackageProvide(interp, name, "1.0", 0);
158 /* Now it must exist */
159 he = Jim_FindHashEntry(&interp->packages, name);
162 Jim_SetResultString(interp, he->u.val, -1);
163 return JIM_OK;
167 *----------------------------------------------------------------------
169 * package provide name ?version?
171 * This procedure is invoked to declare that
172 * a particular package is now present in an interpreter.
173 * The package must not already be provided in the interpreter.
175 * Results:
176 * Returns JIM_OK and sets results as "1.0" (the given version is ignored)
178 *----------------------------------------------------------------------
180 static int package_cmd_provide(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
182 return Jim_PackageProvide(interp, Jim_String(argv[0]), package_version_1, JIM_ERRMSG);
186 *----------------------------------------------------------------------
188 * package require name ?version?
190 * This procedure is load a given package.
191 * Note that the version is ignored.
193 * Results:
194 * Returns JIM_OK and sets the package version.
196 *----------------------------------------------------------------------
198 static int package_cmd_require(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
200 /* package require failing is important enough to add to the stack */
201 interp->addStackTrace++;
203 return Jim_PackageRequire(interp, Jim_String(argv[0]), JIM_ERRMSG);
207 *----------------------------------------------------------------------
209 * package list
211 * Returns a list of known packages
213 * Results:
214 * Returns JIM_OK and sets a list of known packages.
216 *----------------------------------------------------------------------
218 static int package_cmd_list(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
220 Jim_HashTableIterator *htiter;
221 Jim_HashEntry *he;
222 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
224 htiter = Jim_GetHashTableIterator(&interp->packages);
225 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
226 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, he->key, -1));
228 Jim_FreeHashTableIterator(htiter);
230 Jim_SetResult(interp, listObjPtr);
232 return JIM_OK;
235 static const jim_subcmd_type package_command_table[] = {
237 "provide",
238 "name ?version?",
239 package_cmd_provide,
242 /* Description: Indicates that the current script provides the given package */
245 "require",
246 "name ?version?",
247 package_cmd_require,
250 /* Description: Loads the given package by looking in standard places */
253 "list",
254 NULL,
255 package_cmd_list,
258 /* Description: Lists all known packages */
261 NULL
265 int Jim_packageInit(Jim_Interp *interp)
267 Jim_CreateCommand(interp, "package", Jim_SubCmdProc, (void *)package_command_table, NULL);
268 return JIM_OK;