My very first newLisp code follows.
FYI: I do a lot of web programming. In some cases, I'm writing
for remote servers where I have complete control, including
secure shell and root access. In other cases, I enjoy the cooperation
of a sysadmin who will do anything for me as long as it is safe.
However, there are domain hosters who will not install a lesser-known
binary, such as rebol or newlisp at a system directory, but will allow
an install under a domain path, such as public_html, so I might
have newlisp running at /home/bin and modules at some arbitrary
directory path.
Thus I wanted an interface that was simple and took care of searching
paths in the background.
First code is the startup module itself. Second a simple CGI script
and lastly the output from the script.
I'd welcome comments and caveats that would further enlighten me
on coding techniques.
TIA
tim
;;---------------------------------------------------------------------------------------------
;; initialization code
;; Search in the following order
;; 1)current directory
;; 2)/usr/share/newlisp/modules/
;; 3)Any other hard-coded paths
(setq sys-path '(""
"/usr/share/newlisp/modules"
"/home/http/run/libraries/newlisp"))
;; -------------------------------------------------------------
;; If a 'symbol does not have an extension, add a default ".lsp"
;; -------------------------------------------------------------
(define (lsp-extend symbol)
(setq fname (string symbol)) ;; just in case a symbol is passed
(cond
((< (length(parse fname ".")) 2) ;; no extension
(append fname ".lsp")) ;; add default
(fname)))
;; -------------------------------------------------------------
;; search for file along directories in 'sys-path
;; return full path to file or nil
;; -------------------------------------------------------------
(define (path-found f)
(setq file (lsp-extend f)) ;; confirm extension
(dolist
(path sys-path) ;; search existing paths, same dir first
(if(= path "") ;; check wd first
(setq test (append (real-path) "/" file))
(setq test (append path "/" file))) ;; full path
(if (file? test) ;; we're outta here!
(throw test))))
;; -------------------------------------------------------------
;; If found, load file, if not throw error
;; -------------------------------------------------------------
(define (load-from-path file)
(setq res (catch(path-found file)))
(cond
(res (load res)) ;; path found
((throw-error ;; abort with error message
(append file " could not be found in 'sys-path"))))
res)
;; -------------------------------------------------------------
;; @module usr
;; @syntax (modules "module1 module2 module3 ...")
;; If a file name does not have an extension, .lsp is appended
;; @example (modules "cgi ftp zlib mylib")
;; @return list of loaded files
;; TODO:
;; add further arguments, thus module names are
;; 'words' in a string => 'modstring
;; provide code for a symbol list as an alternative to
;; a string of modules
;; -------------------------------------------------------------
(define (modules modstring)
(setq loaded '()) ;; list of loaded modules
(dolist
(ms (parse modstring))
(push ;; accumulate list of loaded modules
(load-from-path ms) loaded))
loaded)
CGI script
#!/usr/bin/newlisp -c
;; http://localhost/cgi-bin/newlisp/testcgi.lsp
(println "Content-type: text/htmln")
(define resources "cgi ftp cgi1")
(define (main)
(load "usr.lsp")
(setq loaded(modules resources))
(println "<br>$HOME: " $HOME)
(println "<br>Loaded: " loaded)
(exit))
(catch (main) 'err)
(if err(println "<pre>ERROR: " err))
Output:
$HOME: /home/http/
Loaded: ("/home/http/run/newlisp/cgi1.lsp" "/usr/share/newlisp/modules/ftp.lsp" "/usr/share/newlisp/modules/cgi.lsp")
Looks good to me!
I don't think I can run it easily here but I hope to see much more of your code in the future - anyone who can do CGI code easily is extra welcome. :)
I figure the way to learn newlisp is to build an application I've
always thought I could use, but have so far lived without - a
debug file browser.
Might make a good case study for someone else to follow - but
dunno...
I will try to make the next step a wrapper for the CGI context that
writes to a session specific debug file, dumping the cgi environment
and remains open for user-added info.
Something like:
(MGI:init) ;;
;; code...
(MGI:store "myvar: " myvar)
;; etc
(MGI:close)
Thanks
Tim
Rebol's 'do function, which is similar to newlisp's 'load function can
result in system resources being used up, if you happen to 'do two
modules each of which 'do the other.
I'm concerned that this could happen with newlisp. correct me if I
am wrong or being too cautious.
Following that thought, I've added code that maintains a "journal"
of loaded modules and prevents redundant loading unless
specifically called for.
I've also added a 'do function that allows the programmer to
explicitly force a reload.
The modules function was modified to take a second form:
a list of module symbols as opposed to a string of space-delimited
module names.
;; Initialization code
;; Search in the following order
;; 1)current directory
;; 2)/usr/share/newlisp/modules/
;; 3)Any other hard-coded paths
;; => Don't use same file twice unless explicitly called for.
(setq sys-path '(""
"/usr/share/newlisp/modules"
"/home/http/run/libraries/newlisp"))
(setq loaded '())
;; -------------------------------------------------------------
;; If a 'symbol does not have an extension, add a default ".lsp"
;; -------------------------------------------------------------
(define (lsp-extend symbol)
(setq fname (string symbol)) ;; just in case a symbol is passed
(cond
((< (length(parse fname ".")) 2) ;; no extension
(append fname ".lsp")) ;; add default
(fname)))
;; -------------------------------------------------------------
;; search for file along directories in 'sys-path
;; return full path to file or nil
;; -------------------------------------------------------------
(define (path-found f)
(setq file (lsp-extend f)) ;; confirm extension
(dolist
(path sys-path) ;; search existing paths, same dir first
(if(= path "") ;; check wd first
(setq test (append (real-path) "/" file))
(setq test (append path "/" file))) ;; full path
(if (file? test) ;; we're outta here!
(throw test))))
;; -------------------------------------------------------------
;; If found, load file, if not throw error. If 'reload, forced
;; reload. Forcing reload should only be done as a call from
;; (do)
;; -------------------------------------------------------------
(define (load-from-path file reload)
(setq res (catch(path-found file)))
(setq isloaded nil)
(setq doload (not (member res loaded))) ;; In sys-path already?
(setq dopush doload) ;; push only if not in sys-path
(if(= reload true)(setq doload true))
(cond
(res(if doload
(begin
(setq isloaded true)
(if dopush (push res loaded))
(load res)))) ;; path found
((throw-error ;; abort with error message
(append "module (" file ") could not be found in 'sys-path"))))
isloaded)
;; -------------------------------------------------------------
;; @module usr
;; @syntax (modules "module1 module2 module3 ...")
;; OR (modules (module1 module2 module3))
;; If a file name does not have an extension, .lsp is appended
;; @example (modules "cgi ftp zlib mylib")
;; @return list of loaded files
;; TODO:
;; add further arguments, thus module names are
;; 'words' in a string => 'modstring
;; -------------------------------------------------------------
(define (modules mods)
(setq modlist
(if(string? mods)
(parse mods)
mods))
(dolist
(ms modlist)
(load-from-path ms))
loaded)
;; -------------------------------------------------------------
;; @module usr
;; @syntax (do 'cgi) => only if not already loaded
;; (do 'cgi true) => force reload
;; Intent for reloading a module from the interpreter
;; command line. The second argument must be the symbol
;; true to force reload. An argument that evaluates to
;; true will be ignored for now.
;; -------------------------------------------------------------
(define (do modsym reload) (load-from-path modsym reload))
if you happen to 'do two modules each of which 'do the other.
Loading a module more than once, will not use up any more memory, the second load will just overwrite the first and automatically free the memory of the first load when overwriting definitions. But of course you could end up in a loop if two modules call each other.
Lutz
Quote from: "Lutz"
if you happen to 'do two modules each of which 'do the other.
But of course you could end up in a loop if two modules call each other.
Lutz
I only did that once - :) I swear - but it hosed a server. Thus my caution.
Thanks
Tim