From 27e6b58dacdb56651d7098fd55c1c0f2151f3cb9 Mon Sep 17 00:00:00 2001
From: Dirk Zimoch <dirk.zimoch@psi.ch>
Date: Tue, 6 Sep 2016 14:18:21 +0200
Subject: [PATCH] allow to search scripts in all loaded modules

---
 require.c   | 89 +++++++++++++++++++++++++++++++++--------------------
 require.h   |  1 +
 runScript.c | 38 ++++++++++++++++++++++-
 3 files changed, 93 insertions(+), 35 deletions(-)

diff --git a/require.c b/require.c
index ef8ca9e3..ba50d483 100644
--- a/require.c
+++ b/require.c
@@ -372,67 +372,71 @@ int putenvprintf(const char* format, ...)
     return status;
 }
 
-static int setupDbPath(const char* module, const char* dbdir)
+static void insertDirIntoPath(const char* varname, const char* dirname)
 {
     char* old_path;           
-    char* p;
-    size_t len;
-
-    char* absdir = realpath(dbdir, NULL); /* so we can change directory later safely */
-    if (absdir == NULL)
-    {
-        if (requireDebug)
-            printf("require: cannot resolve %s\n", dbdir);
-        return -1;
-    }
-    len = strlen(absdir);
-
-    if (requireDebug)
-        printf("require: found template directory %s\n", absdir);
-
-    /* set up db search path environment variables
-      <module>_TEMPLATES      template path of <module>
-      <module>_DB             template path of <module>
-      TEMPLATES               template path of the current module (overwritten)
-      EPICS_DB_INCLUDE_PATH   template path of all loaded modules (last in front after ".")
-    */
 
-    putenvprintf("%s_DB=%s", module, absdir);
-    putenvprintf("%s_TEMPLATES=%s", module, absdir);
-    putenvprintf("TEMPLATES=%s", absdir);
-
-    /* add directory to front of EPICS_DB_INCLUDE_PATH */
-    old_path = getenv("EPICS_DB_INCLUDE_PATH");
+    /* add directory to front */
+    old_path = getenv(varname);
     if (old_path == NULL)
-        putenvprintf("EPICS_DB_INCLUDE_PATH=." OSI_PATH_LIST_SEPARATOR "%s", absdir);
+        putenvprintf("%s=." OSI_PATH_LIST_SEPARATOR "%s", varname, dirname);
     else
     {
+        size_t len = strlen(dirname);
+        char* p;
+
         /* skip over "." at the beginning */
         if (old_path[0] == '.' && old_path[1] == OSI_PATH_LIST_SEPARATOR[0])
             old_path += 2;
 
         /* If directory is already in path, move it to front */
         p = old_path;
-        while ((p = strstr(p, absdir)) != NULL)
+        while ((p = strstr(p, dirname)) != NULL)
         {
             if ((p == old_path || *(p-1) == OSI_PATH_LIST_SEPARATOR[0]) &&
                 (p[len] == 0 || p[len] == OSI_PATH_LIST_SEPARATOR[0]))
             {
                 if (p == old_path) break; /* already at front, nothing to do */
                 memmove(old_path+len+1, old_path, p-old_path-1);
-                strcpy(old_path, absdir);
+                strcpy(old_path, dirname);
                 old_path[len] = OSI_PATH_LIST_SEPARATOR[0];
                 if (requireDebug)
-                    printf("require: modified EPICS_DB_INCLUDE_PATH=%s\n", old_path);
+                    printf("require: modified %s=%s\n", varname, old_path);
                 break;
             }
             p += len;
         }
         if (p == NULL)
             /* add new directory to the front (after "." )*/
-            putenvprintf("EPICS_DB_INCLUDE_PATH=." OSI_PATH_LIST_SEPARATOR "%s" OSI_PATH_LIST_SEPARATOR "%s",
-                 absdir, old_path);
+            putenvprintf("%s=." OSI_PATH_LIST_SEPARATOR "%s" OSI_PATH_LIST_SEPARATOR "%s",
+                 varname, dirname, old_path);
     }
+}
+
+static int setupDbPath(const char* module, const char* dbdir)
+{
+    char* absdir = realpath(dbdir, NULL); /* so we can change directory later safely */
+    if (absdir == NULL)
+    {
+        if (requireDebug)
+            printf("require: cannot resolve %s\n", dbdir);
+        return -1;
+    }
+
+    if (requireDebug)
+        printf("require: found template directory %s\n", absdir);
+
+    /* set up db search path environment variables
+      <module>_TEMPLATES      template path of <module>
+      <module>_DB             template path of <module>
+      TEMPLATES               template path of the current module (overwritten)
+      EPICS_DB_INCLUDE_PATH   template path of all loaded modules (last in front after ".")
+    */
+
+    putenvprintf("%s_DB=%s", module, absdir);
+    putenvprintf("%s_TEMPLATES=%s", module, absdir);
+    putenvprintf("TEMPLATES=%s", absdir);
+    insertDirIntoPath("EPICS_DB_INCLUDE_PATH", absdir);
     free(absdir);
     return 0;
 }
@@ -739,6 +743,21 @@ static void registerExternalModules()
 }
 #endif
 
+size_t foreachLoadedLib(size_t (*func)(const char* name, const char* version, const char* path, void* arg), void* arg)
+{
+    moduleitem* m;
+    int result;
+
+    for (m = loadedModules; m; m=m->next)
+    {
+        const char* name = m->content;
+        const char* version = name + strlen(name)+1;
+        const char* path = version + strlen(version)+1;
+        result = func(name, version, path, arg);
+        if (result) return result;
+    }
+    return 0;
+}
 
 const char* getLibVersion(const char* libname)
 {
@@ -1533,6 +1552,8 @@ loadlib:
 
     status = 0;       
 
+    insertDirIntoPath("SCRIPT_PATH", filename);
+
     if (requireDebug)
         printf("require: looking for template directory\n");
     /* filename = "<dirname>/[dirlen]<module>/<version>/R<epicsRelease>/[releasediroffs]..." */
diff --git a/require.h b/require.h
index 4d618879..5e33736b 100644
--- a/require.h
+++ b/require.h
@@ -10,6 +10,7 @@ extern "C" {
 #endif
 
 int require(const char* libname, const char* version, const char* args);
+size_t foreachLoadedLib(size_t (*func)(const char* name, const char* version, const char* path, void* arg), void* arg);
 const char* getLibVersion(const char* libname);
 const char* getLibLocation(const char* libname);
 int libversionShow(const char* outfile);
diff --git a/runScript.c b/runScript.c
index de9b70c4..1ac8b270 100644
--- a/runScript.c
+++ b/runScript.c
@@ -10,6 +10,7 @@
 #include <epicsVersion.h>
 
 #ifdef vxWorks
+#include "asprintf.h"
 #include <sysSymTbl.h>
 #ifdef _WRS_VXWORKS_MAJOR
 /* vxWorks 6+ */
@@ -22,10 +23,15 @@
 #include <symLib.h>
 #endif
 
+#define IS_ABS_PATH(filename) (filename[0] == '/')  /* may be different for other OS */
+
 #ifdef BASE_VERSION
 #define EPICS_3_13
 extern char** ppGlobalEnviron;
+#define OSI_PATH_SEPARATOR "/"
+#define OSI_PATH_LIST_SEPARATOR ":"
 #else
+#include <osiFileName.h>
 #include <iocsh.h>
 epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd);
 #include <epicsExport.h>
@@ -210,7 +216,37 @@ int runScript(const char* filename, const char* args)
         free(pairs);
     }
 
-    if ((file = fopen(filename, "r")) == NULL) { perror(filename); return errno; }
+    if (IS_ABS_PATH(filename))
+    {
+        file = fopen(filename, "r");
+    }
+    else
+    {
+        const char* dirname;
+        const char* end;
+        char* fullname;
+        const char* path = getenv("SCRIPT_PATH");
+        int dirlen;
+        
+        for (dirname = path; dirname != NULL; dirname = end)
+        {
+            end = strchr(dirname, OSI_PATH_LIST_SEPARATOR[0]);
+            if (end && end[1] == OSI_PATH_SEPARATOR[0] && end[2] == OSI_PATH_SEPARATOR[0])   /* "http://..." and friends */
+                end = strchr(end+2, OSI_PATH_LIST_SEPARATOR[0]);
+            if (end) dirlen = (int)(end++ - dirname);
+            else dirlen = (int)strlen(dirname);
+            if (dirlen == 0) continue; /* ignore empty path elements */
+            asprintf(&fullname, "%.*s" OSI_PATH_SEPARATOR "%s", 
+                dirlen, dirname, filename);
+            if (runScriptDebug)
+                printf("runScript: trying %s\n", fullname);
+            file = fopen(fullname, "r");
+            if (!file && errno != ENOENT) perror(fullname);
+            free(fullname);
+            if (file) break;
+        }
+    }
+    if (file == NULL) { perror(filename); return errno; }
 
     /*  line by line after expanding macros with arguments or environment */
     while (fgets(line_raw, line_raw_size, file))
-- 
GitLab