1 /* $NetBSD: path.c,v 1.4 1999/10/20 15:10:00 hubertf Exp $ */ 2 3 #include "sh.h" 4 #include "ksh_stat.h" 5 6 /* 7 * Contains a routine to search a : separated 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 : separated 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 #if defined (OS2) || defined (__CYGWIN__) 128 if (path[0] && path[1] == ':') /* skip a: */ 129 very_start += 2; 130 #endif /* OS2 || __CYGWIN__ */ 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 and CYGWIN: 141 * a:/foo/../.. a:/ 142 * a:. a: 143 * a:.. a:.. 144 * a:foo/../../blah a:../blah 145 */ 146 147 #ifdef __CYGWIN__ 148 /* preserve leading double-slash on pathnames (for UNC paths) */ 149 if (path[0] && ISDIRSEP(path[0]) && path[1] && ISDIRSEP(path[1])) 150 very_start++; 151 #endif /* __CYGWIN__ */ 152 153 for (cur = t = start = very_start; ; ) { 154 /* treat multiple '/'s as one '/' */ 155 while (ISDIRSEP(*t)) 156 t++; 157 158 if (*t == '\0') { 159 if (cur == path) 160 /* convert empty path to dot */ 161 *cur++ = '.'; 162 *cur = '\0'; 163 break; 164 } 165 166 if (t[0] == '.') { 167 if (!t[1] || ISDIRSEP(t[1])) { 168 t += 1; 169 continue; 170 } else if (t[1] == '.' && (!t[2] || ISDIRSEP(t[2]))) { 171 if (!isrooted && cur == start) { 172 if (cur != very_start) 173 *cur++ = DIRSEP; 174 *cur++ = '.'; 175 *cur++ = '.'; 176 start = cur; 177 } else if (cur != start) 178 while (--cur > start && !ISDIRSEP(*cur)) 179 ; 180 t += 2; 181 continue; 182 } 183 } 184 185 if (cur != very_start) 186 *cur++ = DIRSEP; 187 188 /* find/copy next component of pathname */ 189 while (*t && !ISDIRSEP(*t)) 190 *cur++ = *t++; 191 } 192 } 193 194 195 void 196 set_current_wd(path) 197 char *path; 198 { 199 int len; 200 char *p = path; 201 202 if (!p && !(p = ksh_get_wd((char *) 0, 0))) 203 p = null; 204 205 len = strlen(p) + 1; 206 207 if (len > current_wd_size) 208 current_wd = aresize(current_wd, current_wd_size = len, APERM); 209 memcpy(current_wd, p, len); 210 if (p != path && p != null) 211 afree(p, ATEMP); 212 } 213 214 #ifdef S_ISLNK 215 char * 216 get_phys_path(path) 217 const char *path; 218 { 219 XString xs; 220 char *xp; 221 222 Xinit(xs, xp, strlen(path) + 1, ATEMP); 223 224 xp = do_phys_path(&xs, xp, path); 225 226 if (!xp) 227 return (char *) 0; 228 229 if (Xlength(xs, xp) == 0) 230 Xput(xs, xp, DIRSEP); 231 Xput(xs, xp, '\0'); 232 233 return Xclose(xs, xp); 234 } 235 236 static char * 237 do_phys_path(xsp, xp, path) 238 XString *xsp; 239 char *xp; 240 const char *path; 241 { 242 const char *p, *q; 243 int len, llen; 244 int savepos; 245 char lbuf[PATH]; 246 247 Xcheck(*xsp, xp); 248 for (p = path; p; p = q) { 249 while (ISDIRSEP(*p)) 250 p++; 251 if (!*p) 252 break; 253 len = (q = ksh_strchr_dirsep(p)) ? q - p : strlen(p); 254 if (len == 1 && p[0] == '.') 255 continue; 256 if (len == 2 && p[0] == '.' && p[1] == '.') { 257 while (xp > Xstring(*xsp, xp)) { 258 xp--; 259 if (ISDIRSEP(*xp)) 260 break; 261 } 262 continue; 263 } 264 265 savepos = Xsavepos(*xsp, xp); 266 Xput(*xsp, xp, DIRSEP); 267 XcheckN(*xsp, xp, len + 1); 268 memcpy(xp, p, len); 269 xp += len; 270 *xp = '\0'; 271 272 llen = readlink(Xstring(*xsp, xp), lbuf, sizeof(lbuf) - 1); 273 if (llen < 0) { 274 /* EINVAL means it wasn't a symlink... */ 275 if (errno != EINVAL) 276 return (char *) 0; 277 continue; 278 } 279 lbuf[llen] = '\0'; 280 281 /* If absolute path, start from scratch.. */ 282 xp = ISABSPATH(lbuf) ? Xstring(*xsp, xp) 283 : Xrestpos(*xsp, xp, savepos); 284 if (!(xp = do_phys_path(xsp, xp, lbuf))) 285 return (char *) 0; 286 } 287 return xp; 288 } 289 #endif /* S_ISLNK */ 290 291 #ifdef TEST 292 293 main(argc, argv) 294 { 295 int rv; 296 char *cp, cdpath[256], pwd[256], file[256], result[256]; 297 298 printf("enter CDPATH: "); gets(cdpath); 299 printf("enter PWD: "); gets(pwd); 300 while (1) { 301 if (printf("Enter file: "), gets(file) == 0) 302 return 0; 303 cp = cdpath; 304 do { 305 rv = make_path(pwd, file, &cp, result, sizeof(result)); 306 printf("make_path returns (%d), \"%s\" ", rv, result); 307 simplify_path(result); 308 printf("(simpifies to \"%s\")\n", result); 309 } while (cp); 310 } 311 } 312 #endif /* TEST */ 313