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