• rewrite simplify_path() to keep more in line with do_realpath()

• add regression test to ensure that //foo pathnames are never simplified
This commit is contained in:
tg 2011-03-26 21:09:09 +00:00
parent 7d86eaba8c
commit a55de5f840
2 changed files with 93 additions and 58 deletions

11
check.t
View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.435 2011/03/26 16:19:29 tg Exp $
# $MirOS: src/bin/mksh/check.t,v 1.436 2011/03/26 21:09:06 tg Exp $
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
@ -8581,6 +8581,12 @@ stdin:
realpath t2
realpath t3
rm -f t1 t2 t3
cd //usr/bin
pwd
cd ../lib
pwd
cd -P ../libexec
pwd
expected-stdout:
/bin
//bin
@ -8591,6 +8597,9 @@ expected-stdout:
/bin
//bin
/bin
//usr/bin
//usr/bin/../lib
//usr/libexec
---
name: crash-1
description:

140
misc.c
View File

@ -29,7 +29,7 @@
#include <grp.h>
#endif
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.161 2011/03/26 19:43:48 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.162 2011/03/26 21:09:09 tg Exp $");
/* type bits for unsigned char */
unsigned char chtypes[UCHAR_MAX + 1];
@ -1511,78 +1511,104 @@ make_path(const char *cwd, const char *file,
return (rval);
}
/*
/*-
* Simplify pathnames containing "." and ".." entries.
* ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
* but simplify_path("//./C/foo/bar/../baz") stays as it is
*
* simplify_path(this) = that
* /a/b/c/./../d/.. /a/b
* //./C/foo/bar/../baz //./C/foo/bar/../baz
* /foo/ /foo
* /foo/../../bar /bar
* /foo/./blah/.. /foo
* . .
* .. ..
* ./foo foo
* foo/../../../bar ../../bar
*/
void
simplify_path(char *pathl)
simplify_path(char *p)
{
char *cur, *t;
bool isrooted;
char *very_start = pathl, *start;
char *dp, *ip, *sp, *tp;
size_t len;
bool needslash;
if (!*pathl)
switch (*p) {
case 0:
return;
case '/':
/* exactly two leading slashes? (SUSv4 3.266) */
if (p[1] == '/' && p[2] != '/')
/* implementation defined, we CANNOT simplify this */
return;
needslash = true;
break;
default:
needslash = false;
}
dp = ip = sp = p;
if ((isrooted = pathl[0] == '/'))
very_start++;
/* exactly two leading slashes? (SUSv4 3.266) */
if (isrooted && pathl[1] == '/' && pathl[2] != '/')
/* implementation defined, we CANNOT simplify this */
return;
/*-
* Before After
* /foo/ /foo
* /foo/../../bar /bar
* /foo/./blah/.. /foo
* . .
* .. ..
* ./foo foo
* foo/../../../bar ../../bar
*/
for (cur = t = start = very_start; ; ) {
/* treat multiple '/'s as one '/' */
while (*t == '/')
t++;
if (*t == '\0') {
if (cur == pathl)
/* convert empty path to dot */
*cur++ = '.';
*cur = '\0';
while (*ip) {
/* skip slashes in input */
while (*ip == '/')
++ip;
if (!*ip)
break;
}
if (t[0] == '.') {
if (!t[1] || t[1] == '/') {
t += 1;
/* get next pathname component from input */
tp = ip;
while (*ip && *ip != '/')
++ip;
len = ip - tp;
/* check input for "." and ".." */
if (tp[0] == '.') {
if (len == 1)
/* just continue with the next one */
continue;
} else if (t[1] == '.' && (!t[2] || t[2] == '/')) {
if (!isrooted && cur == start) {
if (cur != very_start)
*cur++ = '/';
*cur++ = '.';
*cur++ = '.';
start = cur;
} else if (cur != start)
while (--cur > start && *cur != '/')
;
t += 2;
else if (len == 2 && tp[1] == '.') {
/* parent level, but how? */
if (*p == '/')
/* absolute path, only one way */
goto strip_last_component;
else if (dp > sp) {
/* relative path, with subpaths */
needslash = false;
strip_last_component:
/* strip off last pathname component */
while (dp > sp)
if (*--dp == '/')
break;
} else {
/* relative path, at its beginning */
if (needslash)
/* or already dotdot-slash'd */
*dp++ = '/';
/* keep dotdot-slash if not absolute */
*dp++ = '.';
*dp++ = '.';
needslash = true;
sp = dp;
}
/* then continue with the next one */
continue;
}
}
if (cur != very_start)
*cur++ = '/';
if (needslash)
*dp++ = '/';
/* find/copy next component of pathname */
while (*t && *t != '/')
*cur++ = *t++;
/* append next pathname component to output */
memmove(dp, tp, len);
dp += len;
/* append slash if we continue */
needslash = true;
/* try next component */
}
if (dp == p)
/* empty path -> dot */
*dp++ = needslash ? '/' : '.';
*dp = '\0';
}
void