1 /* @(#)path.c 1.2 04/18/83
2 *
3 * Copyright -C- 1982 Barry S. Roitblat
4 *
5 *
6 * This file contains routines that a) implement a path mechanism, whereby
7 * several places may be searched for files, and b) provide a defaulting
8 * mechanism for file name extensions.
9 *
10 * (modified from code originally written by John Ousterhout for the caesar
11 * design system)
12 */
13
14 #include "gremlin.h"
15 #include <pwd.h>
16 #include <ctype.h>
17
18 /* Imports from config.c: */
19
20 extern char GLibrary[];
21
22 /* Library routines: */
23
24 extern char *strcpy(), *strcpyn(), *index(), *sprintf(), *malloc();
25
26 /* The following string holds the current path,which consists of a bunch
27 * of directory names separated by spaces.
28 */
29
30 #define PATHSIZE 400
31 static char path[PATHSIZE] = ".";
32
33 /* The following string pointers constitute a cache of recently
34 * referenced tilde names.
35 */
36
37 #define NTILDE 10
38 static char *tildename[NTILDE] = {NULL, NULL, NULL, NULL, NULL,
39 NULL, NULL, NULL, NULL, NULL};
40 static char *realname[NTILDE] = {NULL, NULL, NULL, NULL, NULL,
41 NULL, NULL, NULL, NULL, NULL};
42 static int discard = 0;
43
44
45 int
PConvertTilde(psource,pdest,size)46 PConvertTilde(psource, pdest, size)
47 char **psource; /* Pointer to a pointer to the source string */
48 char **pdest; /* Pointer to a pointer to the dest. string */
49 int *size; /* Pointer to no. bytes available at pdest */
50
51 /*---------------------------------------------------------
52 * This routine converts tilde notation into standard directory names.
53 *
54 * Results:
55 * If the conversion was done successfully, then TRUE is returned.
56 * If a user name couldn't be found in the password file, then
57 * FALSE is returned.
58 *
59 * Side Effects:
60 * If the first character of the string indicated by psource is a
61 * tilde ("~") then the subsequent user name is converted to a login
62 * directory name and stored in the string indicated by pdest. Then
63 * remaining characters in the file name at psource are copied to
64 * pdest and both pointers are updated. Upon return, psource points
65 * to the terminating character in the source file name and pdest
66 * points to the next character after the last character in the file
67 * name at that location. If a tilde cannot be converted because the
68 * user name cannot be found, psource is still advanced past the current
69 * entry, but pdest is unaffected. At most size characters will be
70 * stored at pdest, and the size is decremented by the number of
71 * characters we actually stored.
72 *---------------------------------------------------------
73 */
74
75 {
76 register char *ps, *pd;
77 struct passwd *passwd, *getpwnam();
78 char username[17], *string;
79 int i, length;
80
81 ps = *psource;
82 if (*ps == '~')
83 {
84 /* Copy the user name into a string (at most 16 characters).
85 * If we don't have a cached entry for this name, then
86 * read the password file entry for the user and grab out
87 * the login directory.
88 */
89
90 pd = username;
91 for (i=0; ; i++)
92 {
93 *pd = *++ps;
94 if (isspace(*pd) || (*pd=='\0') || (*pd=='/') || (*pd==':'))
95 break;
96 if (i < 16) pd++;
97 }
98 *pd = '\0';
99 for (i=0; i<NTILDE; i++)
100 {
101 if (strcmp(tildename[i], username) == 0)
102 {
103 string = realname[i];
104 goto gotname;
105 }
106 }
107
108 /* Since we don't have a cached entry, read the password file
109 * and make a new cache entry for this one. Note that this
110 * means discarding an old entry.
111 */
112
113 passwd = getpwnam(username);
114 string = passwd->pw_dir;
115 if (passwd != NULL)
116 {
117 discard++;
118 if (discard >= NTILDE) discard = 0;
119 if (tildename[discard] != NULL)
120 {
121 free(tildename[discard]);
122 free(realname[discard]);
123 }
124 tildename[discard] = malloc((unsigned) strlen(username)+1);
125 strcpy(tildename[discard], username);
126 realname[discard] = malloc((unsigned) strlen(string)+1);
127 strcpy(realname[discard], string);
128 }
129 else
130
131 /* The entry can't be found, so skip the source entry and return */
132
133 {
134 while ((*ps != '\0') && !isspace(*ps) && (*ps != ':')) ps++;
135 *psource = ps;
136 return FALSE;
137 }
138 gotname: length = strlen(string);
139 if (length > *size) length = *size;
140 strcpyn(*pdest, string, length);
141 *size -= length;
142 pd = *pdest+length;
143 }
144 else pd = *pdest;
145
146 /* Copy the rest of the directory name from the source to the dest. */
147
148 while ((*ps != '\0') && !isspace(*ps) && (*ps != ':'))
149 if (*size > 0)
150 {
151 *pd++ = *ps++;
152 (*size)--;
153 }
154 else ps++;
155 *psource = ps;
156 *pdest = pd;
157 return TRUE;
158 }
159
160
161 int
PSetPath(string)162 PSetPath(string)
163 char *string; /* Pointer to a string that is to become
164 * the new fle search path. Must consist
165 * of one or more directory names separated
166 * by white space or colons. Two adjacent
167 * colons are the same as ":.:". Tilde
168 * notation is ok.
169 */
170
171 /*---------------------------------------------------------
172 * PSetPath sets up the current search path.
173 *
174 * Results:
175 * -1 is returned if one or more of the paths contained a tilde
176 * notation that couldn't be converted. -2 is returned if the
177 * path space was exhausted. Otherwise a value >= 0 is returned.
178 *
179 * Side Effects:
180 * The string is stored as the current path, and all entries with
181 * tilde notation are converted to non-tilde notation. Tilde entries
182 * that cannot be converted are ignored. Note: only PATHSIZE total
183 * bytes of path are stored, after tilde conversion. Excess bytes
184 * are truncated.
185 *---------------------------------------------------------
186 */
187
188 {
189 int result, spaceleft;
190 char *p;
191
192 result = 0;
193 spaceleft = PATHSIZE-1;
194 p = path;
195
196 if (*string == '\0')
197 {
198 path[0] = '.';
199 path[1] = '\0';
200 return 0;
201 }
202
203 while (*string != '\0')
204 {
205 if (*string == ':')
206 if ((*++string == ':') && (spaceleft >= 2))
207 {
208 *p++ = '.';
209 *p++ = ' ';
210 spaceleft -= 2;
211 }
212 if (spaceleft <= 0) break;
213 while ((*string == ':') || isspace(*string)) string++;
214 if (!PConvertTilde(&string, &p, &spaceleft)) result = -1;
215 else if (spaceleft-- > 0) *p++ = ' ';
216 }
217 *p = '\0';
218 if (spaceleft < 2) return -2;
219 return result;
220 }
221
222
223 char *
PGetPath()224 PGetPath()
225
226 /*---------------------------------------------------------
227 * This routine merely returns a poiner to the current path.
228 *
229 * Results:
230 * The address of the current path (with all tildes expanded).
231 *
232 * Side Effects: None.
233 *---------------------------------------------------------
234 */
235
236 {
237 return path;
238 }
239
240
241 FILE *
POpen(file,prealname,search)242 POpen(file, prealname, search)
243 char *file; /* Name of the file to be opened. */
244 char **prealname; /* Pointer to a location that will be filled
245 * in with the address of the real name of
246 * the file that was successfully opened.
247 * If NULL, then nothing is stored.
248 */
249 int search; /* If FALSE, then the search path business
250 * doesn't happen. A default extension
251 * will still be added.
252 */
253
254 /*---------------------------------------------------------
255 * This routine does a file lookup using the current path and
256 * supplying a default extension.
257 *
258 * Results:
259 * A pointer to a FILE, or NULL if the file couldn't be found.
260 *
261 * Side Effects:
262 * If the first character of the
263 * file name is "~" or "/" or if search is FALSE, then we try
264 * to look up the file with the original name, doing tilde
265 * expansion of course and returning that result. If none of
266 * these conditions is met, we go through the current path
267 * trying to look up the file once for each path
268 * entry by prepending the path entry to the original file name.
269 * This concatenated name is stored in a static string and made
270 * available to the caller through prealname if the open succeeds.
271 * Note: the static string will be trashed on the next call to this
272 * routine. Also, note that no individual file name is allowed to
273 * be more than NAMESIZE characters long. Excess characters are lost.
274 *---------------------------------------------------------
275 */
276
277 {
278 #define NAMESIZE 50
279 static char realname[NAMESIZE];
280 char extendedname[NAMESIZE], *p, *p2;
281 int length, spaceleft, size;
282 FILE *f;
283
284 if (file == NULL) return (FILE *) NULL;
285 if (file[0] == '\0') return (FILE *) NULL;
286
287 if (prealname != NULL) *prealname = realname;
288
289 /* Now try the original name. If it starts with a ~ or / look
290 * it up directly.
291 */
292
293 if (file[0] == '~')
294 {
295 p = realname;
296 length = NAMESIZE-1;
297 if (!PConvertTilde(&file, &p, &length)) return NULL;
298 *p = '\0';
299 return fopen(realname, "r");
300 }
301
302 if ((file[0] == '/') || (search == FALSE))
303 {
304 strcpyn(realname, file, NAMESIZE-1);
305 realname[NAMESIZE-1] = '\0';
306 return fopen(realname, "r");
307 }
308
309 /* Now try going through the path. */
310
311 p = path;
312 while (*p != '\0')
313 {
314 spaceleft = NAMESIZE-1;
315 p2 = realname;
316 while (isspace(*p)) p++;
317 while ((*p != '\0') && !isspace(*p))
318 if (spaceleft-- > 0) *p2++ = *p++;
319 else p++;
320 if (spaceleft-- > 0) *p2++ = '/';
321 if (spaceleft > 0) strcpyn(p2, file, spaceleft);
322 realname[NAMESIZE-1] = '\0';
323 f = fopen(realname, "r");
324 if (f != NULL) return f;
325 }
326
327 /* We've tried the path and that didn't work. As a last shot,
328 * try the library area. Only use the library for reads.
329 */
330
331 p = GLibrary;
332 p2 = realname;
333 size = NAMESIZE;
334 if (!PConvertTilde(&p, &p2, &size)) return FALSE;
335 (void) strcpyn(p2, file, size);
336 realname[NAMESIZE-1] = '\0';
337 return fopen(realname, "r");
338 }
339