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