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