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