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