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