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