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