mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
merge in patches from rob browning
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@1374 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
@@ -56,7 +56,7 @@ build-flavor:
|
||||
@cd src; $(MAKE) ${FLAVOR}
|
||||
ln -sf gnucash.${FLAVOR} gnucash.bin
|
||||
ln -sf gnucash.${FLAVOR} gnucash-shell
|
||||
(cd share && ln -sf ../src/scm scm)
|
||||
(cd share && rm -f scm && ln -sf ../src/scm scm)
|
||||
|
||||
motif:
|
||||
${MAKE} FLAVOR=motif build-flavor
|
||||
|
||||
4
configure
vendored
4
configure
vendored
@@ -2618,7 +2618,7 @@ trap 'rm -fr `echo "Makefile
|
||||
src/guile/Makefile
|
||||
src/guile/gnucash.h
|
||||
src/scm/Makefile
|
||||
src/scm/startup.scm
|
||||
src/scm/startup/init.scm
|
||||
src/gnome/Makefile
|
||||
src/motif/Makefile
|
||||
src/qt/Makefile
|
||||
@@ -2731,7 +2731,7 @@ CONFIG_FILES=\${CONFIG_FILES-"Makefile
|
||||
src/guile/Makefile
|
||||
src/guile/gnucash.h
|
||||
src/scm/Makefile
|
||||
src/scm/startup.scm
|
||||
src/scm/startup/init.scm
|
||||
src/gnome/Makefile
|
||||
src/motif/Makefile
|
||||
src/qt/Makefile
|
||||
|
||||
@@ -233,7 +233,7 @@ AC_OUTPUT(Makefile
|
||||
src/guile/Makefile
|
||||
src/guile/gnucash.h
|
||||
src/scm/Makefile
|
||||
src/scm/startup.scm
|
||||
src/scm/startup/init.scm
|
||||
src/gnome/Makefile
|
||||
src/motif/Makefile
|
||||
src/qt/Makefile
|
||||
|
||||
2
gnucash
2
gnucash
@@ -1,7 +1,7 @@
|
||||
#! /bin/sh
|
||||
|
||||
GNC_ARGS="${GNC_ARGS} --debug"
|
||||
GNC_ARGS="${GNC_ARGS} --startup-file ./share/scm/startup.scm"
|
||||
GNC_ARGS="${GNC_ARGS} --startup-dir ./share/scm/startup"
|
||||
GNC_ARGS="${GNC_ARGS} --share-dir ./share"
|
||||
GNC_ARGS="${GNC_ARGS} --config-dir ./etc"
|
||||
GNC_ARGS="${GNC_ARGS} --load-path (\"(./share/scm)\")"
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
The startup process looks like this right now:
|
||||
|
||||
1) (at the C level) search args from right to left to see if there's
|
||||
any --startup-file override, use it if there is.
|
||||
any --startup-dir override, use it if there is.
|
||||
|
||||
2) load the desired startup.scm (either the default or the override)
|
||||
-- all the rest of the steps happen from startup.scm
|
||||
2) load the desired <startup-dir>/init.scm (either the default or
|
||||
the override) -- all the rest of the steps happen from init.scm
|
||||
and files loaded from there. Any files in <startup>/ cannot be
|
||||
overridden by --load-path because they'll be loaded before the
|
||||
load-path mechanism is operational.
|
||||
|
||||
3) setup the default config-vars (including the ones that represent
|
||||
the command-line options).
|
||||
|
||||
@@ -1,572 +0,0 @@
|
||||
;;;; startup.scm -*-scheme-*-
|
||||
;;
|
||||
;; Minimal startup code. This file should just contain enough code to
|
||||
;; get the arguments parsed and things like gnc:*load-path* set up.
|
||||
;; After that *everything* should be loaded via gnc:load.
|
||||
|
||||
;; This load should go away when guile gets fixed. as of guile1.3,
|
||||
;; it's not. You have to do this manually, unless you call scm_shell,
|
||||
;; which we can't.
|
||||
(let ((boot-file (if (assoc 'prefix %guile-build-info)
|
||||
(string-append (cdr (assoc 'prefix %guile-build-info))
|
||||
"/share/guile/"
|
||||
(version)
|
||||
"/ice-9/boot-9.scm")
|
||||
"/usr/share/guile/1.3a/ice-9/boot-9.scm")))
|
||||
(primitive-load boot-file))
|
||||
|
||||
;; Automatically generated defaults...
|
||||
(define gnc:_config-dir-default_ "@GNC_RUNTIME_CONFIGDIR@")
|
||||
(define gnc:_share-dir-default_ "@GNC_RUNTIME_SHAREDIR@")
|
||||
|
||||
(define gnc:*command-line-files* #f)
|
||||
|
||||
;;;; Warning functions...
|
||||
|
||||
(define (gnc:warn . items)
|
||||
(display "gnucash: [W] ")
|
||||
(for-each (lambda (i) (display i)) items)
|
||||
(newline))
|
||||
|
||||
(define (gnc:error . items)
|
||||
(display "gnucash: [E] ")
|
||||
(for-each (lambda (i) (display i)) items)
|
||||
(newline))
|
||||
|
||||
(define (gnc:msg . items)
|
||||
(display "gnucash: [M] ")
|
||||
(for-each (lambda (i) (display i)) items)
|
||||
(newline))
|
||||
|
||||
(define (gnc:debug . items)
|
||||
(if (gnc:config-var-value-get gnc:*debugging?*)
|
||||
(begin
|
||||
(display "gnucash: [D] ")
|
||||
(for-each (lambda (i) (display i)) items)
|
||||
(newline))))
|
||||
|
||||
(define (gnc:startup)
|
||||
(gnc:debug "starting up.")
|
||||
#t)
|
||||
|
||||
(define (gnc:shutdown exit-status)
|
||||
(gnc:debug "Shutdown -- exit-status: " exit-status)
|
||||
|
||||
(gnc:hook-run-danglers gnc:*shutdown-hook*)
|
||||
(_gnc_shutdown_ exit-status)
|
||||
(exit exit-status))
|
||||
|
||||
(define gnc:*user-config-loaded?* #f)
|
||||
|
||||
(define (gnc:load-user-config)
|
||||
(gnc:debug "loading user configuration")
|
||||
|
||||
(let ((user-file
|
||||
(string-append (getenv "HOME") "/.gnucash/config.user"))
|
||||
(auto-file
|
||||
(string-append (getenv "HOME") "/.gnucash/config.auto")))
|
||||
|
||||
(if (access? user-file F_OK)
|
||||
(if (false-if-exception (primitive-load user-file))
|
||||
(set! gnc:user-config-loaded #t)
|
||||
(begin
|
||||
(gnc:warn "failure loading " user-file)
|
||||
#f))
|
||||
(if (access? auto-file F_OK)
|
||||
(if (false-if-exception (primitive-load auto-file))
|
||||
(set! gnc:*user-config-loaded?* #t)
|
||||
(begin
|
||||
(gnc:warn "failure loading " auto-file)
|
||||
#f))))))
|
||||
|
||||
(define gnc:*system-config-loaded?* #f)
|
||||
|
||||
(define (gnc:load-system-config)
|
||||
(gnc:debug "loading system configuration")
|
||||
|
||||
(let ((system-config (string-append
|
||||
(gnc:config-var-value-get gnc:*config-dir*)
|
||||
"/config")))
|
||||
|
||||
(if (false-if-exception (primitive-load system-config))
|
||||
(set! gnc:*system-config-loaded?* #t)
|
||||
(begin
|
||||
(gnc:warn "failure loading " system-config)
|
||||
#f))))
|
||||
|
||||
(define gnc:_load-path-directories_ #f)
|
||||
(define gnc:_doc-path-directories_ #f)
|
||||
|
||||
(define (directory? path)
|
||||
;; This follows symlinks normally.
|
||||
(let* ((status (false-if-exception (stat path)))
|
||||
(type (if status (stat:type status) #f)))
|
||||
(eq? type 'directory)))
|
||||
|
||||
|
||||
(define (gnc:directory-subdirectories dir-name)
|
||||
;; Return a recursive list of the subdirs of dir-name, including
|
||||
;; dir-name. Follow symlinks.
|
||||
|
||||
(let ((dir-port (opendir dir-name)))
|
||||
(if (not dir-port)
|
||||
#f
|
||||
(do ((item (readdir dir-port) (readdir dir-port))
|
||||
(dirs '()))
|
||||
((eof-object? item) (reverse dirs))
|
||||
|
||||
(if (not (or (string=? item ".")
|
||||
(string=? item "..")))
|
||||
(let* ((full-path (string-append dir-name "/" item)))
|
||||
;; ignore symlinks, etc.
|
||||
(if (access? full-path F_OK)
|
||||
(let* ((status (lstat full-path))
|
||||
(type (if status (stat:type status) #f)))
|
||||
(if (and (eq? type 'directory))
|
||||
(set! dirs
|
||||
(cons full-path
|
||||
(append
|
||||
(gnc:directory-subdirectories full-path)
|
||||
dirs))))))))))))
|
||||
|
||||
(define (gnc:_path-expand_ items default-items)
|
||||
(if
|
||||
(null? items)
|
||||
'()
|
||||
(let ((item (car items))
|
||||
(other-items (cdr items)))
|
||||
(cond
|
||||
((eq? item 'default)
|
||||
(append
|
||||
(gnc:_path-expand_ default-items))
|
||||
(gnc:_path-expand_ other-items default-items))
|
||||
((string? item)
|
||||
(if (and (char=? #\( (string-ref item 0))
|
||||
(char=? #\) (string-ref item (- (string-length item) 1))))
|
||||
|
||||
(let ((current-dir
|
||||
(make-shared-substring item 1 (- (string-length item) 1))))
|
||||
|
||||
(if (directory? current-dir)
|
||||
(let ((subdirs (gnc:directory-subdirectories current-dir))
|
||||
(rest (gnc:_path-expand_ other-items default-items)))
|
||||
(cons current-dir (append subdirs rest)))
|
||||
(begin
|
||||
(gnc:warn "Ignoring non-directory " current-dir
|
||||
" in gnc:_path-expand_ item.")
|
||||
(gnc:_path-expand_ other-items default-items))))
|
||||
(if (directory? item)
|
||||
(begin
|
||||
(gnc:warn "Ignoring non-directory " item
|
||||
" in gnc:_path-expand_ item.")
|
||||
(gnc:_path-expand_ other-items default-items))
|
||||
(cons item (gnc:_path-expand_ other-items default-items)))))
|
||||
(else (gnc:warn "Ignoring invalid item " item " in gnc:_path-expand_.")
|
||||
(gnc:_path-expand_ other-items default-items))))))
|
||||
|
||||
(define (gnc:_load-path-update_ var items)
|
||||
(gnc:msg "Updating load path from " items)
|
||||
(let ((result (gnc:_path-expand_
|
||||
items
|
||||
(gnc:config-var-default-value-get var))))
|
||||
(if result
|
||||
(begin
|
||||
(set! gnc:_load-path-directories_ result)
|
||||
(gnc:msg " Setting path to " result)
|
||||
result)
|
||||
(begin
|
||||
(gnc:msg " No path... " result)
|
||||
#f))))
|
||||
|
||||
(define (gnc:_doc-path-update_ var items)
|
||||
(let ((result (gnc:_path-expand_
|
||||
items
|
||||
(gnc:config-var-default-value-get var))))
|
||||
(if result
|
||||
(begin
|
||||
(set! gnc:_doc-path-directories_ result)
|
||||
result)
|
||||
#f)))
|
||||
|
||||
(define (gnc:find-in-directories file directories)
|
||||
"Find file named 'file' anywhere in 'directories'. 'file' must be a
|
||||
string and 'directories' must be a list of strings."
|
||||
|
||||
(gnc:debug "gnc:find-in-directories looking for " file " in " directories)
|
||||
|
||||
(do ((rest directories (cdr rest))
|
||||
(finished? #f)
|
||||
(result #f))
|
||||
((or (null? rest) finished?) result)
|
||||
|
||||
(let ((file-name (string-append (car rest) "/" file)))
|
||||
(gnc:debug " checking for " file-name)
|
||||
(if (access? file-name F_OK)
|
||||
(begin
|
||||
(gnc:debug "found file " file-name)
|
||||
(set! finished? #t)
|
||||
(set! result file-name))))))
|
||||
|
||||
;; It may make sense to dump this in favor of guile's load-path later,
|
||||
;; but for now this works, and having gnc things separate may be less
|
||||
;; confusing and avoids shadowing problems.
|
||||
|
||||
(define (gnc:load name)
|
||||
"Name must be a string. The system attempts to locate the file of
|
||||
the given name and load it. The system will attempt to locate the
|
||||
file in all of the directories specified by gnc:*load-path*."
|
||||
|
||||
(let ((file-name (gnc:find-in-directories name gnc:_load-path-directories_)))
|
||||
(if (not file-name)
|
||||
#f
|
||||
(if (false-if-exception (primitive-load file-name))
|
||||
(begin
|
||||
(gnc:debug "loaded file " file-name)
|
||||
#t)
|
||||
(begin
|
||||
(gnc:warn "failure loading " file-name)
|
||||
#f)))))
|
||||
|
||||
;;; config-var: You can create them, set values, find out of the value
|
||||
;;; is different from the default, and you can get a description. You
|
||||
;;; can also specify an action function which will be called whenever
|
||||
;;; the value is changed. The action function receives the special
|
||||
;;; var and the new value as arguments and should return either #f if
|
||||
;;; the modification should be rejected, or a list containing the
|
||||
;;; result otherwise.
|
||||
|
||||
;;; Finally, a config var has two states, "officially" modified, and
|
||||
;;; unofficially modified. You control what kind of modification
|
||||
;;; you're making with the second argument to
|
||||
;;; gnc:config-var-value-set! The idea is that options specified on
|
||||
;;; the command line will set the value of these config vars, but that
|
||||
;;; setting is considered transient. Other settings (like from the UI
|
||||
;;; preferences panel, or normal user code) should be considered
|
||||
;;; permanent, and if they leave the variable value different from the
|
||||
;;; default, should be saved to ~/.gnucash/config.auto.
|
||||
|
||||
(define (gnc:make-config-var description
|
||||
set-action-func
|
||||
equality-func
|
||||
default)
|
||||
(let ((var
|
||||
(vector description set-action-func equality-func #f default default)))
|
||||
(gnc:config-var-value-set! var #f default)
|
||||
var))
|
||||
|
||||
(define (gnc:config-var-description-get var) (vector-ref var 0))
|
||||
|
||||
(define (gnc:config-var-action-func-get var) (vector-ref var 1))
|
||||
|
||||
(define (gnc:config-var-equality-func-get var) (vector-ref var 2))
|
||||
|
||||
(define (gnc:config-var-modified? var) (vector-ref var 3))
|
||||
(define (gnc:config-var-modified?-set! var value) (vector-set! var 3 value))
|
||||
|
||||
(define (gnc:config-var-default-value-get var) (vector-ref var 4))
|
||||
(define (gnc:config-var-default-value-set! var value) (vector-set! var 4 value))
|
||||
|
||||
(define (gnc:config-var-value-get var) (vector-ref var 5))
|
||||
(define (gnc:config-var-value-set! var is-config-mod? value)
|
||||
(let ((set-action (gnc:config-var-action-func-get var))
|
||||
(result (list value)))
|
||||
(if set-action (set! result (set-action var value)))
|
||||
(if result
|
||||
(begin
|
||||
(if is-config-mod? (gnc:config-var-modified?-set var #t))
|
||||
(vector-set! var 5 (car result))))))
|
||||
|
||||
(define (gnc:config-var-value-is-default? var)
|
||||
(if (not (gnc:config-var-modified? var))
|
||||
#t
|
||||
(let (equal-values? gnc:config-var-equality-func-get var)
|
||||
(equal-values?
|
||||
(gnc:config-var-default-value-get var)
|
||||
(gnc:config-var-value-get var)))))
|
||||
|
||||
|
||||
;;;; Preferences
|
||||
|
||||
(define gnc:*arg-show-usage*
|
||||
(gnc:make-config-var
|
||||
"Generate an argument summary."
|
||||
(lambda (var value) (if (boolean? value) (list value) #f))
|
||||
eq?
|
||||
#f))
|
||||
|
||||
(define gnc:*arg-show-help*
|
||||
(gnc:make-config-var
|
||||
"Generate an argument summary."
|
||||
(lambda (var value) (if (boolean? value) (list value) #f))
|
||||
eq?
|
||||
#f))
|
||||
|
||||
(define gnc:*debugging?*
|
||||
(gnc:make-config-var
|
||||
"Enable debugging code."
|
||||
(lambda (var value) (if (boolean? value) (list value) #f))
|
||||
eq?
|
||||
#f))
|
||||
|
||||
(define gnc:*startup-file*
|
||||
(gnc:make-config-var
|
||||
"Initial lowest level scheme startup file."
|
||||
(lambda (var value)
|
||||
;; You can't change the startup file from here since this is the
|
||||
;; startup file...
|
||||
#f)
|
||||
string=?
|
||||
gnc:_startup-file-default_))
|
||||
|
||||
(define gnc:*config-dir*
|
||||
(gnc:make-config-var
|
||||
"Configuration directory."
|
||||
(lambda (var value) (if (string? value) (list value) #f))
|
||||
string=?
|
||||
gnc:_config-dir-default_))
|
||||
|
||||
(define gnc:*share-dir*
|
||||
(gnc:make-config-var
|
||||
"Shared files directory."
|
||||
(lambda (var value) (if (string? value) (list value) #f))
|
||||
string=?
|
||||
gnc:_share-dir-default_))
|
||||
|
||||
(define gnc:*load-path*
|
||||
(gnc:make-config-var
|
||||
"A list of strings indicating the load path for (gnc:load name).
|
||||
Any path element enclosed in parentheses will automatically be
|
||||
expanded to that directory and all its subdirectories whenever this
|
||||
variable is modified. The symbol element default will expand to the default directory. i.e. (gnc:config-var-value-set! gnc:*load-path* '(\"/my/dir/\" default))"
|
||||
(lambda (var value)
|
||||
(if (not (list? value))
|
||||
#f
|
||||
(let ((result (gnc:_load-path-update_ var value)))
|
||||
(if (list? result)
|
||||
(list result)
|
||||
#f))))
|
||||
equal?
|
||||
(list
|
||||
(string-append "(" (getenv "HOME") "/.gnucash/scm)")
|
||||
(string-append "(" gnc:_share-dir-default_ "/scm)"))))
|
||||
|
||||
(define gnc:*doc-path*
|
||||
(gnc:make-config-var
|
||||
"A list of strings indicating where to look for html and parsed-html files
|
||||
Any path element enclosed in parentheses will automatically be
|
||||
expanded to that directory and all its subdirectories whenever this
|
||||
variable is modified. The symbol element default will expand to the
|
||||
default directory. i.e. (gnc:config-var-value-set! gnc:*doc-path*
|
||||
'(\"/my/dir/\" default))"
|
||||
(lambda (var value)
|
||||
(if (not (list? value))
|
||||
#f
|
||||
(let ((result (gnc:_doc-path-update_ var value)))
|
||||
(if (list? result)
|
||||
(list result)
|
||||
#f))))
|
||||
equal?
|
||||
(list
|
||||
(string-append "(" (getenv "HOME") "/.gnucash/doc)")
|
||||
(string-append "(" gnc:_share-dir-default_ "/Docs)")
|
||||
(string-append "(" gnc:_share-dir-default_ "/Reports)"))))
|
||||
|
||||
(define gnc:*prefs*
|
||||
(list
|
||||
|
||||
(cons
|
||||
"usage"
|
||||
(cons 'boolean
|
||||
(lambda (val)
|
||||
(gnc:config-var-value-set! gnc:*arg-show-usage* #f val))))
|
||||
(cons
|
||||
"help"
|
||||
(cons 'boolean
|
||||
(lambda (val)
|
||||
(gnc:config-var-value-set! gnc:*arg-show-help* #f val))))
|
||||
(cons
|
||||
"debug"
|
||||
(cons 'boolean
|
||||
(lambda (val)
|
||||
(gnc:config-var-value-set! gnc:*debugging?* #f val))))
|
||||
|
||||
(cons
|
||||
"startup-file"
|
||||
(cons 'string
|
||||
(lambda (val)
|
||||
(gnc:config-var-value-set! gnc:*startup-file* #f val))))
|
||||
|
||||
(cons
|
||||
"config-dir"
|
||||
(cons 'string
|
||||
(lambda (val)
|
||||
(gnc:config-var-value-set! gnc:*config-dir* #f val))))
|
||||
|
||||
(cons
|
||||
"share-dir"
|
||||
(cons 'string
|
||||
(lambda (val)
|
||||
(gnc:config-var-value-set! gnc:*share-dir* #f val))))
|
||||
|
||||
|
||||
(cons
|
||||
"load-path"
|
||||
(cons 'string
|
||||
(lambda (val)
|
||||
(let ((path-list
|
||||
(call-with-input-string val (lambda (port) (read port)))))
|
||||
(if (list? path-list)
|
||||
(gnc:config-var-value-set! gnc:*load-path* #f path-list)
|
||||
(begin
|
||||
(gnc:error "non-list given for --load-path: " val)
|
||||
(gnc:shutdown 1)))))))
|
||||
|
||||
(cons
|
||||
"doc-path"
|
||||
(cons 'string
|
||||
(lambda (val)
|
||||
(let ((path-list
|
||||
(call-with-input-string val (lambda (port) (read port)))))
|
||||
(if (list? path-list)
|
||||
(gnc:config-var-value-set! gnc:*doc-path* #f path-list)
|
||||
(begin
|
||||
(gnc:error "non-list given for --doc-path: " val)
|
||||
(gnc:shutdown 1)))))))
|
||||
|
||||
(cons "load-user-config" (cons 'boolean gnc:load-user-config))
|
||||
(cons "load-system-config" (cons 'boolean gnc:load-system-config))))
|
||||
|
||||
|
||||
;; also "-c"
|
||||
|
||||
|
||||
(define (gnc:cmd-line-get-boolean-arg args)
|
||||
;; --arg means #t
|
||||
;; --arg true means #t
|
||||
;; --arg false means #f
|
||||
|
||||
(if (not (pair? args))
|
||||
;; Special case of end of list
|
||||
(list #t args)
|
||||
(let ((arg (car args)))
|
||||
(if (string=? arg "false")
|
||||
(list #f (cdr args))
|
||||
(list #t
|
||||
(if (string=? arg "true")
|
||||
(cdr args)
|
||||
args))))))
|
||||
|
||||
(define (gnc:cmd-line-get-integer-arg args)
|
||||
(let ((arg (car args)))
|
||||
(let ((value (string->number arg)))
|
||||
(if (not value)
|
||||
#f
|
||||
(if (not (exact? value))
|
||||
#f
|
||||
(list value (cdr args)))))))
|
||||
|
||||
(define (gnc:cmd-line-get-string-arg args)
|
||||
(list (car args) (cdr args)))
|
||||
|
||||
(define (gnc:prefs-show-usage)
|
||||
(display "usage: gnucash [ option ... ] [ datafile ]") (newline))
|
||||
|
||||
|
||||
(define (gnc:handle-command-line-args)
|
||||
(gnc:debug "handling command line arguments")
|
||||
|
||||
(let ((files-to-open '())
|
||||
(result #t))
|
||||
|
||||
(do ((rest (cdr (program-arguments))) ; initial cdr skips argv[0]
|
||||
(quit? #f)
|
||||
(item #f))
|
||||
((or quit? (null? rest)))
|
||||
|
||||
(set! item (car rest))
|
||||
|
||||
(gnc:debug "handling arg " item)
|
||||
|
||||
(if (not (string=? "--" (make-shared-substring item 0 2)))
|
||||
(begin
|
||||
(gnc:debug "non-option " item ", assuming file")
|
||||
(set! rest (cdr rest))
|
||||
(set! files-to-open (cons item files-to-open)))
|
||||
|
||||
;; Got something that looks like an option...
|
||||
(let* ((arg-string (make-shared-substring item 2))
|
||||
(arg-def (assoc-ref gnc:*prefs* arg-string)))
|
||||
|
||||
(if (not arg-def)
|
||||
(begin
|
||||
(gnc:prefs-show-usage)
|
||||
(set! result #f)
|
||||
(set! quit? #t))
|
||||
|
||||
(let* ((arg-type (car arg-def))
|
||||
(arg-parse-result
|
||||
(case arg-type
|
||||
((boolean) (gnc:cmd-line-get-boolean-arg (cdr rest)))
|
||||
((string) (gnc:cmd-line-get-string-arg (cdr rest)))
|
||||
((integer)
|
||||
(gnc:cmd-line-get-integer-arg (cdr rest)))
|
||||
(else
|
||||
(gnc:error "bad argument type " arg-type ".")
|
||||
(gnc:shutdown 1)))))
|
||||
|
||||
(if (not arg-parse-result)
|
||||
(begin
|
||||
(set result #f)
|
||||
(set! quit? #t))
|
||||
(let ((parsed-value (car arg-parse-result))
|
||||
(remaining-args (cadr arg-parse-result)))
|
||||
((cdr arg-def) parsed-value)
|
||||
(set! rest remaining-args))))))))
|
||||
(if result
|
||||
(gnc:debug "files to open: " files-to-open))
|
||||
|
||||
(set! gnc:*command-line-files* files-to-open)
|
||||
|
||||
result))
|
||||
|
||||
;;;; Now the fun begins.
|
||||
|
||||
(gnc:startup)
|
||||
|
||||
(if (not (gnc:handle-command-line-args))
|
||||
(gnc:shutdown 1))
|
||||
|
||||
;;; Now we can load a bunch of files.
|
||||
|
||||
(gnc:load "hooks.scm")
|
||||
(gnc:load "doc.scm")
|
||||
|
||||
;;; Load the system and user configs
|
||||
|
||||
(if (not gnc:*user-config-loaded?*)
|
||||
(if (not (gnc:load-system-config))
|
||||
(gnc:shutdown 1)))
|
||||
|
||||
(if (not gnc:*system-config-loaded?*)
|
||||
(if (not (gnc:load-user-config))
|
||||
(gnc:shutdown 1)))
|
||||
|
||||
(gnc:hook-run-danglers gnc:*startup-hook*)
|
||||
|
||||
(if (or (gnc:config-var-value-get gnc:*arg-show-usage*)
|
||||
(gnc:config-var-value-get gnc:*arg-show-help*))
|
||||
(begin
|
||||
(gnc:prefs-show-usage)
|
||||
(gnc:shutdown 0)))
|
||||
|
||||
(if (not (= (gnucash_lowlev_app_init) 0))
|
||||
(gnc:shutdown 0))
|
||||
|
||||
(if (pair? gnc:*command-line-files*)
|
||||
;; You can only open single files right now...
|
||||
(gnucash_ui_open_file (car gnc:*command-line-files*))
|
||||
(gnucash_ui_select_file))
|
||||
|
||||
(gnucash_lowlev_app_main)
|
||||
|
||||
(gnc:shutdown 0)
|
||||
93
src/scm/startup/command-line.scm
Normal file
93
src/scm/startup/command-line.scm
Normal file
@@ -0,0 +1,93 @@
|
||||
|
||||
;; also "-c"
|
||||
|
||||
(define gnc:*command-line-files* #f)
|
||||
|
||||
(define (gnc:cmd-line-get-boolean-arg args)
|
||||
;; --arg means #t
|
||||
;; --arg true means #t
|
||||
;; --arg false means #f
|
||||
|
||||
(if (not (pair? args))
|
||||
;; Special case of end of list
|
||||
(list #t args)
|
||||
(let ((arg (car args)))
|
||||
(if (string=? arg "false")
|
||||
(list #f (cdr args))
|
||||
(list #t
|
||||
(if (string=? arg "true")
|
||||
(cdr args)
|
||||
args))))))
|
||||
|
||||
(define (gnc:cmd-line-get-integer-arg args)
|
||||
(let ((arg (car args)))
|
||||
(let ((value (string->number arg)))
|
||||
(if (not value)
|
||||
#f
|
||||
(if (not (exact? value))
|
||||
#f
|
||||
(list value (cdr args)))))))
|
||||
|
||||
(define (gnc:cmd-line-get-string-arg args)
|
||||
(list (car args) (cdr args)))
|
||||
|
||||
(define (gnc:prefs-show-usage)
|
||||
(display "usage: gnucash [ option ... ] [ datafile ]") (newline))
|
||||
|
||||
|
||||
(define (gnc:handle-command-line-args)
|
||||
(gnc:debug "handling command line arguments")
|
||||
|
||||
(let ((files-to-open '())
|
||||
(result #t))
|
||||
|
||||
(do ((rest (cdr (program-arguments))) ; initial cdr skips argv[0]
|
||||
(quit? #f)
|
||||
(item #f))
|
||||
((or quit? (null? rest)))
|
||||
|
||||
(set! item (car rest))
|
||||
|
||||
(gnc:debug "handling arg " item)
|
||||
|
||||
(if (not (string=? "--" (make-shared-substring item 0 2)))
|
||||
(begin
|
||||
(gnc:debug "non-option " item ", assuming file")
|
||||
(set! rest (cdr rest))
|
||||
(set! files-to-open (cons item files-to-open)))
|
||||
|
||||
;; Got something that looks like an option...
|
||||
(let* ((arg-string (make-shared-substring item 2))
|
||||
(arg-def (assoc-ref gnc:*prefs* arg-string)))
|
||||
|
||||
(if (not arg-def)
|
||||
(begin
|
||||
(gnc:prefs-show-usage)
|
||||
(set! result #f)
|
||||
(set! quit? #t))
|
||||
|
||||
(let* ((arg-type (car arg-def))
|
||||
(arg-parse-result
|
||||
(case arg-type
|
||||
((boolean) (gnc:cmd-line-get-boolean-arg (cdr rest)))
|
||||
((string) (gnc:cmd-line-get-string-arg (cdr rest)))
|
||||
((integer)
|
||||
(gnc:cmd-line-get-integer-arg (cdr rest)))
|
||||
(else
|
||||
(gnc:error "bad argument type " arg-type ".")
|
||||
(gnc:shutdown 1)))))
|
||||
|
||||
(if (not arg-parse-result)
|
||||
(begin
|
||||
(set result #f)
|
||||
(set! quit? #t))
|
||||
(let ((parsed-value (car arg-parse-result))
|
||||
(remaining-args (cadr arg-parse-result)))
|
||||
((cdr arg-def) parsed-value)
|
||||
(set! rest remaining-args))))))))
|
||||
(if result
|
||||
(gnc:debug "files to open: " files-to-open))
|
||||
|
||||
(set! gnc:*command-line-files* files-to-open)
|
||||
|
||||
result))
|
||||
57
src/scm/startup/config-var.scm
Normal file
57
src/scm/startup/config-var.scm
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
;;; config-var: You can create them, set values, find out of the value
|
||||
;;; is different from the default, and you can get a description. You
|
||||
;;; can also specify an action function which will be called whenever
|
||||
;;; the value is changed. The action function receives the special
|
||||
;;; var and the new value as arguments and should return either #f if
|
||||
;;; the modification should be rejected, or a list containing the
|
||||
;;; result otherwise.
|
||||
|
||||
;;; Finally, a config var has two states, "officially" modified, and
|
||||
;;; unofficially modified. You control what kind of modification
|
||||
;;; you're making with the second argument to
|
||||
;;; gnc:config-var-value-set! The idea is that options specified on
|
||||
;;; the command line will set the value of these config vars, but that
|
||||
;;; setting is considered transient. Other settings (like from the UI
|
||||
;;; preferences panel, or normal user code) should be considered
|
||||
;;; permanent, and if they leave the variable value different from the
|
||||
;;; default, should be saved to ~/.gnucash/config.auto.
|
||||
|
||||
(define (gnc:make-config-var description
|
||||
set-action-func
|
||||
equality-func
|
||||
default)
|
||||
(let ((var
|
||||
(vector description set-action-func equality-func #f default default)))
|
||||
(gnc:config-var-value-set! var #f default)
|
||||
var))
|
||||
|
||||
(define (gnc:config-var-description-get var) (vector-ref var 0))
|
||||
|
||||
(define (gnc:config-var-action-func-get var) (vector-ref var 1))
|
||||
|
||||
(define (gnc:config-var-equality-func-get var) (vector-ref var 2))
|
||||
|
||||
(define (gnc:config-var-modified? var) (vector-ref var 3))
|
||||
(define (gnc:config-var-modified?-set! var value) (vector-set! var 3 value))
|
||||
|
||||
(define (gnc:config-var-default-value-get var) (vector-ref var 4))
|
||||
(define (gnc:config-var-default-value-set! var value) (vector-set! var 4 value))
|
||||
|
||||
(define (gnc:config-var-value-get var) (vector-ref var 5))
|
||||
(define (gnc:config-var-value-set! var is-config-mod? value)
|
||||
(let ((set-action (gnc:config-var-action-func-get var))
|
||||
(result (list value)))
|
||||
(if set-action (set! result (set-action var value)))
|
||||
(if result
|
||||
(begin
|
||||
(if is-config-mod? (gnc:config-var-modified?-set var #t))
|
||||
(vector-set! var 5 (car result))))))
|
||||
|
||||
(define (gnc:config-var-value-is-default? var)
|
||||
(if (not (gnc:config-var-modified? var))
|
||||
#t
|
||||
(let (equal-values? gnc:config-var-equality-func-get var)
|
||||
(equal-values?
|
||||
(gnc:config-var-default-value-get var)
|
||||
(gnc:config-var-value-get var)))))
|
||||
37
src/scm/startup/init.scm.in
Normal file
37
src/scm/startup/init.scm.in
Normal file
@@ -0,0 +1,37 @@
|
||||
;;;; startup.scm -*-scheme-*-
|
||||
;;
|
||||
;; Minimal startup code. This file should just contain enough code to
|
||||
;; get the arguments parsed and things like gnc:*load-path* set up.
|
||||
;; After that *everything* should be loaded via gnc:load.
|
||||
|
||||
;; This load should go away when guile gets fixed. as of guile1.3,
|
||||
;; it's not. You have to do this manually, unless you call scm_shell,
|
||||
;; which we can't.
|
||||
(let ((boot-file (if (assoc 'prefix %guile-build-info)
|
||||
(string-append (cdr (assoc 'prefix %guile-build-info))
|
||||
"/share/guile/"
|
||||
(version)
|
||||
"/ice-9/boot-9.scm")
|
||||
"/usr/share/guile/1.3a/ice-9/boot-9.scm")))
|
||||
(primitive-load boot-file))
|
||||
|
||||
;; Automatically generated defaults...
|
||||
(define gnc:_config-dir-default_ "@GNC_RUNTIME_CONFIGDIR@")
|
||||
(define gnc:_share-dir-default_ "@GNC_RUNTIME_SHAREDIR@")
|
||||
|
||||
(let ((lowlev-files
|
||||
'("utilities.scm"
|
||||
"config-var.scm"
|
||||
"path.scm"
|
||||
"prefs.scm"
|
||||
"command-line.scm"
|
||||
"main.scm")))
|
||||
|
||||
(for-each (lambda (filename)
|
||||
(display "loading startup file ")
|
||||
(display (string-append gnc:_startup-dir-default_ "/" filename))
|
||||
(newline)
|
||||
|
||||
(primitive-load
|
||||
(string-append gnc:_startup-dir-default_ "/" filename)))
|
||||
lowlev-files))
|
||||
51
src/scm/startup/main.scm
Normal file
51
src/scm/startup/main.scm
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
(define (gnc:startup)
|
||||
(gnc:debug "starting up.")
|
||||
#t)
|
||||
|
||||
(define (gnc:shutdown exit-status)
|
||||
(gnc:debug "Shutdown -- exit-status: " exit-status)
|
||||
|
||||
(gnc:hook-run-danglers gnc:*shutdown-hook*)
|
||||
(_gnc_shutdown_ exit-status)
|
||||
(exit exit-status))
|
||||
|
||||
;;;; Now the fun begins.
|
||||
|
||||
(gnc:startup)
|
||||
|
||||
(if (not (gnc:handle-command-line-args))
|
||||
(gnc:shutdown 1))
|
||||
|
||||
;;; Now we can load a bunch of files.
|
||||
|
||||
(gnc:load "hooks.scm")
|
||||
(gnc:load "doc.scm")
|
||||
|
||||
;;; Load the system and user configs
|
||||
|
||||
(if (not (gnc:load-system-config-if-needed))
|
||||
(gnc:shutdown 1))
|
||||
|
||||
(if (not (gnc:load-user-config-if-needed))
|
||||
(gnc:shutdown 1))
|
||||
|
||||
(gnc:hook-run-danglers gnc:*startup-hook*)
|
||||
|
||||
(if (or (gnc:config-var-value-get gnc:*arg-show-usage*)
|
||||
(gnc:config-var-value-get gnc:*arg-show-help*))
|
||||
(begin
|
||||
(gnc:prefs-show-usage)
|
||||
(gnc:shutdown 0)))
|
||||
|
||||
(if (not (= (gnucash_lowlev_app_init) 0))
|
||||
(gnc:shutdown 0))
|
||||
|
||||
(if (pair? gnc:*command-line-files*)
|
||||
;; You can only open single files right now...
|
||||
(gnucash_ui_open_file (car gnc:*command-line-files*))
|
||||
(gnucash_ui_select_file))
|
||||
|
||||
(gnucash_lowlev_app_main)
|
||||
|
||||
(gnc:shutdown 0)
|
||||
124
src/scm/startup/path.scm
Normal file
124
src/scm/startup/path.scm
Normal file
@@ -0,0 +1,124 @@
|
||||
|
||||
(define gnc:_load-path-directories_ #f)
|
||||
(define gnc:_doc-path-directories_ #f)
|
||||
|
||||
(define (gnc:_path-expand_ items default-items)
|
||||
(if
|
||||
(null? items)
|
||||
'()
|
||||
(let ((item (car items))
|
||||
(other-items (cdr items)))
|
||||
(cond
|
||||
((eq? item 'default)
|
||||
(append
|
||||
(gnc:_path-expand_ default-items))
|
||||
(gnc:_path-expand_ other-items default-items))
|
||||
((string? item)
|
||||
(if (and (char=? #\( (string-ref item 0))
|
||||
(char=? #\) (string-ref item (- (string-length item) 1))))
|
||||
|
||||
(let ((current-dir
|
||||
(make-shared-substring item 1 (- (string-length item) 1))))
|
||||
|
||||
(if (directory? current-dir)
|
||||
(let ((subdirs (gnc:directory-subdirectories current-dir))
|
||||
(rest (gnc:_path-expand_ other-items default-items)))
|
||||
(cons current-dir (append subdirs rest)))
|
||||
(begin
|
||||
(gnc:warn "Ignoring non-directory " current-dir
|
||||
" in gnc:_path-expand_ item.")
|
||||
(gnc:_path-expand_ other-items default-items))))
|
||||
(if (directory? item)
|
||||
(begin
|
||||
(gnc:warn "Ignoring non-directory " item
|
||||
" in gnc:_path-expand_ item.")
|
||||
(gnc:_path-expand_ other-items default-items))
|
||||
(cons item (gnc:_path-expand_ other-items default-items)))))
|
||||
(else (gnc:warn "Ignoring invalid item " item " in gnc:_path-expand_.")
|
||||
(gnc:_path-expand_ other-items default-items))))))
|
||||
|
||||
(define (gnc:_load-path-update_ var items)
|
||||
(gnc:msg "Updating load path from " items)
|
||||
(let ((result (gnc:_path-expand_
|
||||
items
|
||||
(gnc:config-var-default-value-get var))))
|
||||
(if result
|
||||
(begin
|
||||
(set! gnc:_load-path-directories_ result)
|
||||
(gnc:msg " Setting path to " result)
|
||||
result)
|
||||
(begin
|
||||
(gnc:msg " No path... " result)
|
||||
#f))))
|
||||
|
||||
(define (gnc:_doc-path-update_ var items)
|
||||
(let ((result (gnc:_path-expand_
|
||||
items
|
||||
(gnc:config-var-default-value-get var))))
|
||||
(if result
|
||||
(begin
|
||||
(set! gnc:_doc-path-directories_ result)
|
||||
result)
|
||||
#f)))
|
||||
|
||||
;; It may make sense to dump this in favor of guile's load-path later,
|
||||
;; but for now this works, and having gnc things separate may be less
|
||||
;; confusing and avoids shadowing problems.
|
||||
|
||||
(define (gnc:load name)
|
||||
"Name must be a string. The system attempts to locate the file of
|
||||
the given name and load it. The system will attempt to locate the
|
||||
file in all of the directories specified by gnc:*load-path*."
|
||||
|
||||
(let ((file-name (gnc:find-in-directories name gnc:_load-path-directories_)))
|
||||
(if (not file-name)
|
||||
#f
|
||||
(if (false-if-exception (primitive-load file-name))
|
||||
(begin
|
||||
(gnc:debug "loaded file " file-name)
|
||||
#t)
|
||||
(begin
|
||||
(gnc:warn "failure loading " file-name)
|
||||
#f)))))
|
||||
|
||||
(define (gnc:load-user-config-if-needed)
|
||||
(let ((user-config-loaded? #f))
|
||||
(lambda ()
|
||||
(if (not user-config-loaded)
|
||||
(begin
|
||||
(gnc:debug "loading user configuration")
|
||||
|
||||
(let ((user-file
|
||||
(string-append (getenv "HOME") "/.gnucash/config.user"))
|
||||
(auto-file
|
||||
(string-append (getenv "HOME") "/.gnucash/config.auto")))
|
||||
|
||||
(if (access? user-file F_OK)
|
||||
(if (false-if-exception (primitive-load user-file))
|
||||
(set! gnc:user-config-loaded #t)
|
||||
(begin
|
||||
(gnc:warn "failure loading " user-file)
|
||||
#f))
|
||||
(if (access? auto-file F_OK)
|
||||
(if (false-if-exception (primitive-load auto-file))
|
||||
(set! user-config-loaded? #t)
|
||||
(begin
|
||||
(gnc:warn "failure loading " auto-file)
|
||||
#f))))))))))
|
||||
|
||||
(define gnc:load-system-config-if-needed
|
||||
(let ((system-config-loaded? #f))
|
||||
(lambda ()
|
||||
(if (not system-config-loaded?)
|
||||
(begin
|
||||
(gnc:debug "loading system configuration")
|
||||
|
||||
(let ((system-config (string-append
|
||||
(gnc:config-var-value-get gnc:*config-dir*)
|
||||
"/config")))
|
||||
|
||||
(if (false-if-exception (primitive-load system-config))
|
||||
(set! system-config-loaded? #t)
|
||||
(begin
|
||||
(gnc:warn "failure loading " system-config)
|
||||
#f))))))))
|
||||
152
src/scm/startup/prefs.scm
Normal file
152
src/scm/startup/prefs.scm
Normal file
@@ -0,0 +1,152 @@
|
||||
|
||||
|
||||
;;;; Preferences
|
||||
|
||||
(define gnc:*arg-show-usage*
|
||||
(gnc:make-config-var
|
||||
"Generate an argument summary."
|
||||
(lambda (var value) (if (boolean? value) (list value) #f))
|
||||
eq?
|
||||
#f))
|
||||
|
||||
(define gnc:*arg-show-help*
|
||||
(gnc:make-config-var
|
||||
"Generate an argument summary."
|
||||
(lambda (var value) (if (boolean? value) (list value) #f))
|
||||
eq?
|
||||
#f))
|
||||
|
||||
(define gnc:*debugging?*
|
||||
(gnc:make-config-var
|
||||
"Enable debugging code."
|
||||
(lambda (var value) (if (boolean? value) (list value) #f))
|
||||
eq?
|
||||
#f))
|
||||
|
||||
(define gnc:*startup-dir*
|
||||
(gnc:make-config-var
|
||||
"Location of initial lowest level scheme startup files."
|
||||
(lambda (var value)
|
||||
;; You can't change the startup dir from here. It's considered
|
||||
;; hard-coded once known -- see startup/init.scm.
|
||||
#f)
|
||||
string=?
|
||||
gnc:_startup-dir-default_))
|
||||
|
||||
(define gnc:*config-dir*
|
||||
(gnc:make-config-var
|
||||
"Configuration directory."
|
||||
(lambda (var value) (if (string? value) (list value) #f))
|
||||
string=?
|
||||
gnc:_config-dir-default_))
|
||||
|
||||
(define gnc:*share-dir*
|
||||
(gnc:make-config-var
|
||||
"Shared files directory."
|
||||
(lambda (var value) (if (string? value) (list value) #f))
|
||||
string=?
|
||||
gnc:_share-dir-default_))
|
||||
|
||||
(define gnc:*load-path*
|
||||
(gnc:make-config-var
|
||||
"A list of strings indicating the load path for (gnc:load name).
|
||||
Any path element enclosed in parentheses will automatically be
|
||||
expanded to that directory and all its subdirectories whenever this
|
||||
variable is modified. The symbol element default will expand to the default directory. i.e. (gnc:config-var-value-set! gnc:*load-path* '(\"/my/dir/\" default))"
|
||||
(lambda (var value)
|
||||
(if (not (list? value))
|
||||
#f
|
||||
(let ((result (gnc:_load-path-update_ var value)))
|
||||
(if (list? result)
|
||||
(list result)
|
||||
#f))))
|
||||
equal?
|
||||
(list
|
||||
(string-append "(" (getenv "HOME") "/.gnucash/scm)")
|
||||
(string-append "(" gnc:_share-dir-default_ "/scm)"))))
|
||||
|
||||
(define gnc:*doc-path*
|
||||
(gnc:make-config-var
|
||||
"A list of strings indicating where to look for html and parsed-html files
|
||||
Any path element enclosed in parentheses will automatically be
|
||||
expanded to that directory and all its subdirectories whenever this
|
||||
variable is modified. The symbol element default will expand to the
|
||||
default directory. i.e. (gnc:config-var-value-set! gnc:*doc-path*
|
||||
'(\"/my/dir/\" default))"
|
||||
(lambda (var value)
|
||||
(if (not (list? value))
|
||||
#f
|
||||
(let ((result (gnc:_doc-path-update_ var value)))
|
||||
(if (list? result)
|
||||
(list result)
|
||||
#f))))
|
||||
equal?
|
||||
(list
|
||||
(string-append "(" (getenv "HOME") "/.gnucash/doc)")
|
||||
(string-append "(" gnc:_share-dir-default_ "/Docs)")
|
||||
(string-append "(" gnc:_share-dir-default_ "/Reports)"))))
|
||||
|
||||
(define gnc:*prefs*
|
||||
(list
|
||||
|
||||
(cons
|
||||
"usage"
|
||||
(cons 'boolean
|
||||
(lambda (val)
|
||||
(gnc:config-var-value-set! gnc:*arg-show-usage* #f val))))
|
||||
(cons
|
||||
"help"
|
||||
(cons 'boolean
|
||||
(lambda (val)
|
||||
(gnc:config-var-value-set! gnc:*arg-show-help* #f val))))
|
||||
(cons
|
||||
"debug"
|
||||
(cons 'boolean
|
||||
(lambda (val)
|
||||
(gnc:config-var-value-set! gnc:*debugging?* #f val))))
|
||||
|
||||
(cons
|
||||
"startup-dir"
|
||||
(cons 'string
|
||||
(lambda (val)
|
||||
(gnc:config-var-value-set! gnc:*startup-dir* #f val))))
|
||||
|
||||
(cons
|
||||
"config-dir"
|
||||
(cons 'string
|
||||
(lambda (val)
|
||||
(gnc:config-var-value-set! gnc:*config-dir* #f val))))
|
||||
|
||||
(cons
|
||||
"share-dir"
|
||||
(cons 'string
|
||||
(lambda (val)
|
||||
(gnc:config-var-value-set! gnc:*share-dir* #f val))))
|
||||
|
||||
|
||||
(cons
|
||||
"load-path"
|
||||
(cons 'string
|
||||
(lambda (val)
|
||||
(let ((path-list
|
||||
(call-with-input-string val (lambda (port) (read port)))))
|
||||
(if (list? path-list)
|
||||
(gnc:config-var-value-set! gnc:*load-path* #f path-list)
|
||||
(begin
|
||||
(gnc:error "non-list given for --load-path: " val)
|
||||
(gnc:shutdown 1)))))))
|
||||
|
||||
(cons
|
||||
"doc-path"
|
||||
(cons 'string
|
||||
(lambda (val)
|
||||
(let ((path-list
|
||||
(call-with-input-string val (lambda (port) (read port)))))
|
||||
(if (list? path-list)
|
||||
(gnc:config-var-value-set! gnc:*doc-path* #f path-list)
|
||||
(begin
|
||||
(gnc:error "non-list given for --doc-path: " val)
|
||||
(gnc:shutdown 1)))))))
|
||||
|
||||
(cons "load-user-config" (cons 'boolean gnc:load-user-config-if-needed))
|
||||
(cons "load-system-config" (cons 'boolean gnc:load-system-config-if-needed))))
|
||||
76
src/scm/startup/utilities.scm
Normal file
76
src/scm/startup/utilities.scm
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
|
||||
;;;; Warning functions...
|
||||
|
||||
(define (gnc:warn . items)
|
||||
(display "gnucash: [W] ")
|
||||
(for-each (lambda (i) (display i)) items)
|
||||
(newline))
|
||||
|
||||
(define (gnc:error . items)
|
||||
(display "gnucash: [E] ")
|
||||
(for-each (lambda (i) (display i)) items)
|
||||
(newline))
|
||||
|
||||
(define (gnc:msg . items)
|
||||
(display "gnucash: [M] ")
|
||||
(for-each (lambda (i) (display i)) items)
|
||||
(newline))
|
||||
|
||||
(define (gnc:debug . items)
|
||||
(if (gnc:config-var-value-get gnc:*debugging?*)
|
||||
(begin
|
||||
(display "gnucash: [D] ")
|
||||
(for-each (lambda (i) (display i)) items)
|
||||
(newline))))
|
||||
|
||||
|
||||
(define (directory? path)
|
||||
;; This follows symlinks normally.
|
||||
(let* ((status (false-if-exception (stat path)))
|
||||
(type (if status (stat:type status) #f)))
|
||||
(eq? type 'directory)))
|
||||
|
||||
(define (gnc:directory-subdirectories dir-name)
|
||||
;; Return a recursive list of the subdirs of dir-name, including
|
||||
;; dir-name. Follow symlinks.
|
||||
|
||||
(let ((dir-port (opendir dir-name)))
|
||||
(if (not dir-port)
|
||||
#f
|
||||
(do ((item (readdir dir-port) (readdir dir-port))
|
||||
(dirs '()))
|
||||
((eof-object? item) (reverse dirs))
|
||||
|
||||
(if (not (or (string=? item ".")
|
||||
(string=? item "..")))
|
||||
(let* ((full-path (string-append dir-name "/" item)))
|
||||
;; ignore symlinks, etc.
|
||||
(if (access? full-path F_OK)
|
||||
(let* ((status (lstat full-path))
|
||||
(type (if status (stat:type status) #f)))
|
||||
(if (and (eq? type 'directory))
|
||||
(set! dirs
|
||||
(cons full-path
|
||||
(append
|
||||
(gnc:directory-subdirectories full-path)
|
||||
dirs))))))))))))
|
||||
|
||||
(define (gnc:find-in-directories file directories)
|
||||
"Find file named 'file' anywhere in 'directories'. 'file' must be a
|
||||
string and 'directories' must be a list of strings."
|
||||
|
||||
(gnc:debug "gnc:find-in-directories looking for " file " in " directories)
|
||||
|
||||
(do ((rest directories (cdr rest))
|
||||
(finished? #f)
|
||||
(result #f))
|
||||
((or (null? rest) finished?) result)
|
||||
|
||||
(let ((file-name (string-append (car rest) "/" file)))
|
||||
(gnc:debug " checking for " file-name)
|
||||
(if (access? file-name F_OK)
|
||||
(begin
|
||||
(gnc:debug "found file " file-name)
|
||||
(set! finished? #t)
|
||||
(set! result file-name))))))
|
||||
Reference in New Issue
Block a user