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