1 /*
2  * New xlisp_path code by Dominic Mazzoni
3  *
4  * There is now a function provided to set the xlisp_path.
5  * This is particularly useful for external programs
6  * (e.g. Audacity, or a Nyquist GUI) that have their own
7  * mechanism of setting/finding the path.  If xlisp_path
8  * is NULL, the old platform-specific methods are still
9  * used.
10  */
11 /* CHANGE LOG
12  *
13  * 24-dec-05  RBD
14  *    Made ';' a valid path separator for every system (to work
15  *    around a windows installer limitation)
16  *
17  * 22-jul-07  RBD
18  *    Added get_user_id()
19  *
20  *  9-jan-08  RBD
21  *    Added find-in-xlisp-path as XLISP primitive
22  *
23  * 22-jun-16  RBD & Paul Licameli
24  *    Added cleanup code to free allocated memory
25  */
26 
27 #include <string.h>
28 
29 #include "switches.h"
30 #include "xlisp.h"
31 
32 // boolean flag to support one-shot atexit() registration:
33 static unsigned char registered_path_cleanup = 0;
34 
35 // save a copy of xlisp search path for return_xlisp_path():
36 static char *g_xlisp_path = NULL;
37 
38 // return value for find_in_xlisp_path():
39 static char *g_xlptemp = NULL;
40 
41 
42 // clean up any allocated memory for this module (path.c)
path_cleanup(void)43 static void path_cleanup(void)
44 {
45     if (g_xlisp_path) {
46         free(g_xlisp_path);
47         g_xlisp_path = NULL;
48     }
49 
50     if (g_xlptemp) {
51         free(g_xlptemp);
52         g_xlptemp = NULL;
53     }
54 }
55 
56 
57 // set_xlisp_path - set the search path
58 //     the caller owns the parameter string, a copy is made
59 //     and freed at program exit
set_xlisp_path(const char * p)60 void set_xlisp_path(const char *p)
61 {
62     // one-time register to free any allocated memory at cleanup
63     if (!registered_path_cleanup) {
64         atexit(path_cleanup);
65         registered_path_cleanup = 1;
66     }
67 
68     if (g_xlisp_path) {
69         free(g_xlisp_path);
70         g_xlisp_path = NULL;
71     }
72 
73     if (p) {
74         g_xlisp_path = malloc(strlen(p)+1);
75         // if malloc fails, program will crash here -- maybe that's better
76         //    than setting g_xlisp_path to NULL, masking a critical problem
77         strcpy(g_xlisp_path, p);
78     }
79 }
80 
81 
82 #ifdef UNIX
unix_return_xlisp_path()83 const char *unix_return_xlisp_path()
84 {
85     char *paths = getenv("XLISPPATH");
86     if (!paths || !*paths) {
87         char msg[512];
88         sprintf(msg, "\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
89                 "Warning: XLISP failed to find XLISPPATH in the environment.",
90                 "If you are using Nyquist, probably you should cd to the",
91                 "nyquist directory and type:",
92                 "    setenv XLISPPATH `pwd`/runtime:`pwd`/lib",
93                 "or set XLISPPATH in your .login or .cshrc file.",
94                 "If you use the bash shell, try:",
95                 "    XLISPPATH=`pwd`/runtime:`pwd`/lib; export XLISPPATH");
96         errputstr(msg);
97     }
98     return paths;
99 }
100 #endif
101 
102 #ifdef WINDOWS
103 #include "winfun.h"
104 
windows_return_xlisp_path()105 const char *windows_return_xlisp_path()
106 {
107     #define paths_max 1024
108     char paths[paths_max];
109     get_xlisp_path(paths, paths_max);
110     /* make sure we got paths, and the list is not empty */
111     if (!paths[0]) {
112         sprintf(paths, "\n%s\n%s\n%s\n",
113            "Warning: XLISP failed to find XLISPPATH in the Registry.",
114            "You should follow the installation instructions. Enter an",
115            "empty string if you really want no search path.");
116         errputstr(paths);
117     }
118     set_xlisp_path(paths);
119     /* for debugging:
120       errputstr("windows_return_xlisp_path() returns ");
121       errputstr(paths);
122       errputstr("\n"); */
123     return g_xlisp_path;
124 }
125 
126 #endif
127 
128 #ifdef MACINTOSH
mac_return_xlisp_path()129 const char *mac_return_xlisp_path()
130 {
131     #define paths_max 1024
132     char paths[paths_max];
133     int prefs_found = false;
134     get_xlisp_path(paths, paths_max, &prefs_found);
135     if (!paths[0]) {
136         if (prefs_found) {
137             sprintf(paths, "\n%s\n%s\n%s\n",
138              "Warning: XLISP failed to find XLISPPATH in XLisp Preferences.",
139              "You should probably delete XLisp Preferences and let XLisp",
140              "create a new one for you.");
141         }
142         else {
143            sprintf(paths, "\n%s\n%s\n%s\n%s\n%s\n",
144             "Warning: XLISP failed to find XLisp Preferences.",
145             "You should manually locate and load the file runtime:init.lsp",
146             "Nyquist will create an XLisp Preferences file to automatically",
147             "find the file next time. You may edit XLisp Preferences to add",
148             "additional search paths, using a comma as separator.");
149         }
150         errputstr(paths);
151     }
152     set_xlisp_path(paths);
153     return g_xlisp_path;
154 }
155 
156 
get_user_id()157 const char *get_user_id()
158 {
159     // not implemented for MACINTOSH (OS 9), just use "nyquist"
160     return "nyquist";
161 }
162 #endif
163 
164 
return_xlisp_path()165 const char *return_xlisp_path()
166 {
167     if (g_xlisp_path)
168         return g_xlisp_path;
169 
170     // if g_xlisp_path has not been set, use one of the following
171     // to get the value, cache it in g_xlisp_path, and return it
172 #ifdef WINDOWS
173     return windows_return_xlisp_path();
174 #endif
175 #ifdef MACINTOSH
176     return mac_return_xlisp_path();
177 #endif
178 #ifdef UNIX
179     return unix_return_xlisp_path();
180 #endif
181 }
182 
183 
184 // find_in_xlisp_path -- find fname or fname.lsp by searching XLISP_PATH
185 //
186 // NOTE: this module owns the string. The string is valid
187 // until the next call to find_in_xlisp_path()
188 //
find_in_xlisp_path(const char * fname)189 const char *find_in_xlisp_path(const char *fname)
190 {
191     const char *paths = return_xlisp_path();
192     if (!paths)
193         return NULL;
194 
195     // one-time register to free any allocated memory at cleanup
196     if (!registered_path_cleanup) {
197         atexit(path_cleanup);
198         registered_path_cleanup = 1;
199     }
200 
201     while (paths && *paths) {
202         FILE *fp;
203         const char *start;
204         intptr_t len;
205 
206         /* skip over separator */
207         while (*paths == os_sepchar || *paths == ';') paths++;
208 
209         /* find next directory */
210         start = paths;
211         while (*paths && (*paths != os_sepchar && *paths != ';'))
212             paths++;
213 
214         if (g_xlptemp) {
215            free(g_xlptemp);
216            g_xlptemp = NULL;
217         }
218 
219         len = paths - start;
220         g_xlptemp = malloc(len + strlen(fname) + 10);
221         memcpy(g_xlptemp, start, len);
222 
223         if (len == 0)
224            continue;
225 
226         /* add "/" if needed */
227         if (g_xlptemp[len-1] != os_pathchar)
228            g_xlptemp[len++] = os_pathchar;
229 
230         /* append the file name */
231         memcpy(&g_xlptemp[len], fname, strlen(fname));
232         len += strlen(fname);
233         g_xlptemp[len] = 0;
234 
235 
236         /* printf("Attempting to open %s, start is %s\n", g_xlptemp, start); */
237         fp = osaopen(g_xlptemp, "r");
238         if (!fp) {
239             /* try appending the .lsp extension */
240             if (needsextension(g_xlptemp)) {
241                 strcat(g_xlptemp, ".lsp");
242                 fp = osaopen(g_xlptemp, "r");
243                 if (!fp) {
244                     g_xlptemp[strlen(g_xlptemp) - 4] = 0; /* remove .lsp */
245                 }
246             }
247         }
248         if (fp) {
249            fclose(fp);
250 
251            #ifdef MACINTOSH
252            /* We found the file ok, call setup_preferences to create
253             * XLisp Preferences file (this only happens if previous
254             * attempt to find the file failed
255             */
256            setup_preferences(g_xlptemp);
257            #endif
258 
259            return g_xlptemp;
260         }
261     }
262 
263     /* It wasn't found */
264     return NULL;
265 }
266 
267 
268 /* xfind_in_xlisp_path -- search XLISPPATH for file, return full path */
xfind_in_xlisp_path()269 LVAL xfind_in_xlisp_path()
270 {
271    LVAL string = xlgastring();
272    const char *path = (const char *) getstring(string);
273    xllastarg();
274    path = find_in_xlisp_path(path);
275    return (path ? cvstring(path) : NULL);
276 }
277