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