Add a general purpose hashtable pattern matcher
[jimtcl.git] / jim-package.c
blob8a2fc49f2822a8de7e41ba3d3792a8e77d22db7e
1 #include <string.h>
3 #include "jim.h"
4 #include "jimautoconf.h"
5 #include "jim-subcmd.h"
7 #ifdef HAVE_UNISTD_H
8 #include <unistd.h>
9 #else
10 #define R_OK 4
11 #endif
13 /* All packages have a fixed, dummy version */
14 static const char *package_version_1 = "1.0";
16 /* -----------------------------------------------------------------------------
17 * Packages handling
18 * ---------------------------------------------------------------------------*/
20 int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
22 /* If the package was already provided returns an error. */
23 Jim_HashEntry *he = Jim_FindHashEntry(&interp->packages, name);
25 /* An empty result means the automatic entry. This can be replaced */
26 if (he && *(const char *)he->u.val) {
27 if (flags & JIM_ERRMSG) {
28 Jim_SetResultFormatted(interp, "package \"%s\" was already provided", name);
30 return JIM_ERR;
32 if (he) {
33 Jim_DeleteHashEntry(&interp->packages, name);
35 Jim_AddHashEntry(&interp->packages, name, (char *)ver);
36 return JIM_OK;
39 static char *JimFindPackage(Jim_Interp *interp, char **prefixes, int prefixc, const char *pkgName)
41 int i;
42 char *buf = Jim_Alloc(JIM_PATH_LEN);
44 for (i = 0; i < prefixc; i++) {
45 if (prefixes[i] == NULL)
46 continue;
48 /* Loadable modules are tried first */
49 #ifdef jim_ext_load
50 snprintf(buf, JIM_PATH_LEN, "%s/%s.so", prefixes[i], pkgName);
51 if (access(buf, R_OK) == 0) {
52 return buf;
54 #endif
55 if (strcmp(prefixes[i], ".") == 0) {
56 snprintf(buf, JIM_PATH_LEN, "%s.tcl", pkgName);
58 else {
59 snprintf(buf, JIM_PATH_LEN, "%s/%s.tcl", prefixes[i], pkgName);
62 if (access(buf, R_OK) == 0) {
63 return buf;
66 Jim_Free(buf);
67 return NULL;
70 /* Search for a suitable package under every dir specified by JIM_LIBPATH,
71 * and load it if possible. If a suitable package was loaded with success
72 * JIM_OK is returned, otherwise JIM_ERR is returned. */
73 static int JimLoadPackage(Jim_Interp *interp, const char *name, int flags)
75 Jim_Obj *libPathObjPtr;
76 char **prefixes, *path;
77 int prefixc, i, retCode = JIM_ERR;
79 libPathObjPtr = Jim_GetGlobalVariableStr(interp, JIM_LIBPATH, JIM_NONE);
80 if (libPathObjPtr == NULL) {
81 prefixc = 0;
82 libPathObjPtr = NULL;
84 else {
85 Jim_IncrRefCount(libPathObjPtr);
86 prefixc = Jim_ListLength(interp, libPathObjPtr);
89 prefixes = Jim_Alloc(sizeof(char *) * prefixc);
90 for (i = 0; i < prefixc; i++) {
91 Jim_Obj *prefixObjPtr;
93 if (Jim_ListIndex(interp, libPathObjPtr, i, &prefixObjPtr, JIM_NONE) != JIM_OK) {
94 prefixes[i] = NULL;
95 continue;
97 prefixes[i] = Jim_StrDup(Jim_String(prefixObjPtr));
100 /* Scan every directory for the the first match */
101 path = JimFindPackage(interp, prefixes, prefixc, name);
102 if (path != NULL) {
103 char *p = strrchr(path, '.');
105 /* Note: Even if the file fails to load, we consider the package loaded.
106 * This prevents issues with recursion.
107 * Use a dummy version of "" to signify this case.
109 Jim_PackageProvide(interp, name, "", 0);
111 /* Try to load/source it */
112 if (p && strcmp(p, ".tcl") == 0) {
113 retCode = Jim_EvalFileGlobal(interp, path);
115 #ifdef jim_ext_load
116 else {
117 retCode = Jim_LoadLibrary(interp, path);
119 #endif
120 if (retCode != JIM_OK) {
121 /* Upon failure, remove the dummy entry */
122 Jim_DeleteHashEntry(&interp->packages, name);
124 Jim_Free(path);
126 for (i = 0; i < prefixc; i++)
127 Jim_Free(prefixes[i]);
128 Jim_Free(prefixes);
129 if (libPathObjPtr)
130 Jim_DecrRefCount(interp, libPathObjPtr);
131 return retCode;
134 int Jim_PackageRequire(Jim_Interp *interp, const char *name, int flags)
136 Jim_HashEntry *he;
138 /* Start with an empty error string */
139 Jim_SetResultString(interp, "", 0);
141 he = Jim_FindHashEntry(&interp->packages, name);
142 if (he == NULL) {
143 /* Try to load the package. */
144 int retcode = JimLoadPackage(interp, name, flags);
145 if (retcode != JIM_OK) {
146 if (flags & JIM_ERRMSG) {
147 int len;
149 Jim_GetString(Jim_GetResult(interp), &len);
150 Jim_SetResultFormatted(interp, "%#s%sCan't load package %s",
151 Jim_GetResult(interp), len ? "\n" : "", name);
153 return retcode;
156 /* In case the package did no 'package provide' */
157 Jim_PackageProvide(interp, name, "1.0", 0);
159 /* Now it must exist */
160 he = Jim_FindHashEntry(&interp->packages, name);
163 Jim_SetResultString(interp, he->u.val, -1);
164 return JIM_OK;
168 *----------------------------------------------------------------------
170 * package provide name ?version?
172 * This procedure is invoked to declare that
173 * a particular package is now present in an interpreter.
174 * The package must not already be provided in the interpreter.
176 * Results:
177 * Returns JIM_OK and sets results as "1.0" (the given version is ignored)
179 *----------------------------------------------------------------------
181 static int package_cmd_provide(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
183 return Jim_PackageProvide(interp, Jim_String(argv[0]), package_version_1, JIM_ERRMSG);
187 *----------------------------------------------------------------------
189 * package require name ?version?
191 * This procedure is load a given package.
192 * Note that the version is ignored.
194 * Results:
195 * Returns JIM_OK and sets the package version.
197 *----------------------------------------------------------------------
199 static int package_cmd_require(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
201 /* package require failing is important enough to add to the stack */
202 interp->addStackTrace++;
204 return Jim_PackageRequire(interp, Jim_String(argv[0]), JIM_ERRMSG);
208 *----------------------------------------------------------------------
210 * package list
212 * Returns a list of known packages
214 * Results:
215 * Returns JIM_OK and sets a list of known packages.
217 *----------------------------------------------------------------------
219 static int package_cmd_list(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
221 Jim_HashTableIterator *htiter;
222 Jim_HashEntry *he;
223 Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
225 htiter = Jim_GetHashTableIterator(&interp->packages);
226 while ((he = Jim_NextHashEntry(htiter)) != NULL) {
227 Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, he->key, -1));
229 Jim_FreeHashTableIterator(htiter);
231 Jim_SetResult(interp, listObjPtr);
233 return JIM_OK;
236 static const jim_subcmd_type package_command_table[] = {
238 "provide",
239 "name ?version?",
240 package_cmd_provide,
243 /* Description: Indicates that the current script provides the given package */
246 "require",
247 "name ?version?",
248 package_cmd_require,
251 /* Description: Loads the given package by looking in standard places */
254 "list",
255 NULL,
256 package_cmd_list,
259 /* Description: Lists all known packages */
262 NULL
266 int Jim_packageInit(Jim_Interp *interp)
268 Jim_CreateCommand(interp, "package", Jim_SubCmdProc, (void *)package_command_table, NULL);
269 return JIM_OK;