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