dler) (vector-ref handler 1))
  (define (exception-handler-pre-unwind handler) (vector-ref handler 2))

  (define %running-pre-unwind (make-fluid #f))
  (define (pre-unwind-handler-running? handler)
    (let lp ((depth 0))
      (let ((running (fluid-ref* %running-pre-unwind depth)))
        (and running
             (or (eq? running handler) (lp (1+ depth)))))))

  (define (dispatch-exception depth key args)
    (cond
     ((fluid-ref* %eh depth)
      => (lambda (handler)
           (let ((catch-key (exception-handler-catch-key handler)))
             (if (or (eqv? catch-key #t) (eq? catch-key key))
                 (let ((prompt-tag (exception-handler-prompt-tag handler))
                       (pre-unwind (exception-handler-pre-unwind handler)))
                   (cond
                    ((and pre-unwind
                          (not (pre-unwind-handler-running? handler)))
                     ;; Prevent errors from within the pre-unwind
                     ;; handler's invocation from being handled by this
                     ;; handler.
                     (with-fluid* %running-pre-unwind handler
                       (lambda ()
                         ;; FIXME: Currently the "running" flag only
                         ;; applies to the pre-unwind handler; the
                         ;; post-unwind handler is still called if the
                         ;; error is explicitly rethrown.  Instead it
                         ;; would be better to cause a recursive throw to
                         ;; skip all parts of this handler.  Unfortunately
                         ;; that is incompatible with existing semantics.
                         ;; We'll see if we can change that later on.
                         (apply pre-unwind key args)
                         (dispatch-exception depth key args))))
                    (prompt-tag
                     (apply abort-to-prompt prompt-tag key args))
                    (else
                     (dispatch-exception (1+ depth) key args))))
                 (dispatch-exception (1+ depth) key args)))))
     ((eq? key 'quit)
      (primitive-exit (cond
                       ((not (pair? args)) 0)
                       ((integer? (car args)) (car args))
                       ((not (car args)) 1)
                       (else 0))))
     (else
      (format (current-error-port) "guile: uncaught throw to ~a: ~a\n"
              key args)
      (primitive-exit 1))))

  (define (throw key . args)
    "Invoke the catch form matching @var{key}, passing @var{args} to the
@var{handler}.

@var{key} is a symbol. It will match catches of the same symbol or of @code{#t}.

If there is no handler at all, Guile prints an error and then exits."
    (unless (symbol? key)
      (throw 'wrong-type-arg "throw" "Wrong type argument in position ~a: ~a"
             (list 1 key) (list key)))
    (dispatch-exception 0 key args))

  (define* (catch k thunk handler #:optional pre-unwind-handler)
    "Invoke @var{thunk} in the dynamic context of @var{handler} for
exceptions matching @var{key}.  If thunk throws to the symbol
@var{key}, then @var{handler} is invoked this way:
@lisp
 (handler key args ...)
@end lisp

@var{key} is a symbol or @code{#t}.

@var{thunk} takes no arguments.  If @var{thunk} returns
normally, that is the return value of @code{catch}.

Handler is invoked outside the scope of its own @code{catch}.
If @var{handler} again throws to the same key, a new handler
from further up the call chain is invoked.

If the key is @code{#t}, then a throw to @emph{any} symbol will
match this call to @code{catch}.

If a @var{pre-unwind-handler} is given and @var{thunk} throws
an exception that matches @var{key}, Guile calls the
@var{pre-unwind-handler} before unwinding the dynamic state and
invoking the main @var{handler}.  @var{pre-unwind-handler} should
be a procedure with the same signature as @var{handler}, that
is @code{(lambda (key . args))}.  It is typically used to save
the stack at the point where the exception occurred, but can also
query other parts of the dynamic state at that point, such as
fluid values.

A @var{pre-unwind-handler} can exit either normally or non-locally.
If it exits normally, Guile unwinds the stack and dynamic context
and then calls the normal (third argument) handler.  If it exits
non-locally, that exit determines the continuation."
    (define (wrong-type-arg n val)
      (scm-error 'wrong-type-arg "catch"
                 "Wrong type argument in position ~a: ~a"
                 (list n val) (list val)))
    (unless (or (symbol? k) (eqv? k #t))
      (wrong-type-arg 1 k))
    (unless (procedure? handler)
      (wrong-type-arg 3 handler))
    (unless (or (not pre-unwind-handler) (procedure? pre-unwind-handler))
      (wrong-type-arg 4 pre-unwind-handler))
    (let ((tag (make-prompt-tag "catch")))
      (call-with-prompt
       tag
       (lambda ()
         (with-fluid* %eh (make-exception-handler k tag pre-unwind-handler)
           thunk))
       (lambda (cont k . args)
         (apply handler k args)))))

  (define (with-throw-handler k thunk pre-unwind-handler)
    "Add @var{handler} to the dynamic context as a throw handler
for key @var{k}, then invoke @var{thunk}."
    (if (not (or (symbol? k) (eqv? k #t)))
        (scm-error 'wrong-type-arg "with-throw-handler"
                   "Wrong type argument in position ~a: ~a"
                   (list 1 k) (list k)))
    (with-fluid* %eh (make-exception-handler k #f pre-unwind-handler)
      thunk))

  (hashq-remove! (%get-pre-modules-obarray) '%exception-handler)
  (define! 'catch catch)
  (define! 'with-throw-handler with-throw-handler)
  (define! 'throw throw))


