fix documented contracts for path functions

and make `cleanse-path' work for any platform's paths
  while fixing `resolve-path' checking to disallow paths
  for other platforms
 Closes PR 11891
This commit is contained in:
Matthew Flatt 2011-05-04 11:11:40 -06:00
parent 5e7d1f2d9c
commit 34b8dc249e
3 changed files with 55 additions and 43 deletions

View File

@ -103,7 +103,7 @@ the conversion of Windows paths.
See also @racket[some-system-path->string].} See also @racket[some-system-path->string].}
@defproc[(path->bytes [path path?]) bytes?]{ @defproc[(path->bytes [path path-for-some-system?]) bytes?]{
Produces @racket[path]'s byte string representation. No information is Produces @racket[path]'s byte string representation. No information is
lost in this translation, so that @racket[(bytes->path (path->bytes lost in this translation, so that @racket[(bytes->path (path->bytes
@ -138,7 +138,7 @@ As for @racket[path->string], information can be lost from
@defproc[(bytes->path-element [bstr bytes?] @defproc[(bytes->path-element [bstr bytes?]
[type (or/c 'unix 'windows) (system-path-convention-type)]) [type (or/c 'unix 'windows) (system-path-convention-type)])
path?]{ path-for-some-system?]{
Like @racket[bytes->path], except that @racket[bstr] corresponds to a Like @racket[bytes->path], except that @racket[bstr] corresponds to a
single relative element in a path. In terms of conversions and single relative element in a path. In terms of conversions and
@ -186,7 +186,7 @@ reassembling the result with @racket[bytes->path-element] and
@racket[build-path]).} @racket[build-path]).}
@defproc[(path-convention-type [path path?]) @defproc[(path-convention-type [path path-for-some-system?])
(or/c 'unix 'windows)]{ (or/c 'unix 'windows)]{
Accepts a path value (not a string) and returns its convention Accepts a path value (not a string) and returns its convention
@ -201,11 +201,11 @@ Returns the path convention type of the current platform:
Windows.} Windows.}
@defproc[(build-path [base (or/c path-string? 'up 'same)] @defproc[(build-path [base (or/c path-string? path-for-some-system? 'up 'same)]
[sub (or/c (and/c path-string? [sub (or/c (and/c (or/c path-string? path-for-some-system?)
(not/c complete-path?)) (not/c complete-path?))
(or/c 'up 'same))] ...) (or/c 'up 'same))] ...)
path?]{ path-for-some-system?]{
Creates a path given a base path and any number of sub-path Creates a path given a base path and any number of sub-path
extensions. If @racket[base] is an absolute path, the result is an extensions. If @racket[base] is an absolute path, the result is an
@ -219,7 +219,7 @@ drive specification (with or without a trailing slash) the first
@racket[sub] can be an absolute (driveless) path. For all platforms, @racket[sub] can be an absolute (driveless) path. For all platforms,
the last @racket[sub] can be a filename. the last @racket[sub] can be a filename.
The @racket[base] and @racket[sub-paths] arguments can be paths for The @racket[base] and @racket[sub] arguments can be paths for
any platform. The platform for the resulting path is inferred from the any platform. The platform for the resulting path is inferred from the
@racket[base] and @racket[sub] arguments, where string arguments imply @racket[base] and @racket[sub] arguments, where string arguments imply
a path for the current platform. If different arguments are for a path for the current platform. If different arguments are for
@ -261,15 +261,18 @@ Windows examples.
]} ]}
@defproc[(build-path/convention-type [type (or/c 'unix 'windows)] @defproc[(build-path/convention-type
[base path-string?] [type (or/c 'unix 'windows)]
[sub (or/c path-string? 'up 'same)] ...) [base (or/c path-string? path-for-some-system? 'up 'same)]
path?]{ [sub (or/c (and/c (or/c path-string? path-for-some-system?)
(not/c complete-path?))
(or/c 'up 'same))] ...)
path-for-some-system?]{
Like @racket[build-path], except a path convention type is specified Like @racket[build-path], except a path convention type is specified
explicitly.} explicitly.}
@defproc[(absolute-path? [path path-string?]) boolean?]{ @defproc[(absolute-path? [path (or/c path-string? path-for-some-system?)]) boolean?]{
Returns @racket[#t] if @racket[path] is an absolute path, @racket[#f] Returns @racket[#t] if @racket[path] is an absolute path, @racket[#f]
otherwise. The @racket[path] argument can be a path for any otherwise. The @racket[path] argument can be a path for any
@ -278,7 +281,7 @@ contains a nul character), @racket[#f] is returned. This procedure
does not access the filesystem.} does not access the filesystem.}
@defproc[(relative-path? [path path-string?]) boolean?]{ @defproc[(relative-path? [path (or/c path-string? path-for-some-system?)]) boolean?]{
Returns @racket[#t] if @racket[path] is a relative path, @racket[#f] Returns @racket[#t] if @racket[path] is a relative path, @racket[#f]
otherwise. The @racket[path] argument can be a path for any otherwise. The @racket[path] argument can be a path for any
@ -287,7 +290,7 @@ contains a nul character), @racket[#f] is returned. This procedure
does not access the filesystem.} does not access the filesystem.}
@defproc[(complete-path? [path path-string?]) boolean?]{ @defproc[(complete-path? [path (or/c path-string? path-for-some-system?)]) boolean?]{
Returns @racket[#t] if @racket[path] is a completely determined path Returns @racket[#t] if @racket[path] is a completely determined path
(@italic{not} relative to a directory or drive), @racket[#f] (@italic{not} relative to a directory or drive), @racket[#f]
@ -300,9 +303,9 @@ contains a nul character), @racket[#f] is returned.
This procedure does not access the filesystem.} This procedure does not access the filesystem.}
@defproc[(path->complete-path [path path-string?] @defproc[(path->complete-path [path (or/c path-string? path-for-some-system?)]
[base path-string? (current-directory)]) [base (or/c path-string? path-for-some-system?) (current-directory)])
path?]{ path-for-some-system?]{
Returns @racket[path] as a complete path. If @racket[path] is already Returns @racket[path] as a complete path. If @racket[path] is already
a complete path, it is returned as the result. Otherwise, a complete path, it is returned as the result. Otherwise,
@ -317,7 +320,8 @@ platforms, the @exnraise[exn:fail:contract].
This procedure does not access the filesystem.} This procedure does not access the filesystem.}
@defproc[(path->directory-path [path path-string?]) path?]{ @defproc[(path->directory-path [path (or/c path-string? path-for-some-system?)])
path-for-some-system?]{
Returns @racket[path] if @racket[path] syntactically refers to a Returns @racket[path] if @racket[path] syntactically refers to a
directory and ends in a separator, otherwise it returns an extended directory and ends in a separator, otherwise it returns an extended
@ -341,13 +345,14 @@ owning @racket[path]), otherwise @racket[path] is returned (after
expansion).} expansion).}
@defproc[(cleanse-path [path path-string?]) path]{ @defproc[(cleanse-path [path (or/c path-string? path-for-some-system?)])
path-for-some-system?]{
@techlink{Cleanse}s @racket[path] (as described at the beginning of @techlink{Cleanse}s @racket[path] (as described at the beginning of
this chapter) without consulting the filesystem.} this chapter) without consulting the filesystem.}
@defproc[(expand-user-path [path path-string?]) path]{ @defproc[(expand-user-path [path path-string?]) path?]{
@techlink{Cleanse}s @racket[path]. In addition, under @|AllUnix|, a @techlink{Cleanse}s @racket[path]. In addition, under @|AllUnix|, a
leading @litchar{~} is treated as user's home directory and expanded; leading @litchar{~} is treated as user's home directory and expanded;
@ -356,7 +361,9 @@ of the path), where @litchar{~} by itself indicates the home directory
of the current user.} of the current user.}
@defproc[(simplify-path [path path-string?] [use-filesystem? boolean? #t]) path?]{ @defproc[(simplify-path [path (or/c path-string? path-for-some-system?)]
[use-filesystem? boolean? #t])
path-for-some-system?]{
Eliminates redundant path separators (except for a single trailing Eliminates redundant path separators (except for a single trailing
separator), up-directory @litchar{..}, and same-directory @litchar{.} separator), up-directory @litchar{..}, and same-directory @litchar{.}
@ -401,7 +408,8 @@ See @secref["unixpaths"] for more information on simplifying
information on simplifying Windows paths.} information on simplifying Windows paths.}
@defproc[(normal-case-path [path path-string?]) path?]{ @defproc[(normal-case-path [path (or/c path-string? path-for-some-system?)])
path-for-some-system?]{
Returns @racket[path] with ``normalized'' case letters. For @|AllUnix| Returns @racket[path] with ``normalized'' case letters. For @|AllUnix|
paths, this procedure always returns the input path, because paths, this procedure always returns the input path, because
@ -419,9 +427,9 @@ different on the current platform than for the path's platform.
This procedure does not access the filesystem.} This procedure does not access the filesystem.}
@defproc[(split-path [path path-string?]) @defproc[(split-path [path (or/c path-string? path-for-some-system?)])
(values (or/c path? 'relative #f) (values (or/c path-for-some-system? 'relative #f)
(or/c path? 'up 'same) (or/c path-for-some-system? 'up 'same)
boolean?)]{ boolean?)]{
Deconstructs @racket[path] into a smaller path and an immediate Deconstructs @racket[path] into a smaller path and an immediate
@ -469,9 +477,9 @@ See @secref["unixpaths"] for more information on splitting
information on splitting Windows paths.} information on splitting Windows paths.}
@defproc[(path-replace-suffix [path path-string?] @defproc[(path-replace-suffix [path (or/c path-string? path-for-some-system?)]
[suffix (or/c string? bytes?)]) [suffix (or/c string? bytes?)])
path?]{ path-for-some-system?]{
Returns a path that is the same as @racket[path], except that the Returns a path that is the same as @racket[path], except that the
suffix for the last element of the path is changed to suffix for the last element of the path is changed to
@ -483,9 +491,9 @@ at the end of the path element, as long as the path element is not
path for any platform, and the result is for the same platform. If path for any platform, and the result is for the same platform. If
@racket[path] represents a root, the @exnraise[exn:fail:contract].} @racket[path] represents a root, the @exnraise[exn:fail:contract].}
@defproc[(path-add-suffix [path path-string?] @defproc[(path-add-suffix [path (or/c path-string? path-for-some-system?)]
[suffix (or/c string? bytes?)]) [suffix (or/c string? bytes?)])
path?]{ path-for-some-system?]{
Similar to @racket[path-replace-suffix], but any existing suffix on Similar to @racket[path-replace-suffix], but any existing suffix on
@racket[path] is preserved by replacing every @litchar{.} in the last @racket[path] is preserved by replacing every @litchar{.} in the last

View File

@ -674,9 +674,8 @@
(test (string->path "\\\\?\\RED\\..\\..") normal-case-path (coerce "\\\\?\\RED\\..\\..")) (test (string->path "\\\\?\\RED\\..\\..") normal-case-path (coerce "\\\\?\\RED\\..\\.."))
;; cleanse-path removes redundant backslashes ;; cleanse-path removes redundant backslashes
(when (eq? 'windows (system-type))
(test (string->path "\\\\?\\\\UNC\\x\\y") cleanse-path (coerce "\\\\?\\\\UNC\\x\\y")) (test (string->path "\\\\?\\\\UNC\\x\\y") cleanse-path (coerce "\\\\?\\\\UNC\\x\\y"))
(test (string->path "\\\\?\\c:\\") cleanse-path (coerce "\\\\?\\c:\\\\"))) (test (string->path "\\\\?\\c:\\") cleanse-path (coerce "\\\\?\\c:\\\\"))
;; cleanse-path removes redundant backslashes, and ;; cleanse-path removes redundant backslashes, and
;; simplify-path uses cleanse-path under Windows: ;; simplify-path uses cleanse-path under Windows:
@ -690,9 +689,9 @@
(test (string->path "\\\\?\\UNC\\a\\b\\.") cleanse-path (coerce "\\\\?\\UNC\\\\a\\b\\\\.")) (test (string->path "\\\\?\\UNC\\a\\b\\.") cleanse-path (coerce "\\\\?\\UNC\\\\a\\b\\\\."))
(test (string->path "\\\\?\\RED\\\\..") cleanse-path (coerce "\\\\?\\RED\\..")) (test (string->path "\\\\?\\RED\\\\..") cleanse-path (coerce "\\\\?\\RED\\.."))
(test (string->path "\\\\?\\") cleanse-path (coerce "\\\\?\\\\")))]) (test (string->path "\\\\?\\") cleanse-path (coerce "\\\\?\\\\")))])
(when (eq? 'windows (system-type))
(go cleanse-path) (go cleanse-path)
(test (string->path "\\\\?\\c:") cleanse-path (coerce "\\\\?\\c:")) (test (string->path "\\\\?\\c:") cleanse-path (coerce "\\\\?\\c:"))
(when (eq? 'windows (system-type))
(go simplify-path)) (go simplify-path))
(go (lambda (p) (simplify-path p #f))) (go (lambda (p) (simplify-path p #f)))
(test (string->path "a\\b") simplify-path (coerce "a/b") #f) (test (string->path "a\\b") simplify-path (coerce "a/b") #f)

View File

@ -4018,8 +4018,8 @@ static Scheme_Object *resolve_path(int argc, Scheme_Object *argv[])
char *filename; char *filename;
int expanded; int expanded;
if (!SCHEME_GENERAL_PATH_STRINGP(argv[0])) if (!SCHEME_PATH_STRINGP(argv[0]))
scheme_wrong_type("resolve-path", SCHEME_GENERAL_PATH_STRING_STR, 0, argc, argv); scheme_wrong_type("resolve-path", SCHEME_PATH_STRING_STR, 0, argc, argv);
filename = do_expand_filename(argv[0], filename = do_expand_filename(argv[0],
NULL, NULL,
@ -4669,10 +4669,15 @@ static Scheme_Object *current_drive(int argc, Scheme_Object *argv[])
static Scheme_Object *cleanse_path(int argc, Scheme_Object *argv[]) static Scheme_Object *cleanse_path(int argc, Scheme_Object *argv[])
{ {
char *filename; char *filename;
int expanded; int expanded, kind;
if (!SCHEME_PATH_STRINGP(argv[0])) if (!SCHEME_GENERAL_PATH_STRINGP(argv[0]))
scheme_wrong_type("cleanse-path", SCHEME_PATH_STRING_STR, 0, argc, argv); scheme_wrong_type("cleanse-path", SCHEME_GENERAL_PATH_STRING_STR, 0, argc, argv);
if (SCHEME_GENERAL_PATHP(argv[0]))
kind = SCHEME_PATH_KIND(argv[0]);
else
kind = SCHEME_PLATFORM_PATH_KIND;
filename = do_expand_filename(argv[0], filename = do_expand_filename(argv[0],
NULL, NULL,
@ -4681,13 +4686,13 @@ static Scheme_Object *cleanse_path(int argc, Scheme_Object *argv[])
&expanded, &expanded,
1, 0, 1, 0,
0, /* no security check, since the filesystem is not used */ 0, /* no security check, since the filesystem is not used */
SCHEME_PLATFORM_PATH_KIND, kind,
0); 0);
if (!expanded && SCHEME_PATHP(argv[0])) if (!expanded && SCHEME_GENERAL_PATHP(argv[0]))
return argv[0]; return argv[0];
else else
return scheme_make_sized_path(filename, strlen(filename), 1); return scheme_make_sized_offset_kind_path(filename, 0, strlen(filename), 1, kind);
} }
static Scheme_Object *expand_user_path(int argc, Scheme_Object *argv[]) static Scheme_Object *expand_user_path(int argc, Scheme_Object *argv[])