From: rif
Subject: pathnames question
Date: 
Message-ID: <wj0smqqyqzk.fsf@five-percent-nation.mit.edu>
How can I take a pathname that shows up in CL (CMUCL) as
#p"home:data/foo.tmp" and get a string like "/home/rif/data/foo.tmp"
(or the equivalent if the program's running in Windows)?  I need to
get to a "basic string" in order to export the name to a non-CL
program that's being scripted.

Cheers,

rif

From: Raymond Toy
Subject: Re: pathnames question
Date: 
Message-ID: <4nisrmhuki.fsf@edgedsp4.rtp.ericsson.se>
>>>>> "rif" == rif  <···@mit.edu> writes:

    rif> How can I take a pathname that shows up in CL (CMUCL) as
    rif> #p"home:data/foo.tmp" and get a string like "/home/rif/data/foo.tmp"
    rif> (or the equivalent if the program's running in Windows)?  I need to
    rif> get to a "basic string" in order to export the name to a non-CL
    rif> program that's being scripted.

You've got a CMUCL search list (presumably).  If so, look up
enumerate-search-list:

(enumerate-search-list (p #p"home:apps/foo.tmp")
		       (return p))
=>
#p"/home/unix/toy/apps/foo.tmp"

Ray
From: Nikodemus Siivola
Subject: Re: pathnames question
Date: 
Message-ID: <bbkuu8$5liba$1@midnight.cs.hut.fi>
rif <···@mit.edu> wrote:

> How can I take a pathname that shows up in CL (CMUCL) as
> #p"home:data/foo.tmp" and get a string like "/home/rif/data/foo.tmp"
> (or the equivalent if the program's running in Windows)?  I need to
> get to a "basic string" in order to export the name to a non-CL
> program that's being scripted.

Anyways, if the file exists you should get the name with:

 (namestring (truename #p"home:data/foo.tmp"))

TRUENAME is needed to resolve the search-path home to an actual
pathname, but you can't use it on a non-existent file.

Since I'm guessing that you get the "home:" part by calling
USER-HOMEDIR-PATHNAME, is suggest you replace that by:

 (truename (user-homedir-pathname))

Then you should end up with #p"/home/rif/data/foo.tmp",
which you can just pass to NAMESTRING. Alternatively you
can deal with what you have, though that gets a tad ugly:

(defun truename1 (pathspec)
  "Like CL:TRUENAME, but works for non-existent files as well. Untested."
  (or (probe-file pathspec)
      (let (n)
         (unwind-protect
               (with-open-file (s pathspec :direction :output)
                   (close s)
                   (setf n (truename s)))
            (delete-file n))
          n)))

Cheers,

  -- Nikodemus
From: Sam Steingold
Subject: Re: pathnames question
Date: 
Message-ID: <m31xy9zpnr.fsf@loiso.podval.org>
> * In message <··············@midnight.cs.hut.fi>
> * On the subject of "Re: pathnames question"
> * Sent on 4 Jun 2003 14:15:36 GMT
> * Honorable Nikodemus Siivola <········@kekkonen.cs.hut.fi> writes:
>
> (defun truename1 (pathspec)
>   "Like CL:TRUENAME, but works for non-existent files as well. Untested."
>   (or (probe-file pathspec)
>       (let (n)
>          (unwind-protect
>                (with-open-file (s pathspec :direction :output)
>                    (close s)
>                    (setf n (truename s)))
>             (delete-file n))
>           n)))

(defun safe-truename (path)
  "Like TRUENAME, but handle non-existing files.
Note that the directory must exist."
  (make-pathname :name (pathname-name path) :type (pathname-type path)
                 :defaults (truename (make-pathname :name nil :type nil
                                                    :defaults path))))


-- 
Sam Steingold (http://www.podval.org/~sds) running RedHat9 GNU/Linux
<http://www.camera.org> <http://www.iris.org.il> <http://www.memri.org/>
<http://www.mideasttruth.com/> <http://www.palestine-central.com/links.html>
We're too busy mopping the floor to turn off the faucet.
From: Kent M Pitman
Subject: Re: pathnames question
Date: 
Message-ID: <sfw8yshk8i0.fsf@shell01.TheWorld.com>
Sam Steingold <···@gnu.org> writes:

> > * In message <··············@midnight.cs.hut.fi>
> > * On the subject of "Re: pathnames question"
> > * Sent on 4 Jun 2003 14:15:36 GMT
> > * Honorable Nikodemus Siivola <········@kekkonen.cs.hut.fi> writes:
> >
> > (defun truename1 (pathspec)
> >   "Like CL:TRUENAME, but works for non-existent files as well. Untested."
> >   (or (probe-file pathspec)
> >       (let (n)
> >          (unwind-protect
> >                (with-open-file (s pathspec :direction :output)
> >                    (close s)
> >                    (setf n (truename s)))
> >             (delete-file n))
> >           n)))

This is open to a timing error where you overwrite a file that got closed
with valid data between the probe-file and the open.

Moreover, you should be using :IF-EXISTS :ERROR in the call to OPEN.

But on some file systems, multiple opens can exist on the same filename,
and if you close one of them and then the other opened file closes, you
could be deleting the wrong one above.
 
> (defun safe-truename (path)
>   "Like TRUENAME, but handle non-existing files.
> Note that the directory must exist."
>   (make-pathname :name (pathname-name path) :type (pathname-type path)
>                  :defaults (truename (make-pathname :name nil :type nil
>                                                     :defaults path))))

There is no portable guarantee that TRUENAME here will yield a good value
here.  In some file systems, that call to TRUENAME is going to yield NIL
and the directory information is going to be lost.

It's hard to do what the user wants entirely correctly because in some
situations for some file systems, you can't just piecemeal create a
file name by assembly from other components.  For example, a TOPS-20
file name like SOURCE: (which might be implemented as a search list,
similar to logical pathnames) might put files for a given base
filename in one directory or for another base filename in another
directory.  Probing the base without the name might yield a third
directory.  If you re-merge against the truename'd result, you might
not get the filename that you would have gotten if you told the file
system the whole name.  (You could probably get similar results with
logical pathnames, for that matter.)

Something like the following will work "more often" but I'm not sure is
really reliable even then, for the reasons I just described:

 (defun somewhat-safe-truename (path)
   (or (truename path)
       (let* ((dir (make-pathname :name nil :type nil :version nil
                                  :defaults path))
              (true-dir (truename dir)))
         (make-pathname :name (pathname-name path)
                        :type (pathname-type path)
                        :version (pathname-version path)
                        :defaults (or true-dir dir)))))

Note that this treatment of versions is a little better on versioned
file systems, but that there is also no real way to win with that.
I don't recommend throwing the version info away, since if there's a 
timing error and one or more other versioned files exist by the time 
an OPEN is done on the resulting name, the retained version can help 
resolve how to open or create a proper version.

Usually, though, you just have to resort to implementation-specific extra
information about how directories are managed.  Most implementations
have MUCH better tools for doing a lot of this and it's better to build
up from those.
From: Sam Steingold
Subject: Re: pathnames question
Date: 
Message-ID: <m3ptlty7th.fsf@loiso.podval.org>
> * In message <···············@shell01.TheWorld.com>
> * On the subject of "Re: pathnames question"
> * Sent on 04 Jun 2003 14:22:47 -0400
> * Honorable Kent M Pitman <······@world.std.com> writes:
>
> Sam Steingold <···@gnu.org> writes:
> 
> > (defun safe-truename (path)
> >   "Like TRUENAME, but handle non-existing files.
> > Note that the directory must exist."
> >   (make-pathname :name (pathname-name path) :type (pathname-type path)
> >                  :defaults (truename (make-pathname :name nil :type nil
> >                                                     :defaults path))))
> 
> There is no portable guarantee that TRUENAME here will yield a good value
> here.  In some file systems, that call to TRUENAME is going to yield NIL
> and the directory information is going to be lost.

TRUENAME cannot return NIL, it can signal an error instead.
I assume that you mean (IGNORE-ERRORS (TRUENAME ...))

> Note that this treatment of versions is a little better on versioned
> file systems, but that there is also no real way to win with that.

I was just being sloppy (none of the CLs I know of uses versions)



(defun safe-truename (path)
  "Like TRUENAME, but handle non-existing files.
Note that the directory must exist."
  (or (ignore-errors (truename path))
      (let ((dir (make-pathname :name nil :type nil :version nil
                                :defaults path)))
        (make-pathname :name (pathname-name path) :type (pathname-type path)
                       :version (pathname-version path)
                       :defaults (or (truename dir) dir)))))

if (truename dir) is replaced with (ignore-errors (truename dir)),
then the directory does not have to exist.

-- 
Sam Steingold (http://www.podval.org/~sds) running RedHat9 GNU/Linux
<http://www.camera.org> <http://www.iris.org.il> <http://www.memri.org/>
<http://www.mideasttruth.com/> <http://www.palestine-central.com/links.html>
Illiterate?  Write today, for free help!
From: Raymond Toy
Subject: Re: pathnames question
Date: 
Message-ID: <4nadcxijkv.fsf@edgedsp4.rtp.ericsson.se>
>>>>> "Sam" == Sam Steingold <···@gnu.org> writes:

    Sam> I was just being sloppy (none of the CLs I know of uses versions)

CMUCL supports versions in the Emacs style.  Version n of file
xxx.lisp is mapped to the file xxx.lisp~n~.  

I don't know if anybody actually takes advantage of this or not.  I
certainly don't.

Ray
From: Raymond Wiker
Subject: Re: pathnames question
Date: 
Message-ID: <861xyarpgs.fsf@raw.grenland.fast.no>
rif <···@mit.edu> writes:

> How can I take a pathname that shows up in CL (CMUCL) as
> #p"home:data/foo.tmp" and get a string like "/home/rif/data/foo.tmp"
> (or the equivalent if the program's running in Windows)?  I need to
> get to a "basic string" in order to export the name to a non-CL
> program that's being scripted.

        First of all, the pathname #p"home:data/foo.tmp" is invalid;
it should be #p"home:data;foo.tmp"

        Given suitable logical pathname translation(s) for "home", you
should be able to use translate-logical-pathname to get the physical
pathname you want:

* (logical-pathname-translations "home")
(("**;*.*.*" "/usr/home/raw/**/*.*.*"))
* (translate-logical-pathname "home:data;foo.tmp")
#P"/usr/home/raw/data/foo.tmp"


-- 
Raymond Wiker                        Mail:  ·············@fast.no
Senior Software Engineer             Web:   http://www.fast.no/
Fast Search & Transfer ASA           Phone: +47 23 01 11 60
P.O. Box 1677 Vika                   Fax:   +47 35 54 87 99
NO-0120 Oslo, NORWAY                 Mob:   +47 48 01 11 60

Try FAST Search: http://alltheweb.com/
From: rif
Subject: Re: pathnames question
Date: 
Message-ID: <wj07k829feu.fsf@five-percent-nation.mit.edu>
Raymond Wiker <·············@fast.no> writes:

> rif <···@mit.edu> writes:
> 
> > How can I take a pathname that shows up in CL (CMUCL) as
> > #p"home:data/foo.tmp" and get a string like "/home/rif/data/foo.tmp"
> > (or the equivalent if the program's running in Windows)?  I need to
> > get to a "basic string" in order to export the name to a non-CL
> > program that's being scripted.
> 
>         First of all, the pathname #p"home:data/foo.tmp" is invalid;
> it should be #p"home:data;foo.tmp"

What exactly is meant by "invalid" here?  For example, I can say:

* (setf *p* (merge-pathnames #p"home:data/" "foo.tmp"))
#p"home:data/foo.tmp"

and the resulting pathname *p* is "usable", within CMUCL I can use it
to open and read from a file /home/rif/data/foo.tmp.  (The
#p"home:data/" is actually the cllib:*datadir* variable defined by
CLOCC to hold temporary saved data.)

>         Given suitable logical pathname translation(s) for "home", you
> should be able to use translate-logical-pathname to get the physical
> pathname you want:
> 
> * (logical-pathname-translations "home")
> (("**;*.*.*" "/usr/home/raw/**/*.*.*"))
> * (translate-logical-pathname "home:data;foo.tmp")
> #P"/usr/home/raw/data/foo.tmp"
> 

The "home:" is not defined by me, but comes directly from the built-in
(user-homedir-pathname) function.  Is there an
implementation-independent way to get from the pathname returned by
(user-homedir-pathname) to a basic string representation that could be
used outside of CL?

Cheers,

rif
From: Christophe Rhodes
Subject: Re: pathnames question
Date: 
Message-ID: <sqwug2m1zi.fsf@lambda.jcn.srcf.net>
rif <···@mit.edu> writes:

> The "home:" is not defined by me, but comes directly from the built-in
> (user-homedir-pathname) function.  Is there an
> implementation-independent way to get from the pathname returned by
> (user-homedir-pathname) to a basic string representation that could be
> used outside of CL?

The short answer is "no".

The slightly less short answer is
  (namestring (truename (user-homedir-pathname)))
but you're still at the mercy of the implementation's definitions of
TRUENAME and NAMESTRING corner cases.  I suggest that you complain to
your vendor if this doesn't work, though.

Cheers,

Christophe
-- 
http://www-jcsu.jesus.cam.ac.uk/~csr21/       +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%")    (pprint #36rJesusCollegeCambridge)
From: Daniel Barlow
Subject: Re: pathnames question
Date: 
Message-ID: <871xyadmsk.fsf@noetbook.telent.net>
Raymond Wiker <·············@fast.no> writes:

> rif <···@mit.edu> writes:
>
>> How can I take a pathname that shows up in CL (CMUCL) as
>> #p"home:data/foo.tmp" and get a string like "/home/rif/data/foo.tmp"
>> (or the equivalent if the program's running in Windows)?  I need to
>> get to a "basic string" in order to export the name to a non-CL
>> program that's being scripted.
>
>         First of all, the pathname #p"home:data/foo.tmp" is invalid;
> it should be #p"home:data;foo.tmp"

Trick question.  Well, not, but it might as well have been.  "home" is
(within the meaning of the standard) a physical host in CMUCL, not a
logical host.  

(Actually, it's a search list, which seems to be a pre-ANSI LPN
equivalent that's actually a lot more useful, but I digress)


-dan

-- 

   http://www.cliki.net/ - Link farm for free CL-on-Unix resources 
From: Henrik Motakef
Subject: Re: pathnames question
Date: 
Message-ID: <873ciqf26d.fsf@interim.henrik-motakef.de>
rif <···@mit.edu> writes:

> How can I take a pathname that shows up in CL (CMUCL) as
> #p"home:data/foo.tmp" and get a string like "/home/rif/data/foo.tmp"
> (or the equivalent if the program's running in Windows)?  I need to
> get to a "basic string" in order to export the name to a non-CL
> program that's being scripted.

With NAMESTRING.
From: rif
Subject: Re: pathnames question
Date: 
Message-ID: <wj0brxe9fqp.fsf@five-percent-nation.mit.edu>
Henrik Motakef <··············@web.de> writes:

> rif <···@mit.edu> writes:
> 
> > How can I take a pathname that shows up in CL (CMUCL) as
> > #p"home:data/foo.tmp" and get a string like "/home/rif/data/foo.tmp"
> > (or the equivalent if the program's running in Windows)?  I need to
> > get to a "basic string" in order to export the name to a non-CL
> > program that's being scripted.
> 
> With NAMESTRING.

This is the first thing I tried, but I wasn't sure how to make it work.
Under CMUCL:

* *p*
#p"home:data/foo.tmp"
* (namestring *p*)
"home:data/foo.tmp"

Did you have something else in mind?

Cheers,

rif
From: Henrik Motakef
Subject: Re: pathnames question
Date: 
Message-ID: <87znkydmv6.fsf@interim.henrik-motakef.de>
rif <···@mit.edu> writes:

> This is the first thing I tried, but I wasn't sure how to make it work.
> Under CMUCL:
>
> * *p*
> #p"home:data/foo.tmp"
> * (namestring *p*)
> "home:data/foo.tmp"
>
> Did you have something else in mind?

No, that's what I meant, sorry. I guess it has to do with CMUCL
search-lists (hence the "/" instead of ";"?). UNIX-NAMESTRING seems to
work, but as the pathname itself, it isn't portable.
From: Eric Marsden
Subject: Re: pathnames question
Date: 
Message-ID: <wziznkym23s.fsf@melbourne.laas.fr>
>>>>> "rif" == rif  <···@mit.edu> writes:

  rif> How can I take a pathname that shows up in CL (CMUCL) as
  rif> #p"home:data/foo.tmp" and get a string like
  rif> "/home/rif/data/foo.tmp" (or the equivalent if the program's
  rif> running in Windows)? I need to get to a "basic string" in order
  rif> to export the name to a non-CL program that's being scripted.

in standard Common Lisp, you would use NAMESTRING (and possibly
TRUENAME first, if you wish to resolve symbolic links and so on to
obtain the file's canonical name, and you know that the file exists).
Using CMUCL, you should be aware of two additional points.

CMUCL has an extension to Common Lisp pathnames, called search lists
(a somewhat incompatible extension, unfortunately, since their syntax
can be confused with that of logical pathnames). In your case, "home:"
is a predefined search list that designates the user's home directory.
See the CMUCL User's Manual for details on pathnames and search lists
in CMUCL.

The best solution to your problem is to use the CMUCL function
EXT:UNIX-NAMESTRING. This is similar to NAMESTRING, but does
additional checks to ensure that the result is a legal argument to a
Unix library call, such as checking that the filename doesn't contain
any embedded #\Nul characters.
  
-- 
Eric Marsden                          <URL:http://www.laas.fr/~emarsden/>
From: rif
Subject: Re: pathnames question
Date: 
Message-ID: <wj03ciq9e23.fsf@five-percent-nation.mit.edu>
> The best solution to your problem is to use the CMUCL function
> EXT:UNIX-NAMESTRING. This is similar to NAMESTRING, but does
> additional checks to ensure that the result is a legal argument to a
> Unix library call, such as checking that the filename doesn't contain
> any embedded #\Nul characters.

The specific issue I'm interested in is improving CLOCC's gunplot
interface, cllib/gnuplot.lisp.  As currently written, it outputs to a
file, defined by:

(defcustom *gnuplot-file* pathname (merge-pathnames "plot.tmp" *datadir*)
  "*The tmp file for gnuplot."))

where *datadir* is defined in cllib/base.lisp as 

(defcustom *datadir* pathname
  (mk-path (user-homedir-pathname) :directory '(:relative "data"))
  "The directory where the data file are created by default.")

The primary difficulty is that (at least under CMUCL, which is what I
have handy) cllib:*gnuplot-file* resolves to #p"home:data/plot.tmp",
and when gunplot manipulates it to get the output postscript file to
generate, you get a line /home/rif/data/plot.tmp that looks like:

set output 'home:data/plot.ps'

which gnuplot of course does not like.  

So what I really want to know is what is the correct general way to
solve this problem for CLOCC, rather than something CMUCL specific?
Will passing the path through truename before writing it out work on
all the major implementations?  Is it safe to rely on that?  Is there
a better way?

rif
From: Eric Marsden
Subject: Re: pathnames question
Date: 
Message-ID: <wzir869nbyn.fsf@melbourne.laas.fr>
>>>>> "rif" == rif  <···@mit.edu> writes:

  rif> So what I really want to know is what is the correct general
  rif> way to solve this problem for CLOCC, rather than something
  rif> CMUCL specific? Will passing the path through truename before
  rif> writing it out work on all the major implementations? Is it
  rif> safe to rely on that? Is there a better way?

using TRUENAME won't work, since presumably your file doesn't yet
exist. Unfortunately, I don't think there's a standard way of doing
what you need (obtaining an externally usable representation of a
pathname), other than Nikodemus' rather gross hack. Perhaps this is a
function that could be added to CLOCC. 
  
-- 
Eric Marsden                          <URL:http://www.laas.fr/~emarsden/>
From: rif
Subject: Re: pathnames question
Date: 
Message-ID: <wj0el29c22p.fsf@five-percent-nation.mit.edu>
Eric Marsden <········@laas.fr> writes:

> >>>>> "rif" == rif  <···@mit.edu> writes:
> 
>   rif> So what I really want to know is what is the correct general
>   rif> way to solve this problem for CLOCC, rather than something
>   rif> CMUCL specific? Will passing the path through truename before
>   rif> writing it out work on all the major implementations? Is it
>   rif> safe to rely on that? Is there a better way?
> 
> using TRUENAME won't work, since presumably your file doesn't yet
> exist. Unfortunately, I don't think there's a standard way of doing
> what you need (obtaining an externally usable representation of a
> pathname), other than Nikodemus' rather gross hack. Perhaps this is a
> function that could be added to CLOCC. 

That is unfortunate.  For cllib, I can think of two design
alternatives.  Which is better and why?

1. Require that pieces of cllib which need to have externally usable
   filenames simply don't generate filenames that include
   (user-homedir-pathname).  In the case of gnuplot, this would
   require the user manually setting the output file at least once
   before generating gnuplot files.

2. Higher up in cllib, where cllib:*datadir* is defined, apply
   truename immediately --- this seems to me OK under the (very mild)
   assumption that (user-homedir-pathname) will return a path to a
   directory that exists.  Then we might be OK merging this with other
   stuff, although we'll still (somehow) have to make sure that the
   pathname is essentially "string style".

Cheers,

rif
From: Nikodemus Siivola
Subject: Re: pathnames question
Date: 
Message-ID: <bbl336$5mf3e$1@midnight.cs.hut.fi>
rif <···@mit.edu> wrote:

> 1. Require that pieces of cllib which need to have externally usable
>    filenames simply don't generate filenames that include
>    (user-homedir-pathname).  In the case of gnuplot, this would

> 2. Higher up in cllib, where cllib:*datadir* is defined, apply
>    truename immediately --- this seems to me OK under the (very mild)

No 2. seems better. The assumption, as you say is pretty mild -- and if
the directory does not exist you can try to default somewhere else.

The no. 1 may not even be enough in the long run: a pathname requiring
resolution could (I'm assuming) conceivably slip in at a later stage
anyway, so this doesn't solve the larger issue anyway.

Cheers,

  -- Nikodemus
From: Paul Foley
Subject: Re: pathnames question
Date: 
Message-ID: <m2ptltpatc.fsf@mycroft.actrix.gen.nz>
On Wed, 04 Jun 2003 14:57:59 +0200, Eric Marsden wrote:

> CMUCL has an extension to Common Lisp pathnames, called search lists
> (a somewhat incompatible extension, unfortunately, since their syntax
> can be confused with that of logical pathnames).

Where/why do you see an incompatibility?

The way it's implemented on CMUCL is broken, though, because the
search-list is parsed as part of the directory component, rather than
the host, and the host comes from *default-pathname-defaults*, so
search-lists only work if (pathname-host *default-pathname-defaults*)
is the default host.

-- 
There is no reason for any individual to have a computer in their home.
                                           -- Ken Olson, President of DEC
(setq reply-to
  (concatenate 'string "Paul Foley " "<mycroft" '(··@) "actrix.gen.nz>"))
From: Mario S. Mommer
Subject: Re: pathnames question
Date: 
Message-ID: <fzr8684qxw.fsf@cupid.igpm.rwth-aachen.de>
rif <···@mit.edu> writes:
> How can I take a pathname that shows up in CL (CMUCL) as
> #p"home:data/foo.tmp" and get a string like "/home/rif/data/foo.tmp"
> (or the equivalent if the program's running in Windows)?  I need to
> get to a "basic string" in order to export the name to a non-CL
> program that's being scripted.
> 
> Cheers,
> 
> rif

I'm a bit puzzled that this answer did not come up before, so I'd like
to ask: What is wrong with the following?

(format nil "~A"
        (translate-logical-pathname #p"home:data;foo.tmp"))

Regards,
        Mario.
From: Nikodemus Siivola
Subject: Re: pathnames question
Date: 
Message-ID: <bbng9a$5rppg$1@midnight.cs.hut.fi>
Mario S. Mommer <········@yahoo.com> wrote:

> to ask: What is wrong with the following?
>
> (format nil "~A"
>         (translate-logical-pathname #p"home:data;foo.tmp"))

The question was about #p"home:data/foo.tmp" on CMUCL, which isn't a
logical pathname.

Cheers,

 -- Nikodemus