-/*.elc
+*.elc
+*~
+elc-stamp
/*.tar.gz
-/*~
/COPYING
/Makefile
/Makefile.in
/elc-temp
/install-sh
/missing
+script
* "kshdb":https://github.com/rocky/kshdb/wiki (Korn Shell)
* perldb (Perl)
* "Devel::Trepan":https://github.com/rocky/Perl-Devel-Trepan/wiki (Perl)
+ * pdb (Stock C Python debugger)
* "pydb":http://bashdb.sourceforge.net/pydb/ (Python)
* "pydbgr":http://code.google.com/p/pydbgr/ (Python)
* "trepanning":https://github.com/rocky/rb-trepanning/wiki (Ruby 1.9)
dbgr/debugger/gdb/Makefile \
dbgr/debugger/kshdb/Makefile \
dbgr/debugger/perldb/Makefile \
+ dbgr/debugger/pdb/Makefile \
dbgr/debugger/pydbgr/Makefile \
dbgr/debugger/rdebug/Makefile \
dbgr/debugger/remake/Makefile \
"./dbgr/debugger/bashdb/bashdb"
"./dbgr/debugger/gdb/gdb"
"./dbgr/debugger/kshdb/kshdb"
+ "./dbgr/debugger/pdb/pdb"
"./dbgr/debugger/pydbgr/pydbgr"
"./dbgr/debugger/perldb/perldb"
"./dbgr/debugger/rdebug/rdebug"
-SUBDIRS = bashdb gdb kshdb perldb pydbgr rdebug remake \
+SUBDIRS = bashdb gdb kshdb pdb perldb pydbgr rdebug remake \
trepan trepan.pl trepanx trepan8 zshdb
EXTRA_DIST = common.mk
--- /dev/null
+/Makefile
+/Makefile.in
+/core.elc
--- /dev/null
+include ../common.mk
--- /dev/null
+;;; Copyright (C) 2012 Rocky Bernstein <rocky@gnu.org>
+(eval-when-compile (require 'cl))
+
+(require 'load-relative)
+(require-relative-list '("../../common/track"
+ "../../common/core"
+ "../../common/lang")
+ "dbgr-")
+(require-relative-list '("init") "dbgr-pdb-")
+
+
+;; FIXME: I think the following could be generalized and moved to
+;; dbgr-... probably via a macro.
+(defvar pdb-minibuffer-history nil
+ "minibuffer history list for the command `pdb'.")
+
+(easy-mmode-defmap pdb-minibuffer-local-map
+ '(("\C-i" . comint-dynamic-complete-filename))
+ "Keymap for minibuffer prompting of gud startup command."
+ :inherit minibuffer-local-map)
+
+;; FIXME: I think this code and the keymaps and history
+;; variable chould be generalized, perhaps via a macro.
+(defun pdb-query-cmdline (&optional opt-debugger)
+ (dbgr-query-cmdline
+ 'pdb-suggest-invocation
+ pdb-minibuffer-local-map
+ 'pdb-minibuffer-history
+ opt-debugger))
+
+(defun pdb-parse-cmd-args (orig-args)
+ "Parse command line ARGS for the annotate level and name of script to debug.
+
+ARGS should contain a tokenized list of the command line to run.
+
+We return the a list containing
+- the command processor (e.g. python) and it's arguments if any - a list of strings
+- the name of the debugger given (e.g. pdb) and its arguments - a list of strings
+- the script name and its arguments - list of strings
+- whether the annotate or emacs option was given ('-A', '--annotate' or '--emacs) - a boolean
+
+For example for the following input
+ (map 'list 'symbol-name
+ '(python2.6 -O -Qold ./gcd.py a b))
+
+we might return:
+ ((python2.6 -O -Qold) (pdb) (./gcd.py a b) 't)
+
+NOTE: the above should have each item listed in quotes.
+"
+
+ ;; Parse the following kind of pattern:
+ ;; [python python-options] pdb pdb-options script-name script-options
+ (let (
+ (args orig-args)
+ (pair) ;; temp return from
+ (python-opt-two-args '())
+ ;; Python doesn't have mandatory 2-arg options in our sense,
+ ;; since the two args can be run together, e.g. "-C/tmp" or "-C /tmp"
+ ;;
+ (python-two-args '())
+ ;; pdb doesn't have any arguments
+ (pdb-two-args '())
+ (pdb-opt-two-args '())
+ (interp-regexp
+ (if (member system-type (list 'windows-nt 'cygwin 'msdos))
+ "^python[-0-9.]*\\(.exe\\)?$"
+ "^python[-0-9.]*$"))
+
+ ;; Things returned
+ (annotate-p nil)
+ (debugger-args '())
+ (debugger-name nil)
+ (interpreter-args '())
+ (script-args '())
+ (script-name nil)
+ )
+
+ (if (not (and args))
+ ;; Got nothing: return '(nil, nil)
+ (list interpreter-args debugger-args script-args annotate-p)
+ ;; else
+ ;; Strip off optional "python" or "python182" etc.
+ (when (string-match interp-regexp
+ (file-name-sans-extension
+ (file-name-nondirectory (car args))))
+ (setq interpreter-args (list (pop args)))
+
+ ;; Strip off Python-specific options
+ (while (and args
+ (string-match "^-" (car args)))
+ (setq pair (dbgr-parse-command-arg
+ args python-two-args python-opt-two-args))
+ (nconc interpreter-args (car pair))
+ (setq args (cadr pair))))
+
+ ;; Remove "pdb" from "pdb --pdb-options script
+ ;; --script-options"
+ (setq debugger-name (file-name-sans-extension
+ (file-name-nondirectory (car args))))
+ (unless (string-match "^\\(pdb\\|cli.py\\)$" debugger-name)
+ (message
+ "Expecting debugger name `%s' to be `pdb' or `cli.py'"
+ debugger-name))
+ (setq debugger-args (list (pop args)))
+
+ ;; Skip to the first non-option argument.
+ (while (and args (not script-name))
+ (let ((arg (car args)))
+ (cond
+ ;; Options with arguments.
+ ((string-match "^-" arg)
+ (setq pair (dbgr-parse-command-arg
+ args pdb-two-args pdb-opt-two-args))
+ (nconc debugger-args (car pair))
+ (setq args (cadr pair)))
+ ;; Anything else must be the script to debug.
+ (t (setq script-name arg)
+ (setq script-args args))
+ )))
+ (list interpreter-args debugger-args script-args annotate-p))))
+
+(defvar pdb-command-name) ; # To silence Warning: reference to free variable
+(defun pdb-suggest-invocation (debugger-name)
+ "Suggest a pdb command invocation via `dbgr-suggest-invocaton'"
+ (dbgr-suggest-invocation pdb-command-name pdb-minibuffer-history
+ "python" "\\.py"))
+
+(defun pdb-reset ()
+ "Pdb cleanup - remove debugger's internal buffers (frame,
+breakpoints, etc.)."
+ (interactive)
+ ;; (pdb-breakpoint-remove-all-icons)
+ (dolist (buffer (buffer-list))
+ (when (string-match "\\*pdb-[a-z]+\\*" (buffer-name buffer))
+ (let ((w (get-buffer-window buffer)))
+ (when w
+ (delete-window w)))
+ (kill-buffer buffer))))
+
+;; (defun pdb-reset-keymaps()
+;; "This unbinds the special debugger keys of the source buffers."
+;; (interactive)
+;; (setcdr (assq 'pdb-debugger-support-minor-mode minor-mode-map-alist)
+;; pdb-debugger-support-minor-mode-map-when-deactive))
+
+
+(defun pdb-customize ()
+ "Use `customize' to edit the settings of the `pdb' debugger."
+ (interactive)
+ (customize-group 'pdb))
+
+(provide-me "dbgr-pdb-")
--- /dev/null
+;;; Copyright (C) 2012 Rocky Bernstein <rocky@gnu.org>
+;;; Stock Python debugger pdb
+
+(eval-when-compile (require 'cl))
+
+(require 'load-relative)
+(require-relative-list '("../../common/regexp"
+ "../../common/loc"
+ "../../common/init")
+ "dbgr-")
+(require-relative-list '("../../lang/python") "dbgr-lang-")
+
+(defvar dbgr-pat-hash)
+(declare-function make-dbgr-loc-pat (dbgr-loc))
+
+(defvar dbgr-pdb-pat-hash (make-hash-table :test 'equal)
+ "Hash key is the what kind of pattern we want to match:
+backtrace, prompt, etc. The values of a hash entry is a
+dbgr-loc-pat struct")
+
+(declare-function make-dbgr-loc "dbgr-loc" (a b c d e f))
+
+;; Regular expression that describes a pdb location generally shown
+;; before a command prompt.
+;;
+;; Program-location lines look like this:
+;; > /usr/bin/zonetab2pot.py(15)<module>()
+;; or MS Windows:
+;; > c:\\mydirectory\\gcd.py(10)<module>
+(setf (gethash "loc" dbgr-pdb-pat-hash)
+ (make-dbgr-loc-pat
+ :regexp "^> \\(\\(?:[a-zA-Z]:\\)?[-a-zA-Z0-9_/.\\\\ ]+\\)(\\([0-9]+\\))"
+ :file-group 1
+ :line-group 2))
+
+(setf (gethash "prompt" dbgr-pdb-pat-hash)
+ (make-dbgr-loc-pat
+ :regexp "^[(]+Pdb[)]+ "
+ ))
+
+;; Regular expression that describes a Python backtrace line.
+(setf (gethash "lang-backtrace" dbgr-pdb-pat-hash)
+ dbgr-python-backtrace-loc-pat)
+
+;; Regular expression that describes a "breakpoint set" line. For example:
+;; Breakpoint 1 at /usr/bin/pdb:7
+(setf (gethash "brkpt-set" dbgr-pdb-pat-hash)
+ (make-dbgr-loc-pat
+ :regexp "^Breakpoint \\([0-9]+\\) at[ \t\n]+\\(.+\\):\\([0-9]+\\)\\(\n\\|$\\)"
+ :num 1
+ :file-group 2
+ :line-group 3))
+
+;; Regular expression that describes a "delete breakpoint" line
+(setf (gethash "brkpt-del" dbgr-pdb-pat-hash)
+ (make-dbgr-loc-pat
+ :regexp "^Deleted breakpoint \\([0-9]+\\)\n"
+ :num 1))
+
+(setf (gethash "font-lock-keywords" dbgr-pdb-pat-hash)
+ '(
+ ;; The frame number and first type name, if present.
+ ("^\\(->\\|##\\)\\([0-9]+\\) \\(<module>\\)? *\\([a-zA-Z_][a-zA-Z0-9_]*\\)(\\(.+\\))?"
+ (2 dbgr-backtrace-number-face)
+ (4 font-lock-function-name-face nil t)) ; t means optional.
+
+ ;; Parameter sequence, E.g. gcd(a=3, b=5)
+ ;; ^^^^^^^^^
+ ("(\\(.+\\))"
+ (1 font-lock-variable-name-face))
+
+ ;; File name. E.g file '/test/gcd.py'
+ ;; ------^^^^^^^^^^^^-
+ ("[ \t]+file '\\([^ ]+*\\)'"
+ (1 dbgr-file-name-face))
+
+ ;; Line number. E.g. at line 28
+ ;; ---------^^
+ ("[ \t]+at line \\([0-9]+\\)$"
+ (1 dbgr-line-number-face))
+
+ ;; Function name.
+ ("\\<\\([a-zA-Z_][a-zA-Z0-9_]*\\)\\.\\([a-zA-Z_][a-zA-Z0-9_]*\\)"
+ (1 font-lock-type-face)
+ (2 font-lock-function-name-face))
+ ;; (pdb-frames-match-current-line
+ ;; (0 pdb-frames-current-frame-face append))
+ ))
+
+(setf (gethash "pdb" dbgr-pat-hash) dbgr-pdb-pat-hash)
+
+(defvar dbgr-pdb-command-hash (make-hash-table :test 'equal)
+ "Hash key is command name like 'shell' and the value is
+ the pdb command to use, like 'python'")
+
+(setf (gethash "shell" dbgr-pdb-command-hash) "python")
+(setf (gethash "pdb" dbgr-command-hash) dbgr-pdb-command-hash)
+
+(provide-me "dbgr-pdb-")
--- /dev/null
+;;; Copyright (C) 2012 Rocky Bernstein <rocky@gnu.org>
+;; `pdb' Main interface to pdb via Emacs
+(require 'load-relative)
+(require-relative-list '("../../common/helper"
+ "../../common/track") "dbgr-")
+(require-relative-list '("core" "track-mode") "dbgr-pdb-")
+
+;; This is needed, or at least the docstring part of it is needed to
+;; get the customization menu to work in Emacs 23.
+(defgroup pdb nil
+ "The Python pdb debugger"
+ :group 'processes
+ :group 'dbgr
+ :group 'python
+ :version "23.1")
+
+;; -------------------------------------------------------------------
+;; User definable variables
+;;
+
+(defcustom pdb-command-name
+ "pdb"
+ "File name for executing the stock Python debugger and command options.
+This should be an executable on your path, or an absolute file name."
+ :type 'string
+ :group 'pdb)
+
+(declare-function pdb-track-mode (bool))
+
+;; -------------------------------------------------------------------
+;; The end.
+;;
+
+;;;###autoload
+(defun dbgr-pdb (&optional opt-command-line no-reset)
+ "Invoke the pdb Python debugger and start the Emacs user interface.
+
+String COMMAND-LINE specifies how to run pdb.
+
+Normally command buffers are reused when the same debugger is
+reinvoked inside a command buffer with a similar command. If we
+discover that the buffer has prior command-buffer information and
+NO-RESET is nil, then that information which may point into other
+buffers and source buffers which may contain marks and fringe or
+marginal icons is reset."
+
+
+ (interactive)
+ (let* (
+ (cmd-str (or opt-command-line (pdb-query-cmdline
+ "pdb")))
+ (cmd-args (split-string-and-unquote cmd-str))
+ (parsed-args (pdb-parse-cmd-args cmd-args))
+ (script-args (cdr cmd-args))
+ (script-name (car script-args))
+ (cmd-buf))
+ (dbgr-run-process "pdb" script-name cmd-args
+ 'pdb-track-mode no-reset)
+ )
+ )
+
+
+(defalias 'pdb 'dbgr-pdb)
+
+(provide-me "dbgr-")
--- /dev/null
+;;; Copyright (C) 2010, 2012 Rocky Bernstein <rocky@gnu.org>
+;;; Python "pdb" Debugger tracking a comint
+;;; or eshell buffer.
+
+(eval-when-compile (require 'cl))
+(require 'load-relative)
+(require-relative-list '(
+ "../../common/cmds"
+ "../../common/menu"
+ "../../common/track"
+ "../../common/track-mode"
+ )
+ "dbgr-")
+(require-relative-list '("core" "init") "dbgr-pdb-")
+
+(dbgr-track-mode-vars "pdb")
+
+(declare-function dbgr-track-mode(bool))
+
+(dbgr-python-populate-command-keys pdb-track-mode-map)
+
+(defun pdb-track-mode-hook()
+ (if pdb-track-mode
+ (progn
+ (use-local-map pdb-track-mode-map)
+ (message "using pdb mode map")
+ )
+ (message "pdb track-mode-hook disable called")
+ )
+)
+
+(define-minor-mode pdb-track-mode
+ "Minor mode for tracking ruby debugging inside a process shell."
+ :init-value nil
+ ;; :lighter " pdb" ;; mode-line indicator from dbgr-track is sufficient.
+ ;; The minor mode bindings.
+ :global nil
+ :group 'pdb
+ :keymap pdb-track-mode-map
+ (dbgr-track-set-debugger "pdb")
+ (if pdb-track-mode
+ (progn
+ (setq dbgr-track-mode 't)
+ (run-mode-hooks (intern (pdb-track-mode-hook))))
+ (progn
+ (setq dbgr-track-mode nil)
+ ))
+)
+
+(provide-me "dbgr-pdb-")
-;;; Copyright (C) 2010, 2011 Rocky Bernstein <rocky@gnu.org>
-;;; pydbgr: Python 2.5 and beyond
+;;; Copyright (C) 2010, 2011, 2012 Rocky Bernstein <rocky@gnu.org>
+;;; pydbgr: Python 2.5 but less than 3K
(eval-when-compile (require 'cl))
;; Regular expression that describes a pydbgr location generally shown
;; before a command prompt.
;;
-;; Program-location lines look like this:
+;; For example:
;; (/usr/bin/zonetab2pot.py:15): <module>
;; or MS Windows:
;; (c:\\mydirectory\\gcd.py:10): <module>
-;; and in backtrace like this:
-;; (/usr/bin/zonetab2pot.py:15)
(setf (gethash "loc" dbgr-pydbgr-pat-hash)
(make-dbgr-loc-pat
:regexp "^(\\(\\(?:[a-zA-Z]:\\)?[-a-zA-Z0-9_/.\\\\ ]+\\):\\([0-9]+\\))"
-;;; Copyright (C) 2010, 2011 Rocky Bernstein <rocky@gnu.org>
+;;; Copyright (C) 2010, 2011, 2012 Rocky Bernstein <rocky@gnu.org>
;; `pydbgr' Main interface to pydbgr via Emacs
(require 'load-relative)
(require-relative-list '("../../common/helper"
(defcustom pydbgr-command-name
;;"pydbgr --emacs 3"
"pydbgr"
- "File name for executing the Ruby debugger and command options.
+ "File name for executing the Python debugger and command options.
This should be an executable on your path, or an absolute file name."
:type 'string
:group 'pydbgr)
--- /dev/null
+(require 'test-unit)
+(require 'font-lock)
+
+(load-file "../dbgr/common/buffer/command.el")
+(load-file "../dbgr/common/buffer/backtrace.el")
+(load-file "../dbgr/common/backtrace-mode.el")
+(load-file "../dbgr/common/init.el")
+(load-file "../dbgr/debugger/pdb/init.el")
+
+(test-unit-clear-contexts)
+
+(defun setup-bt(string temp-bt temp-cmdbuf)
+ (with-current-buffer temp-bt
+ (dbgr-backtrace-mode temp-cmdbuf)
+ (goto-char (point-min))
+ (setq buffer-read-only nil)
+ (insert string)
+ (font-lock-fontify-buffer)
+ (goto-char (point-min))
+ ))
+
+
+(context "dbgr-buffer-backtrace-pdb"
+ (tag dbgr-buf-bt-pdb)
+ (specify "fontify"
+ (setq temp-cmdbuf (generate-new-buffer "*cmdbuf-test*"))
+ (with-current-buffer temp-cmdbuf
+ (dbgr-cmdbuf-init temp-cmdbuf "pdb"
+ (gethash "pdb" dbgr-pat-hash))
+
+ )
+ (setq temp-bt (generate-new-buffer "*bt-test*"))
+ (setup-bt
+"->0 gcd(a=3, b=5) called from file '/test/gcd.py' at line 28
+##1 <module> execfile() file '/test/gcd.py' at line 41
+"
+ temp-bt temp-cmdbuf)
+ (with-current-buffer temp-bt
+ (goto-char (point-min))
+ (dolist (pair
+ '(
+ ("->" . dbgr-backtrace-number )
+ ("gc" . font-lock-function-name-face )
+ ("(" . font-lock-variable-name-face )
+ ("/test" . dbgr-file-name)
+ ("2" . dbgr-line-number)
+ ("##" . dbgr-backtrace-number)
+ ("/test" . dbgr-file-name)
+ ("4" . dbgr-line-number)
+ ))
+ (search-forward (car pair))
+ (assert-equal (cdr pair)
+ (get-text-property (point) 'face))
+ )
+ )
+ )
+ )
+(test-unit "dbgr-buf-bt-pdb")
+
--- /dev/null
+(require 'test-unit)
+(load-file "../dbgr/debugger/pdb/pdb.el")
+
+(test-unit-clear-contexts)
+
+(context "pdb"
+ (tag pdb)
+
+ (specify "pdb-parse-cmd-args"
+ (assert-equal '(nil ("pdb") ("foo") nil)
+ (pdb-parse-cmd-args '("pdb" "foo")))
+ (assert-equal '(nil ("pdb") ("program.py" "foo") nil)
+ (pdb-parse-cmd-args
+ '("pdb" "program.py" "foo")))
+ )
+ )
+
+(test-unit "pdb")
+
--- /dev/null
+(require 'test-unit)
+(load-file "../dbgr/debugger/pdb/init.el")
+
+(test-unit-clear-contexts)
+
+
+(setq bps-pat (gethash "brkpt-set" dbgr-pdb-pat-hash))
+(setq loc-pat (gethash "loc" dbgr-pdb-pat-hash))
+(setq prompt-pat (gethash "prompt" dbgr-pdb-pat-hash))
+(setq tb-pat (gethash "lang-backtrace" dbgr-pdb-pat-hash))
+
+(defun loc-match(text var)
+ (string-match (dbgr-loc-pat-regexp var) text)
+)
+
+(defun prompt-match(prompt-str msg-fmt)
+ (assert-equal 0 (loc-match prompt-str prompt-pat)
+ (format msg-fmt prompt-str))
+)
+
+;; FIXME: we get a void variable somewhere in here when running
+;; even though we define it in lexical-let. Dunno why.
+;; setq however will workaround this.
+(setq text " File \"/usr/lib/python2.6/code.py\", line 281, in raw_input")
+(context "traceback location matching"
+ (tag regexp-pdb)
+ (specify "basic traceback location"
+ (assert-t (numberp (loc-match text tb-pat))))
+ (specify "extract file name"
+ (assert-equal "/usr/lib/python2.6/code.py"
+ (match-string (dbgr-loc-pat-file-group tb-pat)
+ text)
+ (format "Failing file group is %s"
+ (dbgr-loc-pat-file-group tb-pat))))
+ (specify "extract line number"
+ (assert-equal "281"
+ (match-string (dbgr-loc-pat-line-group tb-pat)
+ text))
+ ))
+
+(context "breakpoint location matching"
+ (tag regexp-pdb)
+ (lexical-let ((text "Breakpoint 1 at /src/git/code/gcd.py:13"))
+ (specify "basic breakpoint location"
+ (assert-t (numberp (loc-match text bps-pat))))
+ (specify "extract breakpoint file name"
+ (assert-equal "/src/git/code/gcd.py"
+ (match-string (dbgr-loc-pat-file-group
+ bps-pat)
+ text)))
+ (specify "extract breakpoint line number"
+ (assert-equal "13"
+ (match-string (dbgr-loc-pat-line-group
+ bps-pat)
+ text)))
+ )
+ )
+
+(context "prompt matching"
+ (tag regexp-pdb)
+ ;; (lexical-let ((text "(c:\\working\\python\\helloworld.py:30): <module>"))
+ ;; (specify "MS DOS position location"
+ ;; (assert-t (numberp (loc-match text loc-pat))))
+ ;; (specify "extract file name"
+ ;; (assert-equal "c:\\working\\python\\helloworld.py"
+ ;; (match-string (dbgr-loc-pat-file-group loc-pat)
+ ;; text)
+ ;; (format "Failing file group is %s"
+ ;; (dbgr-loc-pat-file-group tb-pat))))
+ ;; (specify "extract line number"
+ ;; (assert-equal "30"
+ ;; (match-string (dbgr-loc-pat-line-group loc-pat)
+ ;; text)))
+
+ ;; )
+ (lexical-let ((text "> /usr/bin/ipython(24)<module>"))
+ (specify "position location"
+ (assert-t (numberp (loc-match text loc-pat))))
+ (specify "extract file name"
+ (assert-equal "/usr/bin/ipython"
+ (match-string (dbgr-loc-pat-file-group loc-pat)
+ text)
+ (format "Failing file group is %s"
+ (dbgr-loc-pat-file-group tb-pat))))
+ (specify "extract line number"
+ (assert-equal "24"
+ (match-string (dbgr-loc-pat-line-group
+ loc-pat)
+ text)))
+
+ )
+
+ (lexical-let ((prompt-str "(Pdb) "))
+ (specify "prompt matching"
+ (prompt-match prompt-str "valid debugger prompt: %s")
+ (setq prompt_str "((Pdb)) ")
+ (prompt-match prompt-str "valid nested debugger prompt: %s")
+ (setq prompt-str "Pdb) ")
+ (assert-nil (numberp (loc-match prompt-str prompt))
+ (format "%s %s" "invalid debugger prompt"
+ prompt-str))
+ )
+ )
+ )
+
+(test-unit "regexp-pdb")
+
)
(defun prompt-match(prompt-str)
- (assert-equal 0 (loc-match prompt-str prompt-pat))
+ (assert-equal 0 (loc-match prompt-str prompt-pat)
+ (format "valid prompt %s" prompt-str))
)
;; FIXME: we get a void variable somewhere in here when running
(prompt-match "((trepan)): ")
(prompt-match "((trepan@55)): ")
(prompt-match "((trepan@main)): ")
+ (assert-nil (loc-match "trepan:" prompt-pat)
+ (format "invalid prompt %s" prompt-str))
)
(specify "control-frame"