1 /* $NetBSD: path.c,v 1.3 1997/10/20 10:39:26 lukem Exp $ */ 2 3 #include "sh.h" 4 #include "ksh_stat.h" 5 6 /* 7 * Contains a routine to search a : seperated list of 8 * paths (a la CDPATH) and make appropiate file names. 9 * Also contains a routine to simplify .'s and ..'s out of 10 * a path name. 11 * 12 * Larry Bouzane (larry@cs.mun.ca) 13 */ 14 15 #ifdef S_ISLNK 16 static char *do_phys_path ARGS((XString *xsp, char *xp, const char *path)); 17 #endif /* S_ISLNK */ 18 19 /* 20 * Makes a filename into result using the following algorithm. 21 * - make result NULL 22 * - if file starts with '/', append file to result & set cdpathp to NULL 23 * - if file starts with ./ or ../ append cwd and file to result 24 * and set cdpathp to NULL 25 * - if the first element of cdpathp doesnt start with a '/' xx or '.' xx 26 * then cwd is appended to result. 27 * - the first element of cdpathp is appended to result 28 * - file is appended to result 29 * - cdpathp is set to the start of the next element in cdpathp (or NULL 30 * if there are no more elements. 31 * The return value indicates whether a non-null element from cdpathp 32 * was appened to result. 33 */ 34 int 35 make_path(cwd, file, cdpathp, xsp, phys_pathp) 36 const char *cwd; 37 const char *file; 38 char **cdpathp; /* & of : seperated list */ 39 XString *xsp; 40 int *phys_pathp; 41 { 42 int rval = 0; 43 int use_cdpath = 1; 44 char *plist; 45 int len; 46 int plen = 0; 47 char *xp = Xstring(*xsp, xp); 48 49 if (!file) 50 file = null; 51 52 if (!ISRELPATH(file)) { 53 *phys_pathp = 0; 54 use_cdpath = 0; 55 } else { 56 if (file[0] == '.') { 57 char c = file[1]; 58 59 if (c == '.') 60 c = file[2]; 61 if (ISDIRSEP(c) || c == '\0') 62 use_cdpath = 0; 63 } 64 65 plist = *cdpathp; 66 if (!plist) 67 use_cdpath = 0; 68 else if (use_cdpath) { 69 char *pend; 70 71 for (pend = plist; *pend && *pend != PATHSEP; pend++) 72 ; 73 plen = pend - plist; 74 *cdpathp = *pend ? ++pend : (char *) 0; 75 } 76 77 if ((use_cdpath == 0 || !plen || ISRELPATH(plist)) 78 && (cwd && *cwd)) 79 { 80 len = strlen(cwd); 81 XcheckN(*xsp, xp, len); 82 memcpy(xp, cwd, len); 83 xp += len; 84 if (!ISDIRSEP(cwd[len - 1])) 85 Xput(*xsp, xp, DIRSEP); 86 } 87 *phys_pathp = Xlength(*xsp, xp); 88 if (use_cdpath && plen) { 89 XcheckN(*xsp, xp, plen); 90 memcpy(xp, plist, plen); 91 xp += plen; 92 if (!ISDIRSEP(plist[plen - 1])) 93 Xput(*xsp, xp, DIRSEP); 94 rval = 1; 95 } 96 } 97 98 len = strlen(file) + 1; 99 XcheckN(*xsp, xp, len); 100 memcpy(xp, file, len); 101 102 if (!use_cdpath) 103 *cdpathp = (char *) 0; 104 105 return rval; 106 } 107 108 /* 109 * Simplify pathnames containing "." and ".." entries. 110 * ie, simplify_path("/a/b/c/./../d/..") returns "/a/b" 111 */ 112 void 113 simplify_path(path) 114 char *path; 115 { 116 char *cur; 117 char *t; 118 int isrooted; 119 char *very_start = path; 120 char *start; 121 122 if (!*path) 123 return; 124 125 if ((isrooted = ISROOTEDPATH(path))) 126 very_start++; 127 #ifdef OS2 128 if (path[0] && path[1] == ':') /* skip a: */ 129 very_start += 2; 130 #endif /* OS2 */ 131 132 /* Before After 133 * /foo/ /foo 134 * /foo/../../bar /bar 135 * /foo/./blah/.. /foo 136 * . . 137 * .. .. 138 * ./foo foo 139 * foo/../../../bar ../../bar 140 * OS2: 141 * a:/foo/../.. a:/ 142 * a:. a: 143 * a:.. a:.. 144 * a:foo/../../blah a:../blah 145 */ 146 147 for (cur = t = start = very_start; ; ) { 148 /* treat multiple '/'s as one '/' */ 149 while (ISDIRSEP(*t)) 150 t++; 151 152 if (*t == '\0') { 153 if (cur == path) 154 /* convert empty path to dot */ 155 *cur++ = '.'; 156 *cur = '\0'; 157 break; 158 } 159 160 if (t[0] == '.') { 161 if (!t[1] || ISDIRSEP(t[1])) { 162 t += 1; 163 continue; 164 } else if (t[1] == '.' && (!t[2] || ISDIRSEP(t[2]))) { 165 if (!isrooted && cur == start) { 166 if (cur != very_start) 167 *cur++ = DIRSEP; 168 *cur++ = '.'; 169 *cur++ = '.'; 170 start = cur; 171 } else if (cur != start) 172 while (--cur > start && !ISDIRSEP(*cur)) 173 ; 174 t += 2; 175 continue; 176 } 177 } 178 179 if (cur != very_start) 180 *cur++ = DIRSEP; 181 182 /* find/copy next component of pathname */ 183 while (*t && !ISDIRSEP(*t)) 184 *cur++ = *t++; 185 } 186 } 187 188 189 void 190 set_current_wd(path) 191 char *path; 192 { 193 int len; 194 char *p = path; 195 196 if (!p && !(p = ksh_get_wd((char *) 0, 0))) 197 p = null; 198 199 len = strlen(p) + 1; 200 201 if (len > current_wd_size) 202 current_wd = aresize(current_wd, current_wd_size = len, APERM); 203 memcpy(current_wd, p, len); 204 if (p != path && p != null) 205 afree(p, ATEMP); 206 } 207 208 #ifdef S_ISLNK 209 char * 210 get_phys_path(path) 211 const char *path; 212 { 213 XString xs; 214 char *xp; 215 216 Xinit(xs, xp, strlen(path) + 1, ATEMP); 217 218 xp = do_phys_path(&xs, xp, path); 219 220 if (!xp) 221 return (char *) 0; 222 223 if (Xlength(xs, xp) == 0) 224 Xput(xs, xp, DIRSEP); 225 Xput(xs, xp, '\0'); 226 227 return Xclose(xs, xp); 228 } 229 230 static char * 231 do_phys_path(xsp, xp, path) 232 XString *xsp; 233 char *xp; 234 const char *path; 235 { 236 const char *p, *q; 237 int len, llen; 238 int savepos; 239 char lbuf[PATH]; 240 241 Xcheck(*xsp, xp); 242 for (p = path; p; p = q) { 243 while (ISDIRSEP(*p)) 244 p++; 245 if (!*p) 246 break; 247 len = (q = ksh_strchr_dirsep(p)) ? q - p : strlen(p); 248 if (len == 1 && p[0] == '.') 249 continue; 250 if (len == 2 && p[0] == '.' && p[1] == '.') { 251 while (xp > Xstring(*xsp, xp)) { 252 xp--; 253 if (ISDIRSEP(*xp)) 254 break; 255 } 256 continue; 257 } 258 259 savepos = Xsavepos(*xsp, xp); 260 Xput(*xsp, xp, DIRSEP); 261 XcheckN(*xsp, xp, len + 1); 262 memcpy(xp, p, len); 263 xp += len; 264 *xp = '\0'; 265 266 llen = readlink(Xstring(*xsp, xp), lbuf, sizeof(lbuf) - 1); 267 if (llen < 0) { 268 /* EINVAL means it wasn't a symlink... */ 269 if (errno != EINVAL) 270 return (char *) 0; 271 continue; 272 } 273 lbuf[llen] = '\0'; 274 275 /* If absolute path, start from scratch.. */ 276 xp = ISABSPATH(lbuf) ? Xstring(*xsp, xp) 277 : Xrestpos(*xsp, xp, savepos); 278 if (!(xp = do_phys_path(xsp, xp, lbuf))) 279 return (char *) 0; 280 } 281 return xp; 282 } 283 #endif /* S_ISLNK */ 284 285 #ifdef TEST 286 287 main(argc, argv) 288 { 289 int rv; 290 char *cp, cdpath[256], pwd[256], file[256], result[256]; 291 292 printf("enter CDPATH: "); gets(cdpath); 293 printf("enter PWD: "); gets(pwd); 294 while (1) { 295 if (printf("Enter file: "), gets(file) == 0) 296 return 0; 297 cp = cdpath; 298 do { 299 rv = make_path(pwd, file, &cp, result, sizeof(result)); 300 printf("make_path returns (%d), \"%s\" ", rv, result); 301 simplify_path(result); 302 printf("(simpifies to \"%s\")\n", result); 303 } while (cp); 304 } 305 } 306 #endif /* TEST */ 307