Re: Simple question about directories



alessiostalla@xxxxxxxxx writes:

Thank you both, John and Rainer. It turned out that I was
misinterpreting the hyperspec where it says that the directory
component of a pathname can be a string. I thought it could be a
string specifying an os-dependent path, e.g. "/home/alessio/", while
the hyperspec clearly states that strings in the directory component
name "a single level of directory structure". I'll double-read it the
next time :)

(This post might be overkill for somebody who hasn't stared at chapter
19 for a while. Apologies in advance.)

ISTM that allowing the directory component to be a non-list was
probably a compatibility sop for some implementations of the day. In
my opinion this is a fairly grievous bug with precisely one
nonobvious, not-standardly-usable benefit.

The problem is that the spot that you're quoting, 19.2.2.4.3, says
that "The directory might be a string, :wild, :unspecific, or nil",
and so conforming programs must be prepared to cope with such values,
but the spec doesn't really specify what it means for the directory
component to be a string or :WILD, and various undesirable things
follow from this possibility.

Now, the dictionary entry for MAKE-PATHNAME says that specifying a
string str for the directory argument is equivalent to specifying
(:ABSOLUTE str), and so I think we may conclude that MAKE-PATHNAME is
supposed to return pathnames with the directory canonicalized into
lists in such cases, and likewise when MAKE-PATHNAME is called with
:WILD as the directory. So I think it follows that you can't
explicitly construct a pathname whose directory is a string or :WILD.

But what happens if an implementation hands you a pathname that does
have a string or :WILD in the directory? (Not every pathname is
necessarily constructed by invocations of MAKE-PATHNAME; for example,
some initial pathname must been constructed specially, because
*DEFAULT-PATHNAME-DEFAULTS* can't have a pathname value if no
pathnames eixst yet.) For one thing, the dictionary entry for
MERGE-PATHNAMES is quite clear that if *P* is a pathname with a string
or :WILD for the directory, then

(pathname-directory
(merge-pathnames (make-pathname :directory '(:relative "SUBDIR")
:case :common)
*p*))
=> (:RELATIVE "SUBDIR")

because MERGE-PATHNAMES's special handling of relative directories is
defined to occur if and only if the first argument's directory is a
list whose car is :RELATIVE and the second argument's directory is a
list. But then something crazy follows:

(let ((p2 (make-pathname :directory (pathname-directory *p*)
:defaults *p*)))
(pathname-directory
(merge-pathnames (make-pathname :directory '(:relative "SUBDIR")
:case :common)
p2)))
=> (:ABSOLUTE str "SUBDIR") ; or
=> (:ABSOLUTE :WILD "SUBDIR")

That is, *P* and a pathname constructed entirely from *P*'s components
will have different merging properties!

Similarly, :WILD as the whole component leads to a problem with
PATHNAME-MATCH-P:

;; Suppose that *P* is a pathname whose directory is :WILD, and that
;; the file system is hierarchical.
(pathname-match-p (make-pathname :directory (:absolute "FOO" "BAR")
:case :common
:defaults *p*)
*p*)
=> T ; justified by 19.2.2.2.2 paragraph 1, for example
(pathname-match-p (make-pathname :directory (:absolute "FOO" "BAR")
:case :common
:defaults *p*)
(make-pathname :directory (pathname-directory *p*)
:defaults *p*))
=> NIL ; by 19.2.2.2.2 paragraph 4 and 19.2.2.4.3.

Next, pathnames constructed using such a pathname as the :DEFAULTS and
with no specified directory argument will inherit these merging and
matching properties. So such directory components can infect
user-constructed pathnames, unless you're careful.

Finally, I can't find anywhere that says that a string str or :WILD in
the directory means that the pathname denotes the same filename or has
the same namestring representation as a pathname with (:ABSOLUTE str)
or (:ABSOLUTE :WILD) for the directory and all other components
equivalent. So it's not clear what such pathnames are supposed to be
for, other than to give these brain-busting semantics to the pathname
operators (which might actually be their purpose, see [1]).

Consequently, if the implementation ever produces a pathname whose
directory is a string or :WILD, various generally desirable properties
don't hold for the pathname system on that implementation.
Nevertheless, portable programs are probably required to behave as if
all implementations did this, and so are required to check the
directory component of every pathname they come across, if they're to
have predictable merging and matching results everywhere. (Of course
nobody does this, nor should anybody have to; I'm just noting some
absurd obstacles to writing ANSI-portable programs that work with
files.)

--
RmK

[1] Pathnames are sometimes constructed solely for use as operands to
pathname functions, so maybe pathnames with strings or :WILD in the
directory are supposed to be for that. For example, one use for a
pathname with :WILD for the whole directory would be for
whole-component matching of directories on file systems that don't
support :WILD-INFERIORS. But if I'm right that you can't standardly
construct such a pathname, then that an implementation might permit
these kinds of pathnames to exist is fairly pointless. And in any
case, that still doesn't explain what good a string for the directory
does.
.



Relevant Pages