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