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
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
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 *
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 *
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