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