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 */ 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 */ 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 * 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 * 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