Handle 'break' and 'continue' errors.

* gash/built-ins/utils.scm (string->positive-integer): New function.
* gash/built-ins/break.scm (main): Use it to simplify argument
checking; check for too many arguments; and print messages on errors.
* gash/built-ins/continue.scm (main): Ditto.
* gash/shell.scm (sh:exec-let): Throw an error if a special built-in
fails.
* tests/spec/oil.scm: Enable and adjust previously failing tests.
This commit is contained in:
Timothy Sample 2019-06-07 11:51:53 -04:00
parent 7d2298d15b
commit ffe9fc1f47
5 changed files with 56 additions and 22 deletions

View File

@ -17,8 +17,10 @@
;;; along with Gash. If not, see <http://www.gnu.org/licenses/>. ;;; along with Gash. If not, see <http://www.gnu.org/licenses/>.
(define-module (gash built-ins break) (define-module (gash built-ins break)
#:use-module (gash built-ins utils)
#:use-module (gash compat) #:use-module (gash compat)
#:use-module (gash environment)) #:use-module (gash environment)
#:use-module (ice-9 match))
;;; Commentary: ;;; Commentary:
;;; ;;;
@ -27,11 +29,15 @@
;;; Code: ;;; Code:
(define (main . args) (define (main . args)
(let* ((arg (and (pair? args) (car args))) (match args
(n (string->number (or arg "1")))) (() (main "1"))
(if (and arg (or (not n) (not (exact-integer? n)) (< n 1))) ((arg)
1 (match (string->positive-integer arg)
(begin (#f (format (current-error-port)
;; Since we do not return, we have to set the status here. "gash: break: argument must be a positive integer~%")
(set-status! 0) EXIT_FAILURE)
(sh:break (1- n)))))) (n (set-status! 0)
(sh:break (1- n)))))
(_ (format (current-error-port)
"gash: break: too many arguments~%")
EXIT_FAILURE)))

View File

@ -17,8 +17,10 @@
;;; along with Gash. If not, see <http://www.gnu.org/licenses/>. ;;; along with Gash. If not, see <http://www.gnu.org/licenses/>.
(define-module (gash built-ins continue) (define-module (gash built-ins continue)
#:use-module (gash built-ins utils)
#:use-module (gash compat) #:use-module (gash compat)
#:use-module (gash environment)) #:use-module (gash environment)
#:use-module (ice-9 match))
;;; Commentary: ;;; Commentary:
;;; ;;;
@ -27,11 +29,15 @@
;;; Code: ;;; Code:
(define (main . args) (define (main . args)
(let* ((arg (and (pair? args) (car args))) (match args
(n (string->number (or arg "1")))) (() (main "1"))
(if (and arg (or (not n) (not (exact-integer? n)) (< n 1))) ((arg)
1 (match (string->positive-integer arg)
(begin (#f (format (current-error-port)
;; Since we do not return, we have to set the status here. "gash: continue: argument must be a positive integer~%")
(set-status! 0) EXIT_FAILURE)
(sh:continue (1- n)))))) (n (set-status! 0)
(sh:continue (1- n)))))
(_ (format (current-error-port)
"gash: continue: too many arguments~%")
EXIT_FAILURE)))

View File

@ -20,7 +20,8 @@
#:use-module (ice-9 match) #:use-module (ice-9 match)
#:export (get-evaluator #:export (get-evaluator
built-in? built-in?
split-assignment)) split-assignment
string->positive-integer))
;;; Commentary: ;;; Commentary:
;;; ;;;
@ -53,3 +54,14 @@
(match (substring assignment (1+ index)) (match (substring assignment (1+ index))
((? string-null?) (values name #f)) ((? string-null?) (values name #f))
(value (values name value))))))) (value (values name value)))))))
(define char-set:ascii-digit
(char-set-intersection char-set:ascii char-set:digit))
(define (string->positive-integer s)
"Return the positive integer represented by the string @var{s}. If
@var{s} does not represent a positive, decimal integer in return
@code{#f}."
(and=> (and (string-every char-set:ascii-digit s) (string->number s))
(lambda (n)
(and (exact-integer? n) (> n 0) n))))

View File

@ -131,6 +131,8 @@ environment variable bindings @var{bindings}."
(setvar! name value))) (setvar! name value)))
bindings) bindings)
(let ((exit-val (apply proc args))) (let ((exit-val (apply proc args)))
(unless (= exit-val EXIT_SUCCESS)
(throw 'shell-error))
(set-status! exit-val)))) (set-status! exit-val))))
(and=> (getfun name) (and=> (getfun name)
(lambda (proc) (lambda (proc)

View File

@ -123,7 +123,7 @@
out))) out)))
(loop (read-line in 'concat) transformers)))))))) (loop (read-line in 'concat) transformers))))))))
(tests-to-filter (tests-to-filter
'(("spec/case_.test.sh" `(("spec/case_.test.sh"
(;; These two are Bash specific. (;; These two are Bash specific.
("Case statement with ;;&") ("Case statement with ;;&")
("Case statement with ;&"))) ("Case statement with ;&")))
@ -162,8 +162,16 @@
("continue at top level") ("continue at top level")
("continue in subshell") ("continue in subshell")
("continue in subshell aborts with errexit") ("continue in subshell aborts with errexit")
("bad arg to break") ;; The Oil shell handles this statically. We
("too many args to continue"))) ;; will treat it as a fatal run-time error (for
;; now).
("too many args to continue"
("## status: 2" ,(string-append
"## status: 2\n"
"## OK gash status: 1\n"
"## OK gash STDOUT:\n"
"a\n"
"## END")))))
("spec/quote.test.sh" ("spec/quote.test.sh"
(;; We match KornShell on these two tests. (;; We match KornShell on these two tests.
("Unterminated single quote" ("Unterminated single quote"