Add PATH searching

* geesh/shell.scm (slashless?) New function.
(split-search-path): New function.
(find-utility): New function.
(exec-let): Use them to search PATH for a utility when appropriate.
(exec): Update doc string.
* tests/shell.scm: Test it.
This commit is contained in:
Timothy Sample 2018-07-18 23:37:21 -04:00
parent 020adfc58b
commit 7cc94e88e1
2 changed files with 56 additions and 4 deletions

View File

@ -22,12 +22,38 @@ will be @var{args}."
(pid (match-let (((pid . status) (waitpid pid)))
(set-var! env "?" (number->string (status:exit-val status))))))))
(define (slashless? s)
"Test if the string @var{s} does not contain any slashes ('/')."
(not (string-index s #\/)))
(define (split-search-path s)
"Split the search path string @var{s}."
(if (string-null? s) '() (string-split s #\:)))
(define (find-utility env name)
"Search for the path of the utility @var{name} using @var{env}. If
it cannot be found, return @code{#f}."
(let loop ((prefixes (split-search-path (var-ref* env "PATH"))))
(and (pair? prefixes)
(let* ((prefix (car prefixes))
(path (if (string-suffix? "/" prefix)
(string-append prefix name)
(string-append prefix "/" name))))
(if (access? path X_OK)
path
(loop (cdr prefixes)))))))
(define (sh:exec-let env bindings name . args)
"Execute @var{name} with arguments @var{args}, environment
"Find and execute @var{name} with arguments @var{args}, environment
@var{env}, and extra environment variable bindings @var{bindings}."
(exec-utility env bindings name name args))
(if (slashless? name)
(or (and=> (find-utility env name)
(lambda (path)
(exec-utility env bindings path name args)))
(error "Command not found."))
(exec-utility env bindings name name args)))
(define (sh:exec env name . args)
"Execute @var{name} with arguments @var{args} and environment
@var{env}."
"Find and execute @var{name} with arguments @var{args} and
environment @var{env}."
(apply sh:exec-let env '() name args))

View File

@ -94,4 +94,30 @@
(sh:exec env utility)
(file-exists? sentinal)))))
(test-assert "Executes a utility by searching PATH"
(call-with-temporary-directory
(lambda (directory)
(let ((utility (string-append directory "/utility"))
(sentinal (string-append directory "/sentinal.txt"))
(env (make-environment `(("PATH" . ,directory)))))
(make-script utility
(with-output-to-file ,sentinal
(lambda ()
(display "x"))))
(sh:exec env "utility")
(file-exists? sentinal)))))
(test-assert "Throws error if a utility cannot be found"
(call-with-temporary-directory
(lambda (directory)
(let ((env (make-environment `(("PATH" . ,directory)))))
(catch #t
(lambda ()
(sh:exec env "utility")
#f)
(lambda args
(match args
(('misc-error _ _ ("Command not found.") _) #t)
(_ #f))))))))
(test-end)