Page 1 of 1

getting absolute pathname from pathname

Posted: Tue Mar 21, 2006 12:33 pm
by cormullion
I'm trying to find out what the 'real' pathname of a file would be. For example, given :

././temp.txt

I'd like to convert it to:

/Users/me/Documents/Junk/temp.txt

Is there a way to do this in newLISP? I found a Unix command 'realpath', but no way to do it without calling out using exec.

Posted: Wed Mar 22, 2006 1:34 pm
by pjot
Hi Cormullion,

With the command (directory) it is impossible to find the name of the current directory you are in. You only will see the files and subdirectories in the current directory. If you go 1 directory higher, it is impossible to see the name of the directory you came from.

With (directory?) you can make a query, and compare the contents of the current directory with the contents of a subdirectory. Still you are never sure if the queried directory is the same directory as you are in.

In other words: to get an absolute reference for "../temp.txt" without using '!' or 'exec' is very hard :-), if not impossible...

Peter


PS Lutz: the codesnippet "Show Directory Tree" at the "Tips&Tricks" section does not work.

Posted: Wed Mar 22, 2006 2:59 pm
by cormullion
Thanks, Peter. I'm hoping Lutz can find a way to do the impossible! ;-)

The show-tree example seems to work, if you call it with a directory:

(show-tree "/Users/me/Documents")

And, if you copy from the web page into a text editor like BBEdit, make sure you remove all the non-ASCII codes first!

Posted: Wed Mar 22, 2006 3:42 pm
by newdep
Well actualy you must interact with the shell eventualy i guess.. because thats
what newlisp does too (underwater)

The same goes for ~/ which could be intepreted as $HOME but it depends on the shell below..

But on both newlisp does not return a name.. neighter on ../../ nor on ~/ so you have to fiddle with the shell i guess.

I there is another way..Im pleased to see that ;-)

A way to do that is:

(exec "pwd") that returns you current working directory
Then count the ammount of "/" you awant to go back "../../"
(including current dir) If that is true with 'pwd" string output
then chop off those from your string. I think its quick with a regex..

Norman.

Posted: Wed Mar 22, 2006 4:17 pm
by cormullion
Perhaps I shouldn't worry about the shell calls as much - but I always thought that an 'exec' type call was going to be slower than a direct call. Perhaps there's too little speed difference to worry these days.

I don't think the (exec "pwd") will work. The working directory seems to stay the same for the whole script, even though a recursive function may be 20 levels down...

Posted: Wed Mar 22, 2006 6:27 pm
by Lutz
Try importing the 'C' function getwd() instead and supply a buffer parameter:

Code: Select all

newLISP v.8.8.0 on BSD, execute 'newlisp -h' for more info.

> (import "libc.so" "getwd") ; on MacOS X use "libc.dylib"
getcwd <2814C114>
> (set 'dir (dup "\000" 256))

> (get-string (getwd dir))
"/usr/home/lutz"
Lutz

ps: this works on both: MacOSX with libc.dylib and FreeBSD with libc.so

Posted: Wed Mar 22, 2006 6:40 pm
by pjot
Nice trick! On Slackware I have to do this:

Code: Select all


(set 'dir ".")
(import "/lib/libc-2.3.5.so" "getcwd")
(println (get-string (getcwd dir 256)))

But it is not secure, as the manpage mentions:
The pathname shall contain no components that are symbolic links.
Also it prints the location where I am currently, not where the script is. So if I am at "/home/peter" and I execute the script like "some/dir/else/script.lsp" the command will show "/home/peter". Then it is difficult to find the absolute path of a file called "../../file.txt".

The directory stuff uses <sys/types.h> and <dirent.h>. There is no API for finding the current dir of the script. In C programming you can parse argv[0] to find the current dir of a binary. But still it would show a "./bla" or something like that; so a relative pathname.

So if you do not startup your newLISP program with a full path, also (main-args) does not help you.

Maybe the only way is put your newLisp program in a fixed directory and put the location of this directory hard-coded into your program and take that as starting point to get a fixed dir. (Probably no programming language can workaround this?)

Peter

Posted: Wed Mar 22, 2006 7:32 pm
by Lutz
... but shouldn't you reserve memory for the 'dir' buffer? From the getcwd() man page:

"The getcwd() function copies the absolute pathname of the current working directory into the memory referenced by buf"

At least on MacOS X and FreeBSD getcwd() and getwd() copy the directory name into the buffer variable. Initializing with (set 'dir ".") will only reseve one byte of memory.

Lutz

Posted: Wed Mar 22, 2006 7:42 pm
by pjot
Ah yes, you are right there, I forgot to do that, it's also needed in Linux.... Still the program doesn't crash if I don't, even when allowing 256 bytes. That's weird.

Peter

Posted: Wed Mar 22, 2006 8:55 pm
by cormullion
Thanks! I think I can build the full pathname from the relative name I'm getting from the recursive scanning, and adding the working directory to the front of it. I think.

Looks good. Would make a nice addition to newLISP built-in library ... :-)

Posted: Wed Mar 22, 2006 9:47 pm
by Lutz
Yes, we will have a built-in function for it. It needs very little code to implement, too many people again and again have asked for it It will be 'cwd' for (c)urrent (w)working (d)directory

Lutz

Posted: Wed Mar 22, 2006 10:05 pm
by Lutz
... or perhaps it should be 'current-dir' to be more consistent with the naming of other directory functions.

Lutz

Posted: Thu Mar 23, 2006 11:12 am
by cormullion
Lutz wrote:... or perhaps it should be 'current-dir' to be more consistent with the naming of other directory functions.
Excellent, thanks!

Can you access the 'realpath' command using the same 'import' method that you gave above? man page starts:
NAME
realpath -- returns the canonicalized absolute pathname

LIBRARY
Standard C Library (libc, -lc)

SYNOPSIS
#include <sys/param.h>
#include <stdlib.h>

char *
realpath(const char *pathname, char resolved_path[PATH_MAX]);

Posted: Thu Mar 23, 2006 12:19 pm
by newdep
Actualy lutz... Perhaps you can bring that option under

or directory? or directory

That keeps then a little together..

I Like (directory? "cwd") more then (cwd)

Just a tip..



Uhum... perhpas not a good idea ;-)

> (directory? "cwd")
nil
> (make-dir "cwd")
true
> (directory? "cwd")
true
>

Posted: Thu Mar 23, 2006 12:59 pm
by Lutz
Can you access the 'realpath' command using the same 'import' method that you gave above?
yes ...

Code: Select all

> (import "libc.dylib" "realpath")
realpath <90048E40>
> (set 'res (dup "\000"256))

> (get-string (realpath "./newlisp" res))
"/Users/lutz/newlisp/newlisp"
> (get-string (realpath "." res))
"/Users/lutz/newlisp"
> 
may be 'realpath' has more usage than 'current-dir' taking the additional argument. One could make (realpath) the default for (realpath ".")?

But checking the history of 'realpath' it seems it did not appear until FreeBSD 4.3, which is fairly recent.

Lutz

Posted: Thu Mar 23, 2006 1:19 pm
by Lutz

Code: Select all

newLISP v.8.8.3 on OSX UTF-8, execute 'newlisp -h' for more info.

> (realpath)
"/Users/lutz/newlisp-8.8.3"
> (realpath "~/newlisp")
nil
> (realpath "./newlisp")
"/Users/lutz/newlisp-8.8.3/newlisp"
> (realpath "../newlisp")
"/Users/lutz/newlisp"
> (realpath "newlisp.c")
"/Users/lutz/newlisp-8.8.3/newlisp.c"
> (realpath ".")
"/Users/lutz/newlisp-8.8.3"
> (realpath "..")
"/Users/lutz"
> 
realpath history:

FreeBSD: 4.3
Linux: libc 4.5.4
The libc4 and libc5 implementation contains a buffer overflow (fixed in libc-5.4.13).
Solaris: at least since 1999

Lutz

Posted: Thu Mar 23, 2006 1:49 pm
by cormullion
Yes! I think that's great. Even easier to use than getwd/current-dir, and very useful for me.

Posted: Sat Apr 01, 2006 10:46 am
by statik
Any idea when (cwd) is gonna make into newlisp? Next release? Release after that? Eventually?

Also, PWD is an environtment variable, and you can clearly see that by doing the following at the command line:

Code: Select all

$ set
Is there a reason why (env) does not utilize ALL available environment variables?

Posted: Sat Apr 01, 2006 1:28 pm
by cormullion
Doesn't real-path do this (cwd) for you?

Posted: Sat Apr 01, 2006 1:45 pm
by Lutz
Yes, look into 'real-path in 8.8.3, its like a 'cwd' on steroids because it also translates relative paths (based on UNIX realpath). A 'cwd' would be (real-path) or (real-path "."), which is the same.
is there a reason why (env) does not utilize ALL available environment variables?
When Unix starts a process, i.e. newLISP, it supplies it only with a subset of environment variables. What you see using 'env' is all you have avaiable in your process space, but you could use the following method to get all environment variablers in the shell you are running:

Code: Select all

(map (fn (e) (parse e "=")) (exec "set"))
This gives you an association list:

Code: Select all

(
...
("BASH_VERSION" "'2.05b.0(1)-release'") 
("DIRSTACK" "()") 
("EUID" "501") 
("GROUPS" "()") 
("HOME" "/Users/lutz") 
...
)
and you could use 'lookup' on it to get a certain value:

Code: Select all

> (set 'my-env (map (fn (e) (parse e "=")) (exec "set")))

> (lookup "PPID" my-env)
"29039"
> 
Lutz

Posted: Sun Apr 02, 2006 3:55 am
by statik
I didn't realize you had implemented realpath into the latest dev... I'll check it out. Thanks.

Posted: Sun Apr 02, 2006 6:25 am
by statik
Does anyone know how I can get the cwd of the file being executed, or a way to get the cwd of any given process?

My problem is that realpath shows the working directory from where newlisp was started.

Example:

Code: Select all

$ pwd
/home/statik/
$ cat code/test.lsp
(println (real-path))
(exit)
$ newlisp code/test.lsp
/home/statik/
$
I'd like a way to find the current working directory of the script being executed. Any ideas?

Posted: Sun Apr 02, 2006 1:26 pm
by Lutz
The location of the script is always the second member in (main-args):

Code: Select all

~> pwd
/Users/lutz
~> cat /usr/bin/mytest
#!/usr/bin/newlisp

(println "real-path -> " (real-path))
(println "script caller -> " (main-args 0))
(println "script -> " (main-args 1))

(exit)

~> mytest

real-path -> /Users/lutz
script caller -> /usr/bin/newlisp
script -> /usr/bin/mytest
~> 
Lutz

Posted: Mon Apr 03, 2006 4:17 am
by statik
That's fine and all, but how does one handle files that are loaded via (load)? How do those file determine where they are?