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