First code: Custom module loader - comments welcome

Started by Tim Johnson, December 19, 2007, 05:37:53 PM

Previous topic - Next topic

Tim Johnson

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")
Programmer since 1987. Unix environment.

cormullion

#1
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. :)

Tim Johnson

#2
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
Programmer since 1987. Unix environment.

Tim Johnson

#3
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))
 
Programmer since 1987. Unix environment.

Lutz

#4
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

Tim Johnson

#5
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
Programmer since 1987. Unix environment.