Subject: [clisp-list] pname

From: Pascal Bourguignon <[email-masked]>
Date: Mon Mar 24 14:08:24 2003

Sorry  if I sound  like whinning,  but I've  got two  difficuties with
clisp used as a script interpreter:

- first, obtaining the  path of the "running" script  is only possible
  using  *LOAD-PATHNAME*,  considering  that  the  script  is  running
  whenever it's loading...

  The problem with this, is  that it's impossible to obtain the script
  path from a file being loaded from the script.  I was hopping to put
  something like:

(DEFPACKAGE "COM.INFORMATIMAGO.CLISP.SCRIPT"
  (:NICKNAMES "PJB-SCRIPT" "SCRIPT")
  (:DOCUMENTATION
   "This package exports script functions.")
  (:USE "COMMON-LISP")
  (:EXPORT  "PNAME" "IS-RUNNING" ;;...
   )) 
(IN-PACKAGE "COM.INFORMATIMAGO.CLISP.SCRIPT")

(DEFPARAMETER PNAME     (FILE-NAMESTRING *LOAD-PATHNAME*))
;; ...

  in  a  "pjb-script.lisp"  file  common  to all  my  scripts,  to  be
  sub-loaded from my scripts.

  What's more,  if I compile the  library packages and  the script and
  concatenate them into a  stand-alone compiled .fas script, obviously
  then *LOAD-PATHNAME* will be the wanted path name, the problem being
  that  one  cannot  use   *LOAD-PATHNAME*  to  get  the  same  result
  everytime. That is, the semantics  of *LOAD-PATHNAME* is not what is
  needed here, and clearly access to the path of the running script is
  missing.

  Would it  be possible to  have a ext:*arg0* or  ext:*pname* variable
  holding the path of the running script?

- my  second problem is  that I  can't find  a way  to know  whether a
  script is running or not.  That is, for debugging purpose, or other,
  I  need to  load the  lisp code  of a  script into  a  running lisp,
  without running the script.
  
  Well  here, the  first problem  is that  I have  to edit  the script
  because the '#!'  on the first line is not accepted.  I guess that I
  could add a reader  macro there, but I think it would  be nice if it
  was  something implemented  in  clisp  as a  part  of its  scripting
  support.

  Then, I'm trying to implement a pjb-scritp:is-running predicate that
  would tell me whether the script is being loaded via (load "script")
  or  whether it's  being  run via  '#!/usr/bin/clisp' mechanism.   It
  would be used as:

    (when (pjb-script:is-running) (main pjb-script:arguments))

  How one could implement this is-running predicate?
  
  It would be solved assuming  that ext:*pname* is nil when the script
  is not running (not loading via the #! mechanism).

-- 
__Pascal_Bourguignon__                   http://www.informatimago.com/
----------------------------------------------------------------------
Do not adjust your mind, there is a fault in reality.

---

From: Sam Steingold <[email-masked]>
Date: Mon Mar 24 16:54:04 2003

> * In message <[email-masked]>
> * On the subject of "[clisp-list] pname"
> * Sent on Mon, 24 Mar 2003 23:06:49 +0100 (CET)
> * Honorable Pascal Bourguignon <[email-masked]> writes:
>

(DEFPARAMETER PNAME     #.(FILE-NAMESTRING *LOAD-PATHNAME*))

>   was something implemented in clisp as a part of its scripting

how do you think CLISP loads your scripts?

it does this, in spvw.d:

(SET-DISPATCH-MACRO-CHARACTER #\# #\!
   #'SYS::UNIX-EXECUTABLE-READER)

>   How one could implement this is-running predicate?

e.g.,
(defun pjb-script:executable-reader (a b c)
  (sys::unix-executable-reader a b c))

(set-dispatch-macro-character #\# #\! #pjb-script:executable-reader)

(load "my-script")

(defun is-running ()
  (eq (get-dispatch-macro-character #\# #\!)
      #'SYS::UNIX-EXECUTABLE-READER))

the simple way to avoid this stupid trickery is to have

(defvar *running* t)

in each script and load scripts with

(defvar *running* nil)
(defun load-script (fn)
  (let ((*running* nil))
    (load fn)))

-- 
Sam Steingold (http://www.podval.org/~sds) running RedHat8 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>
Your mouse has moved - WinNT has to be restarted for this to take effect.

---

From: Pascal Bourguignon <[email-masked]>
Date: Mon Mar 24 19:04:06 2003

> (DEFPARAMETER PNAME     #.(FILE-NAMESTRING *LOAD-PATHNAME*))

This does not work no more.   In both cases, when the file where these
statements are placed  is not the script file, but  a file loaded from
the loaded/run  script, *LOAD-PATHNAME* contains the path  to the file
loaded from the loaded/run script, not the path to the script.

>    #'SYS::UNIX-EXECUTABLE-READER)

Thank you for  the pointer. (I'm affraid a lot  going into the sources
of clisp because  of them being programmed IMHO in  lower level than C
(all these pushSTACK/popSTACK etc)).

> the simple way to avoid this stupid trickery

I agree that it's quite a hack, but once hidden in a package, it would
work.

>  is to have

I  see a  big problem  with this  solution (apart  the smart  usage of
defvar),  it's that  it needs  something to  be done  in each  and all
script sources.  

So I would propose the following change in spvw.d:

    # execute:
    # (PROGN
    #   #+UNIX (SET-DISPATCH-MACRO-CHARACTER #\# #\!
    #            #'SYS::UNIX-EXECUTABLE-READER)
    #   (SETQ *LOAD-VERBOSE* NIL)
    #   (DEFPARAMETER *SCRIPT-IS-RUNNING* T)           ;; new
    #   (DEFPARAMETER *SCRIPT-NAME* argv_execute_file) ;; new
    #   (DEFPARAMETER *ARGS* argv_execute_args)
    #   (EXIT-ON-ERROR
    #     (APPEASE-CERRORS
    #       (LOAD argv_execute_file :EXTRA-FILE-TYPES ...)))
    #   (EXIT)
    # )

With:

   (DEFPARAMETER *SCRIPT-IS-RUNNING* NIL 
     "Whether a script is being loaded and run by '#!'.")

   (DEFPARAMETER *SCRIPT-NAME*       NIL
     "When non nil, it's a string containing the name of the script 
      being loaded and run (argv[0]).")

somewhere in EXT package.

This would offer a cleaner interface than the above propositions.

     EXT:*SCRIPT-IS-RUNNING*          ;; we are in a script
     EXT:*SCRIPT-NAME*                ;; argv[0]
     EXT:*ARGS*                       ;; ( argv[1] ... argv[argc-1] )

(Other names may be choosen).
-- 
__Pascal_Bourguignon__                   http://www.informatimago.com/
----------------------------------------------------------------------
Do not adjust your mind, there is a fault in reality.

---

From: Sam Steingold <[email-masked]>
Date: Tue Mar 25 07:21:01 2003

> * In message <[email-masked]>
> * On the subject of "Re: [clisp-list] pname"
> * Sent on Tue, 25 Mar 2003 04:02:48 +0100

you are confused.
if you put the #. version in your script and compile it, pname will be
the name of the script even after you concat it with other files.

e.g.

$ cat script1.fas script2.fas > script.fas
$ clisp script.fas

"script1.lisp" 
"script2.lisp" 
$ head -200 script*.lisp
==> script1.lisp <==
(defparameter *myname*
  #.(file-namestring (or *load-pathname* *compile-file-pathname*)))
(print *myname*)

==> script2.lisp <==
(defparameter *myname*
  #.(file-namestring (or *load-pathname* *compile-file-pathname*)))
(print *myname*)
$

> (all these pushSTACK/popSTACK etc)).

this is actually "pseudolisp" :-)

>     #   (DEFPARAMETER *SCRIPT-NAME* argv_execute_file) ;; new

you can achieve the same result with the code I suggested.
I see no need for this.

-- 
Sam Steingold (http://www.podval.org/~sds) running RedHat8 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>
If a train station is a place where a train stops, what's a workstation?

---

From: Pascal Bourguignon <[email-masked]>
Date: Tue Mar 25 09:53:22 2003

> the name of the script even after you concat it with other files.

No, I just did not explain myself clearly enough.

I'm intending to create a package exporting functions and variables to
be used  in scripts.   

The source of this package will be in a file named 'pjb-script.lisp'.

There  will  be  scripts  in  files named  'script1',  'script2',  ...
'scriptN'.

Each of these 'scriptI' files will contain:

     (require 'pjb-script) ;; or if you will: (load "pjb-script")
     ;; and stuff like:
     (when (pjb-script:is-running)
         (format t "~A: my args are: ~W~%" 
                   pjb-script:name pjb-script:arguments))

The point is to avoid  inserting in _duplicate_ in all these 'scriptI'
files   stuff  that   is   rather  _encapsulated_   and  _hidden_   in
'pjb-script.lisp'  from  which  a  nice  _abstracted_  _interface_  is
exported.  (I've  underlined some keywords  that should ring  a bell).
That means, that the only thing  that should need to be present in all
the scripts is a mere:

  (require 'pjb-script)

See note(*) below.

So whatever the incantation proposed, it  has to be designed to be put
in  the file  named 'pjb-script.lisp',  and to  work in  the following
conditions (with sample of what is expected):

    - when pjb-script.lisp  or pjb-script.fas is loaded  from a normal
      clisp process:

            $ clisp
            [1]> (load "pjb-script")
            T
            [2]> (script:is-running)
            NIL

    - when  pjb-script.lisp   or  pjb-script.fas  is   loaded  from  a
      "loading"  script loaded  (load  "scriptI) from  a normal  clisp
      process.

            $ clisp
            [1]> (load "scriptI")
            running=NIL name=NIL path=NIL 
            T
            [2]

    - when  pjb-script.lisp   or  pjb-script.fas  is   loaded  from  a
      "running" script invoked via the '#!' mechanism.

            -------(scriptI)--------
            #!/usr/bin/clisp
            (load "pjb-script")
            (format t "running=~A name=~A path=~A~%"
                     (script:is-running) script:name script:path)
            (when (script:is-running) (format t "doing script stuff...~%"))
            ------------------------
            $ /some/path/scriptI
            running=T name=scriptI path=/some/path/scriptI
            doing script stuff...

    - when pjb-script.fas is concatenated  to scriptI.fas into an '#!'
      executable and "run".
 
            $ mv scriptI scriptI.lisp
            $ clisp -q -c scriptI.lisp
            $ echo '#!/usr/bin/clisp' > scriptI
            $ cat pjb-script.fas >> scriptI
            $ cat scriptI.fas >> scriptI
            $ chmod 755 scriptI
            $ ./scriptI
            running=T name=scriptI path=./scriptI
            doing script stuff...

Notably,  in  the  last  case,  the  value  of  *LOAD-PATHNAME*  while
loading/evaluating  the  forms   copied  from  pjb-script.fas  IS  NOT
'pjb-script.fas' but  'scriptI', while in  all the other  cases, while
loading/evaluating the forms from pjb-script.lisp (or pjb-script.fas),
the value  of *LOAD-PATHNAME* is  pjb-script.lisp (or pjb-script.fas).
What's  more,  in  both  case,   what  I'm  interested  in,  it's  not
'pjb-script.lisp', but 'scriptI'.

> you can achieve the same result with the code I suggested.

Right now, to implement pjb-script.lisp, I must add to my .clisprc.lisp:

;; Awfull trick for pjb-script:is-running 
;; and to be able to load clisp scripts:
(DEFUN PJB-EXECUTABLE-READER (A B C) (SYS::UNIX-EXECUTABLE-READER A B C))
(SET-DISPATCH-MACRO-CHARACTER #\# #\! (FUNCTION PJB-EXECUTABLE-READER))

and I  must add a call  to a PJB-SCRIPT:INITIALIZE function  in all my
scripts (since this function is evaluated when we're "running"/loading
the 'scriptI' file, the value of *LOAD-PATHNAME* is correcty "scriptI"
which I can save for later reference).

So you can  see the two problems that should  motivate the addition of
the EXT:*SCRIPT-IS-RUNNING* and EXT:*SCRIPT-NAME* variables:

    - the implementation  of a PJB-SCRIPT:IS-RUNNING  feature, and the
      will to be able to load  clisp code in '#!'  scripts imposes the
      insertion of a ugly hack in the .clisprc.lisp of the users.

    - and the lack of  *SCRIPT-NAME* imposes the gratuituous insertion
      in all the scripts of (PJB-SCRIPT:INITALIZE).

Or, if you will, just look at the code around the two new "pseudolisp"
lines I  proposed.  You may notice  that there is  a strong unwaranted
asymetry  in the processing  of of  argv[]: why  argv[0] is  not saved
while argv[1]...argv[argc] is saved as EXT:*ARGS* ?

(*) Note:

    It should  be obvious  that no code  should be duplicated  for the
    pain to maintain it.

    The  importance of  the other  characteristics,  encapsulation and
    hidding the implementation  details behind an abstracted interface
    can be seen  if you realize that  it's the only way to  be able to
    switch  easily of  common-lisp  implementation.  The  name of  the
    package        in       'pjb-script.lisp'        is       actually
    COM.INFORMATIMAGO.CLISP.SCRIPT, with two nicknames: PJB-SCRIPT and
    SCRIPT.       Then     I'll      have     a      package     named
    COM.INFORMATIMAGO.CMUCL.SCRIPT,  with  a  nickname PJB-SCRIPT  and
    SCRIPT in a  file named pjb-script.lisp, but in  the directory for
    cmucl libraries.  (In my script, the package named used everywhere
    is actually  "SCRIPT").  So only  changing the #! line  would have
    the script run by another lisp.

    Anybody who is to develop serrious applications can only use pure,
    portable, Common-Lisp and modules that are portable to the various
    target implementations used by their customers or users. 
   
    In particular, I'm totally against using the #+/#- features.  This
    is like  the #ifdef/#endif  of C and  this leads  to unmaintenable
    mess.   Well, their  use  could be  restricted  to some  low-level
    package,   but   I'm   witnessing   that   they're   rather   used
    everywhere.  For example, in pseudo (a scheme in Common-Lisp):

    $ egrep -e '[#][-+]' *.lisp|sed -e 's/:.*//'|sort -u|wc -l
          8
    $ ls -l *.lisp | wc -l
         13

    On  the contrary,  I'd advocate  for the  definition  of low-level
    interfaces, implemented on  each target platform encapsulating the
    specifics of the platform  into a low-level package exporting that
    same interface.   These few low-level interface  packages all with
    the same interface  can then be used to  implement the application
    independantly of the target environment.