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