branch: elpa/teco
commit 225353f0ec12352d9cfab36ffcfe17f7ace1c70f
Author: Mark T. Kennedy <m...@acm.org>
Commit: Mark T. Kennedy <m...@acm.org>

    initial commit
---
 README.md |    1 +
 teco.el   | 2369 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 2370 insertions(+)

diff --git a/README.md b/README.md
new file mode 100644
index 0000000000..6ba0ee9ae0
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# teco
diff --git a/teco.el b/teco.el
new file mode 100644
index 0000000000..51cc9c0168
--- /dev/null
+++ b/teco.el
@@ -0,0 +1,2369 @@
+;;; teco.el --- Dale Worley's teco-in-elisp interpreter
+
+;; -*-byte-compile-dynamic-docstrings: nil;-*-
+;;; Commentary
+;;; Teco interpreter for Gnu Emacs, version 7.
+
+;; LCD Archive Entry:
+;; teco|Dale R. Worley|wor...@alum.mit.edu
+;; |Teco interpreter
+;; |96-09-02|version 7|~/packages/teco.el.Z
+
+;; This code has been tested some, but no doubt contains a zillion bugs.
+;; You have been warned.
+
+;; Some byte-compilers will not compile the function definitions for the
+;; Teco commands because the defuns are created by macros.  If you have this
+;; problem, I can send you the byte-compiled version.
+
+;; Written by Dale R. Worley based on a C implementation by Matt Fichtenbaum.
+;; Please send comments, bug fixes, enhancements, etc. to wor...@alum.mit.edu.
+
+;; WARRANTY DISCLAIMER
+
+;; This software was created by Dale R. Worley and is
+;; distributed free of charge.  It is placed in the public domain and
+;; permission is granted to anyone to use, duplicate, modify and redistribute
+;; it provided that this notice is attached.
+
+;; Dale R. Worley provides absolutely NO WARRANTY OF ANY KIND
+;; with respect to this software.  The entire risk as to the quality and
+;; performance of this software is with the user.  IN NO EVENT WILL DALE R.
+;; WORLEY BE LIABLE TO ANYONE FOR ANY DAMAGES ARISING OUT THE
+;; USE OF THIS SOFTWARE, INCLUDING, WITHOUT LIMITATION, DAMAGES RESULTING FROM
+;; LOST DATA OR LOST PROFITS, OR FOR ANY SPECIAL, INCIDENTAL OR CONSEQUENTIAL
+;; DAMAGES.
+
+;; Since much of this code is translated from the C version by
+;; Matt Fichtenbaum, I include his copyright notice:
+;; TECO for Ultrix.   Copyright 1986 Matt Fichtenbaum.
+;; This program and its components belong to GenRad Inc, Concord MA 01742.
+;; They may be copied if this copyright notice is included.
+
+;; Change log:
+
+;; Version 1
+;; Original implementation
+
+;; Version 2
+;; Fix bugs found by Alan Katz in S.
+
+;; Version 3
+;; Fix bugs found by Lum Johnson in key-binding code.
+;; Fix handling of ^C, ^G, and ^L in command input.
+;; Fix <...> so it iterates indefinitely.  (Found by Mark Henderson.)
+;; Fix ; so it exits from iterations correctly.
+;; Add FR and FS commands.
+;; Make commands that are supposed to set ^S do so.
+;; Make flow-control commands clear the @-flag, so the @-flag can be
+;; statically predicted during skipping of code.
+;; Set up immediate-action commands ?, /, and *q.
+
+;; Version 4
+;; The T, D, and K commands weren't clearing their arguments.
+;; Since ! is a flow-control command (O can go to it), it must clear the
+;; @-flag.
+
+;; Version 5
+;; Fix bug found by k...@kelp.boston.ma.us in teco:output, causing trace output
+;; to generate errors.  It also caused =, ==, and === operators to generate
+;; errors.
+;; Put in improved disclaimer of copyright and warranty.
+;; Added teco:version variable and function.
+
+;; Version 6
+;; Add attribution to "Philosophy".
+;; Fix handling of negative arguments to S.  (Fix due to Bill Freeman.)
+;; Fix order of arguments produced by ^Y.  (Fix due to Bill Freeman.)
+;; Add ES flag to control placement of cursor after reverse searches.
+;; (because different Tecos handle cursor placement differently)
+;; Altered expression computation so that "-" alone yields -1 as a value.
+;; (useful for -Sabc$)
+;; Add n,mL command, mostly for use with FW.
+;; Add FL and FW commands.
+;; Add FR and FS to list of commands.
+;; Add FE to execute Emacs lisp code.
+;; Add teco:copy-to-q-reg to make loading q-regs easier.
+;; Function 'teco' added as alias for 'teco:command', to make invocation with
+;; M-x easier.
+;; Fix an assortment of bugs and bad coding found by k...@kelp.boston.ma.us.
+
+;; Version 7
+;; Changed construction of teco:command-keymap to work in Emacs 19.30.
+;; (This may be non-compatible with Emacs 18.)
+;; Fixed minor bugs revealed by byte-compile.
+
+;; Version 8
+;; Change `last-command-char' to new `last-command-event'.
+;; Fixed some bugs revealed by byte-compile.
+;; Added support for evil, try to add the ability to remap
+;; teco:command-escape, which is one of the original feature
+;; of TECO.
+
+;; To be able to invoke Teco directly, do:
+;; (global-set-key "\C-z" 'teco:command)
+;;                                     ; or whatever key binding you want
+;; (autoload 'teco:command "teco"
+;;   "Read and execute a Teco command string."
+;;   t nil)
+;; (autoload 'teco "teco"
+;;   "Read and execute a Teco command string."
+;;   t nil)
+;;                                     ; 'teco' is an alias for 'teco:command'
+;; This can be useful for loading q-regs from an Emacs buffer:
+;; (global-set-key "\C-xy" 'teco:copy-to-q-reg)
+;;                                     ; or whatever key binding you want
+;; (autoload 'teco:copy-to-q-reg "teco"
+;;   "Copy region into Teco q-reg REG."
+;;   t nil)
+
+;; Differences from other Tecos:
+;; Character positions in the buffer are numbered in the Emacs way:  The first
+;; character is numbered 1 (or (point-min) if narrowing is in effect).  The
+;; B command returns that number.
+;; Ends of lines are represented by a single character (newline), so C and R
+;; skip over them, rather than 2C and 2R.
+;; All file I/O is left to the underlying Emacs.  Thus, almost all Ex commands
+;; are omitted.
+;; Immediate action commands are ?, /, and *q.
+
+;; Command set:
+;;     NUL     Not a command.
+;;     ^A      Output message to terminal (argument ends with ^A)
+;;     ^C      Exit macro
+;;     ^C^C    Stop execution
+;;     ^C      (type-in) abort command
+;;     ^D      Set radix to decimal
+;;     ^EA     (match char) Match alphabetics
+;;     ^EC     (match char) Match symbol constituents
+;;     ^ED     (match char) Match numerics
+;;     ^EGq    (match char) Match any char in q-reg
+;;     ^EL     (match char) Match line terminators
+;;     ^EQq    (string char) Use contents of q-reg
+;;     ^ER     (match char) Match alphanumerics
+;;     ^ES     (match char) Match non-null space/tab
+;;     ^EV     (match char) Match lower case alphabetic
+;;     ^EW     (match char) Match upper case alphabetic
+;;     ^EX     (match char) Match any char
+;;     ^G      (type-in) abort command
+;;     TAB     Insert tab and text
+;;     LF      Line terminator; Ignored in commands
+;;     VT      Ignored in commands
+;;     FF      Ignored in commands
+;;     FF      (type-in) redraw screen
+;;     CR      Ignored in commands
+;;     ^Nx     (match char) Match all but x
+;;     ^O      Set radix to octal
+;;     ^Q      Convert line argument into character argument
+;;     ^Qx     (string char) Use x literally
+;;     n^R     Set radix to n
+;;     :^R     Enter recursive edit
+;;     ^S      -(length of last referenced string)
+;;                     set by S, I, TAB, G, FR, FS, and \
+;;     ^S      (match char) match separator char
+;;     ^T      Ascii value of next character typed
+;;     n^T     Output Ascii character with value n
+;;     ^U      (type-in) Kill command line
+;;     ^Uq     Put text argument into q-reg
+;;     n^Uq    Put Ascii character 'n' into q-reg
+;;     :^Uq    Append text argument to q-reg
+;;     n:^Uq   Append character 'n' to q-reg
+;;     ^X      Set/get search mode flag
+;;     ^X      (match char) Match any character
+;;     ^Y      Equivalent to '.+^S,.'
+;;     ^Z      Not a Teco command
+;;     ESC     String terminator; absorbs arguments
+;;     ESC ESC (type-in) End command
+;;     ESC ESC Exit from macro
+;;     ^\      Not a Teco command
+;;     ^]      Not a Teco command
+;;     ^^x     Ascii value of the character x
+;;     ^_      One's complement (logical NOT)
+;;     !       Define label (argument ends with !)
+;;     "       Start conditional
+;;     n"<     Test for less than zero
+;;     n">     Test for greater than zero
+;;     n"=     Test for equal to zero
+;;     n"A     Test for alphabetic
+;;     n"C     Test for symbol constituent
+;;     n"D     Test for numeric
+;;     n"E     Test for equal to zero
+;;     n"F     Test for false
+;;     n"G     Test for greater than zero
+;;     n"L     Test for less than zero
+;;     n"N     Test for not equal to zero
+;;     n"R     Test for alphanumeric
+;;     n"S     Test for successful
+;;     n"T     Test for true
+;;     n"U     Test for unsuccessful
+;;     n"V     Test for lower case
+;;     n"W     Test for upper case
+;;     #       Logical OR
+;;     $       Not a Teco command
+;;     n%q     Add n to q-reg and return result
+;;     &       Logical AND
+;;     '       End conditional
+;;     (       Expression grouping
+;;     )       Expression grouping
+;;     *       Multiplication
+;;     *q      (immediate action) Copy last command into q-reg
+;;     +       Addition
+;;     ,       Argument separator
+;;     -       Subtraction or negation
+;;     .       Current pointer position
+;;     /       Division
+;;     /       (immediate action) Insert last command into command buffer
+;;     0-9     Digit
+;;     n<      Iterate n times
+;;     =       Type in decimal
+;;     :=      Type in decimal, no newline
+;;     ==      Type in octal
+;;     :==     Type in octal, no newline
+;;     ===     Type in hexadecimal
+;;     :===    Type in hexadecimal, no newline
+;;     ::      Make next search a compare
+;;     >       End iteration
+;;     ?       Toggle tracing
+;;     ?       (immediate action) Insert command string or macro that reported
+;;             last error, up to point of error, into command buffer
+;;     n:A     Get Ascii code of character at relative position n
+;;     B       Character position of beginning of buffer
+;;     nC      Advance n characters
+;;     nD      Delete n characters
+;;     n,mD    Delete characters between n and m
+;;     ET      Typeout control flag
+;;                     8       no echo for ^T
+;;                     32      no wait for ^T
+;;     ES      Search control flag
+;;                     1       leave pointer at end of found string
+;;                             after reverse search, rather than at beginning
+;;     FE      Execute text argument as Emacs lisp.  Uses string chars but
+;;             not match chars, so FE^EQq$ executes contents of q-reg
+;;     FL      Searches right or left over balanced parenthesized strings,
+;;             in the same way as FW searches over words
+;;             Syntax is defined by Emacs' syntax table
+;;     FR      Replace string found by previous match with text argument
+;;     FSaaa$bbb$
+;;             Search for string aaa and replace it with bbb.
+;;     nFW     Return arguments .,x where x is the other side of the n-th
+;;             word from point.  Main uses:  FWL moves right one word,
+;;             -FWL moves left one word, FWK deletes one word to right,
+;;             nFWXq puts n words into q-reg
+;;             "word" is defined by Emacs' syntax table
+;;     :FW     Like FW, but goes to near side of the n-th word
+;;     Gq      Get string from q-reg into buffer
+;;     :Gq     Type out q-reg
+;;     H       Equivalent to 'B,Z'
+;;     I       Insert text argument
+;;     nJ      Move pointer to character n
+;;     nK      Kill n lines
+;;     n,mK    Kill characters between n and m
+;;     nL      Advance n lines
+;;     n,mL    Same as n+m-.J, mostly for use by FW
+;;     Mq      Execute string in q-reg
+;;     O       Goto label
+;;     nO      Go to n-th label in list (0-origin)
+;;     Qq      Number in q-reg
+;;     nQq     Ascii value of n-th character in q-reg
+;;     :Qq     Size of text in q-reg
+;;     nR      Back up n characters
+;;     nS      Search
+;;     nT      Type n lines
+;;     n,mT    Type chars from n to m
+;;     nUq     Put number n into q-reg
+;;     nV      Type n lines around pointer
+;;     W       Redraw the display
+;;     nW      Put point on line n of the display
+;;     nXq     Put n lines into q-reg
+;;     n,mXq   Put characters from n to m into q-reg
+;;     n:Xq    Append n lines to q-reg q
+;;     n,m:Xq  Append characters from n to m into q-reg
+;;     Z       Pointer position at end of buffer
+;;     [q      Put q-reg on stack
+;;     \       Value of digit string in buffer
+;;     n\      Convert n to digits and insert in buffer
+;;     ]q      Pop q-reg from stack
+;;     :]q     Test whether stack is empty and return value
+;;     `       Not a Teco command
+;;     a-z     Treated the same as A-Z
+;;     {       Not a Teco command
+;;     |       Conditional 'else'
+;;     }       Not a Teco comand
+;;     ~       Not a Teco command
+;;     DEL     Delete last character typed in
+
+;; Special q-register names:
+;;
+;;     _       last search string
+;;     #       the current command string
+;;     *       last command string
+;;     %       the last command string or macro that returned an error,
+;;             to the point at which the error was found
+
+;; Philosophy:
+;;   Real programmers don't want "what you see is what you get", they want
+;;   "you asked for it, you got it".  They want editors that are terse,
+;;   powerful, cryptic, and unforgiving.  In a word, Teco.
+;; after Ed Post, "Real Programmers Don't Use Pascal", in Datamation,
+;; July 1983, p. 264
+
+;;; Code:
+;; (require 'backquote)
+
+;; The version number
+(defvar teco-version "7.9"
+  "The version of Teco.")
+
+(defun teco-version ()
+  "Return string describing the version of Teco.
+When called interactively, displays the version."
+  (interactive)
+  (if (called-interactively-p)
+      (message "Teco version %s" (teco-version))
+    teco-version))
+
+;; set a range of elements of an array to a value
+(defun teco:set-elements (array start end value)
+  (let ((i start))
+    (while (<= i end)
+      (aset array i value)
+      (setq i (1+ i)))))
+
+;; set a range of elements of an array to their indexes plus an offset
+(defun teco:set-elements-index (array start end offset)
+  (let ((i start))
+    (while (<= i end)
+      (aset array i (+ i offset))
+      (setq i (1+ i)))))
+
+(defvar teco:command-string ""
+  "The current command string being executed.")
+
+(defvar teco:command-pointer nil
+  "Pointer into teco:command-string showing next character to be executed.")
+
+(defvar teco:ctrl-r 10
+  "Current number radix.")
+
+(defvar teco:et-flag 0
+  "ET flags:
+8      do not echo ^T input
+32     do not wait for ^T input if no character is available.")
+
+(defvar teco:es-flag 0
+  "ES flags:
+1      leave pointer at end of found string after reverse search,
+rather than at beginning.")
+
+(defvar teco:ctrl-s 0
+  "The negative of the length of the last string inserted or searched for.
+Set by the S, I, TAB, G, FR, FS, and \\ commands.")
+
+(defvar teco:digit-switch nil
+  "Set if we have just executed a digit.")
+
+(defvar teco:exp-exp nil
+  "Expression value preceeding operator.")
+
+(defvar teco:exp-val1 nil
+  "Current argument value.")
+
+(defvar teco:exp-val2 nil
+  "Argument before comma.")
+
+(defvar teco:exp-flag1 nil
+  "t if argument is present.")
+
+(defvar teco:exp-flag2 nil
+  "t if argument before comma is present.")
+
+(defvar teco:exp-op nil
+  "Pending arithmetic operation on argument.")
+
+(defvar teco:exp-stack nil
+  "Stack for parenthesized expressions.")
+
+(defvar teco:macro-stack nil
+  "Stack for macro invocations.")
+
+(defvar teco:mapch-l nil
+  "Translation table to lower-case letters.")
+
+    (setq teco:mapch-l (make-vector 256 0))
+    (teco:set-elements-index teco:mapch-l 0 255 0)
+    (teco:set-elements-index teco:mapch-l ?A ?Z (- ?a ?A))
+
+(defvar teco:trace nil
+  "t if tracing is on.")
+
+(defvar teco:at-flag nil
+  "t if an @ flag is pending.")
+
+(defvar teco:colon-flag nil
+  "1 if a : flag is pending, 2 if a :: flag is pending.")
+
+(defvar teco:qspec-valid nil
+  "Flags describing whether a character is a vaid q-register name.
+3 means yes, 2 means yes but only for file and search operations.")
+
+    (setq teco:qspec-valid (make-vector 256 0))
+    (teco:set-elements teco:qspec-valid ?a ?z 3)
+    (teco:set-elements teco:qspec-valid ?0 ?9 3)
+    (aset teco:qspec-valid ?_ 2)
+    (aset teco:qspec-valid ?# 2)
+    (aset teco:qspec-valid ?* 2)
+    (aset teco:qspec-valid ?% 2)
+
+(defvar teco:iteration-stack nil
+  "Iteration list.")
+
+(defvar teco:cond-stack nil
+  "Conditional stack.")
+
+(defvar teco:qreg-text (make-vector 256 "")
+  "The text contents of the q-registers.")
+
+(defvar teco:qreg-number (make-vector 256 0)
+  "The number contents of the q-registers.")
+
+(defvar teco:qreg-stack nil
+  "The stack of saved q-registers.")
+
+(defconst teco:prompt "*"
+  "*Prompt to be used when inputting Teco command.")
+
+(defconst teco:exec-1 (make-vector 256 nil)
+  "Names of routines handling type 1 characters (characters that are
+part of expression processing).")
+
+(defconst teco:exec-2 (make-vector 256 nil)
+  "Names of routines handling type 2 characters (characters that are
+not part of expression processing).")
+
+(defvar teco:last-search-regexp ""
+  "Regexp version of last search string (q-reg '_').")
+
+(defvar teco:search-result 0
+  "Result of the last search.")
+
+(defmacro teco:define-type-1 (char &rest body)
+  "Define the code to process a type 1 character.
+Transforms
+  (teco:define-type-1 ?x
+    code ...)
+into
+        (defun teco:type-1-x ()
+    code ...)
+and does
+  (aset teco:exec-1 ?x 'teco:type-1-x)"
+  (let ((s (intern (concat "teco:type-1-" (char-to-string char)))))
+    `(progn
+       (defun ,s ()
+         ,@body)
+       (aset teco:exec-1 ,char (quote ,s)))))
+
+(defmacro teco:define-type-2 (char &rest body)
+  "Define the code to process a type 2 character.
+Transforms
+  (teco:define-type-2 ?x
+    code ...)
+into
+        (defun teco:type-2-x ()
+    code ...)
+and does
+  (aset teco:exec-2 ?x 'teco:type-2-x)"
+  (let ((s (intern (concat "teco:type-2-" (char-to-string char)))))
+    `(progn
+       (defun ,s ()
+         ,@body)
+       (aset teco:exec-2 ,char (quote ,s)))))
+
+(defconst teco:char-types (make-vector 256 0)
+  "Define the characteristics of characters, as tested by \":
+  1    alphabetic
+  2    alphabetic, $, or .
+  4    digit
+  8    alphabetic or digit
+  16   lower-case alphabetic
+  32   upper-case alphabetic")
+
+(teco:set-elements teco:char-types ?0 ?9 (+ 4 8))
+(teco:set-elements teco:char-types ?A ?Z (+ 1 2 8 32))
+(teco:set-elements teco:char-types ?a ?z (+ 1 2 8 16))
+(aset teco:char-types ?$ 2)
+(aset teco:char-types ?. 2)
+
+(defconst teco:error-texts '(("BNI" . "> not in iteration")
+                             ("CPQ" . "Can't pop Q register")
+                             ("COF" . "Can't open output file ")
+                             ("FNF" . "File not found ")
+                             ("IEC" . "Invalid E character")
+                             ("IFC" . "Invalid F character")
+                             ("IIA" . "Invalid insert arg")
+                             ("ILL" . "Invalid command")
+                             ("ILN" . "Invalid number")
+                             ("IPA" . "Invalid P arg")
+                             ("IQC" . "Invalid \" character")
+                             ("IQN" . "Invalid Q-reg name")
+                             ("IRA" . "Invalid radix arg")
+                             ("ISA" . "Invalid search arg")
+                             ("ISS" . "Invalid search or text string")
+                             ("IUC" . "Invalid ^ character")
+                             ("LNF" . "Label not found")
+                             ("MEM" . "Insufficient memory available")
+                             ("MRP" . "Missing )")
+                             ("NAB" . "No arg before ^_")
+                             ("NAC" . "No arg before ,")
+                             ("NAE" . "No arg before =")
+                             ("NAP" . "No arg before )")
+                             ("NAQ" . "No arg before \"")
+                             ("NAS" . "No arg before ;")
+                             ("NAU" . "No arg before U")
+                             ("NFI" . "No file for input")
+                             ("NFO" . "No file for output")
+                             ("NYA" . "Numeric arg with Y")
+                             ("OFO" . "Output file already open")
+                             ("PDO" . "Pushdown list overflow")
+                             ("POP" . "Pointer off page")
+                             ("SNI" . "; not in iteration")
+                             ("SRH" . "Search failure ")
+                             ("STL" . "String too long")
+                             ("UTC" . "Unterminated command")
+                             ("UTM" . "Unterminated macro")
+                             ("XAB" . "Execution interrupted")
+                             ("YCA" . "Y command suppressed")
+                             ("IWA" . "Invalid W arg")
+                             ("NFR" . "Numeric arg with FR")
+                             ("INT" . "Internal error")
+                             ("EFI" . "EOF read from std input")
+                             ("IAA" . "Invalid A arg")
+                             ))
+
+(defconst teco:spec-chars
+  [0          1          0          0  ; ^@ ^A ^B ^C
+              0          64         0          0       ; ^D ^E ^F ^G
+              0          2          128        128     ; ^H ^I ^J ^K
+              128        0          64         0       ; ^L ^M ^N ^O
+              0          64         64         64      ; ^P ^Q ^R ^S
+              0          34         0          0       ; ^T ^U ^V ^W
+              64         0          0          0       ; ^X ^Y ^Z ^\[
+              0          0          1          0       ; ^\ ^\] ^^ ^_
+              0          1025       1040       0       ;    !  \"  #
+              0          0          0          1040; $  %  &  '
+              0          0          0          0       ; \(  \)  *  +
+              0          0          0          0       ; ,  -  .  /
+              0          0          0          0       ; 0  1  2  3
+              0          0          0          0       ; 4  5  6  7
+              0          0          0          0       ; 8  9  :  ;
+              1040       0          1040       0       ; <  =  >  ?
+              1          0          12         0       ; @  A  B  C
+              0          9          1          32      ; D  E  F  G
+              0          2          0          0       ; H  I  J  K
+              0          32         10         1026; L  M  N  O
+              0          32         8          514     ; P  Q  R  S
+              0          32         0          4       ; T  U  V  W
+              32         0          0          32      ; X  Y  Z  \[
+              0          32         1          2       ; \  \]  ^  _
+              0          0          12         0       ; `  a  b  c
+              0          9          1          32      ; d  e  f  g
+              0          2          0          0       ; h  i  j  k
+              0          32         10         1026; l  m  n  o
+              0          32         8          514     ; p  q  r  s
+              0          32         0          4       ; t  u  v  w
+              32         0          0          0       ; x  y  z  {
+              16         0          0          0       ; |  }  ~  DEL
+              ]
+  "The special properties of characters:
+  1    skipto() special character
+  2    command with std text argument
+  4    E<char> takes a text argument
+  8    F<char> takes a text argument
+  16   char causes skipto() to exit
+  32   command with q-register argument
+  64   special char in search string
+  128  character is a line separator
+  256  command with a double text argument
+  512  F<char> takes a double text argument
+  1024 transfer of control command")
+
+
+(defun teco:execute-command (string)
+  "Execute teco command string."
+  ;; Initialize everything
+  (let ((teco:command-string string)
+        (teco:command-pointer 0)
+        (teco:digit-switch nil)
+        (teco:exp-exp nil)
+        (teco:exp-val1 nil)
+        (teco:exp-val2 nil)
+        (teco:exp-flag1 nil)
+        (teco:exp-flag2 nil)
+        (teco:exp-op 'start)
+        (teco:trace nil)
+        (teco:at-flag nil)
+        (teco:colon-flag nil)
+        (teco:iteration-stack nil)
+        (teco:cond-stack nil)
+        (teco:exp-stack nil)
+        (teco:macro-stack nil)
+        (teco:qreg-stack nil)
+        (teco:search-result 0))
+    ;; save command string
+    (aset teco:qreg-text ?* (aref teco:qreg-text ?#))
+    (aset teco:qreg-text ?# string)
+    ;; initialize output
+    (teco:out-init)
+    ;; execute commands
+    (catch 'teco:exit
+      (while t
+        ;; get next command character
+        (let ((cmdc (teco:get-command0 teco:trace)))
+          ;; if it's ^, interpret the next character as a control character
+          (if (eq cmdc ?^)
+              (setq cmdc (logand (teco:get-command teco:trace) 31)))
+          (if (and (<= ?0 cmdc) (<= cmdc ?9))
+              ;; process a number
+              (progn
+                (setq cmdc (- cmdc ?0))
+                ;; check for invalid digit
+                (if (>= cmdc teco:ctrl-r)
+                    (teco:error "ILN"))
+                (if teco:digit-switch
+                    ;; later digits
+                    (setq teco:exp-val1
+                          (+ (* teco:exp-val1 teco:ctrl-r) cmdc))
+                  ;; first digit
+                  (setq teco:exp-val1 cmdc)
+                  (setq teco:digit-switch t))
+                ;; indicate a value was read in
+                (setq teco:exp-flag1 t))
+            ;; not a digit
+            (setq teco:digit-switch nil)
+            ;; cannonicalize the case
+            (setq cmdc (aref teco:mapch-l cmdc))
+            ;; dispatch on the character, if it is a type 1 character
+            (let ((r (aref teco:exec-1 cmdc)))
+              (if r
+                  (funcall r)
+                ;; if a value has been entered, process any pending operation
+                (if teco:exp-flag1
+                    (cond ((eq teco:exp-op 'start)
+                           nil)
+                          ((eq teco:exp-op 'add)
+                           (setq teco:exp-val1 (+ teco:exp-exp teco:exp-val1))
+                           (setq teco:exp-op 'start))
+                          ((eq teco:exp-op 'sub)
+                           (setq teco:exp-val1 (- teco:exp-exp teco:exp-val1))
+                           (setq teco:exp-op 'start))
+                          ((eq teco:exp-op 'mult)
+                           (setq teco:exp-val1 (* teco:exp-exp teco:exp-val1))
+                           (setq teco:exp-op 'start))
+                          ((eq teco:exp-op 'div)
+                           (setq teco:exp-val1
+                                 (if (/= teco:exp-val1 0)
+                                     (/ teco:exp-exp teco:exp-val1)
+                                   0))
+                           (setq teco:exp-op 'start))
+                          ((eq teco:exp-op 'and)
+                           (setq teco:exp-val1
+                                 (logand teco:exp-exp teco:exp-val1))
+                           (setq teco:exp-op 'start))
+                          ((eq teco:exp-op 'or)
+                           (setq teco:exp-val1
+                                 (logior teco:exp-exp teco:exp-val1))
+                           (setq teco:exp-op 'start)))
+                  ;; a solitary '-' yields -1
+                  (if (eq teco:exp-op 'sub)
+                      (setq teco:exp-val1 -1
+                            teco:exp-op 'start
+                            teco:exp-flag1 t)))
+                ;; dispatch on a type 2 character
+                (let ((r (aref teco:exec-2 cmdc)))
+                  (if r
+                      (funcall r)
+                    (teco:error "ILL")))))))))))
+
+;; Type 1 commands
+
+(teco:define-type-1
+ ?\^m                                  ; CR
+ nil)
+
+(teco:define-type-1
+ ?\n                                   ; LF
+ nil)
+
+(teco:define-type-1
+ ?\^k                                  ; VT
+ nil)
+
+(teco:define-type-1
+ ?\^l                                  ; FF
+ nil)
+
+(teco:define-type-1
+ 32                                    ; SPC
+ nil)
+
+(teco:define-type-1
+ ?\e                                   ; ESC
+ (if (teco:peek-command ?\e)
+     ;; ESC ESC terminates macro or command
+     (teco:pop-macro-stack)
+   ;; otherwise, consume arguments
+   (setq teco:exp-flag1 nil
+         teco:exp-flag2 nil
+         teco:exp-op 'start)))
+
+(teco:define-type-1
+ ?!                                    ; !
+ (while (/= (teco:get-command teco:trace) ?!)
+   nil)
+ (setq teco:at-flag nil))
+
+(teco:define-type-1
+ ?@                                    ; @
+ ;; set at-flag
+ (setq teco:at-flag t))
+
+(teco:define-type-1
+ ?:                                    ; :
+ ;; is it '::'?
+ (if (teco:peek-command ?:)
+     (progn
+       ;; skip second colon
+       (teco:get-command teco:trace)
+       ;; set flag to show two colons
+       (setq teco:colon-flag 2))
+   ;; set flag to show one colon
+   (setq teco:colon-flag 1)))
+
+(teco:define-type-1
+ ??                                    ; ?
+ ;; toggle trace
+ (setq teco:trace (not teco:trace)))
+
+(teco:define-type-1
+ ?.                                    ; .
+ ;; value is point
+ (setq teco:exp-val1 (point)
+       teco:exp-flag1 t))
+
+(teco:define-type-1
+ ?z                                    ; z
+ ;; value is point-max
+ (setq teco:exp-val1 (point-max)
+       teco:exp-flag1 t))
+
+(teco:define-type-1
+ ?b                                    ; b
+ ;; value is point-min
+ (setq teco:exp-val1 (point-min)
+       teco:exp-flag1 t))
+
+(teco:define-type-1
+ ?h                                    ; h
+ ;; value is b,z
+ (setq teco:exp-val1 (point-max)
+       teco:exp-val2 (point-min)
+       teco:exp-flag1 t
+       teco:exp-flag2 t
+       teco:exp-op 'start))
+
+(teco:define-type-1
+ ?\^s                                  ; ^s
+ ;; value is - length of last insert, etc.
+ (setq teco:exp-val1 teco:ctrl-s
+       teco:exp-flag1 t))
+
+(teco:define-type-1
+ ?\^y                                  ; ^y
+ ;; value is .+^S,.
+ (setq teco:exp-val1 (point)
+       teco:exp-val2 (+ (point) teco:ctrl-s)
+       teco:exp-flag1 t
+       teco:exp-flag2 t
+       teco:exp-op 'start))
+
+(teco:define-type-1
+ ?\(                                   ; \(
+ ;; push expression stack
+ (teco:push-exp-stack)
+ (setq teco:exp-flag1 nil
+       teco:exp-flag2 nil
+       teco:exp-op 'start))
+
+(teco:define-type-1
+ ?\C-^                                 ; ^^
+ ;; get next command character
+ (setq teco:exp-val1 (teco:get-command teco:trace)
+       teco:exp-flag1 t))
+
+
+;; Type 2 commands
+(teco:define-type-2
+ ?+                                    ; +
+ (setq teco:exp-exp (if teco:exp-flag1 teco:exp-val1 0)
+       teco:exp-flag1 nil
+       teco:exp-op 'add))
+
+(teco:define-type-2
+ ?-                                    ; -
+ (setq teco:exp-exp (if teco:exp-flag1 teco:exp-val1 0)
+       teco:exp-flag1 nil
+       teco:exp-op 'sub))
+
+(teco:define-type-2
+ ?*                                    ; *
+ (setq teco:exp-exp (if teco:exp-flag1 teco:exp-val1 0)
+       teco:exp-flag1 nil
+       teco:exp-op 'mult))
+
+(teco:define-type-2
+ ?/                                    ; /
+ (setq teco:exp-exp (if teco:exp-flag1 teco:exp-val1 0)
+       teco:exp-flag1 nil
+       teco:exp-op 'div))
+
+(teco:define-type-2
+ ?&                                    ; &
+ (setq teco:exp-exp (if teco:exp-flag1 teco:exp-val1 0)
+       teco:exp-flag1 nil
+       teco:exp-op 'and))
+
+(teco:define-type-2
+ ?#                                    ; #
+ (setq teco:exp-exp (if teco:exp-flag1 teco:exp-val1 0)
+       teco:exp-flag1 nil
+       teco:exp-op 'or))
+
+(teco:define-type-2
+ ?\)                                   ; \)
+ (if (or (not teco:exp-flag1) (not teco:exp-stack))
+     (teco:error "NAP"))
+ (let ((v teco:exp-val1))
+   (teco:pop-exp-stack)
+   (setq teco:exp-val1 v
+         teco:exp-flag1 t)))
+
+(teco:define-type-2
+ ?,                                    ; ,
+ (if (not teco:exp-flag1)
+     (teco:error "NAC"))
+ (setq teco:exp-val2 teco:exp-val1
+       teco:exp-flag2 t
+       teco:exp-flag1 nil))
+
+(teco:define-type-2
+ ?\^_                                  ; ^_
+ (if (not teco:exp-flag1)
+     (teco:error "NAB")
+   (setq teco:exp-val1 (lognot teco:exp-val1))))
+
+(teco:define-type-2
+ ?\^d                                  ; ^d
+ (setq teco:ctrl-r 10
+       teco:exp-flag1 nil
+       teco:exp-op 'start))
+
+(teco:define-type-2
+ ?\^o                                  ; ^o
+ (setq teco:ctrl-r 8
+       teco:exp-flag1 nil
+       teco:exp-op 'start))
+
+(teco:define-type-2
+ ?\^r                                  ; ^r
+ (if teco:colon-flag
+     (progn
+       (recursive-edit)
+       (setq teco:colon-flag nil))
+   (if teco:exp-flag1
+       ;; set radix
+       (progn
+         (if (and (/= teco:exp-val1 8)
+                  (/= teco:exp-val1 10)
+                  (/= teco:exp-val1 16))
+             (teco:error "IRA"))
+         (setq teco:ctrl-r teco:exp-val1
+               teco:exp-flag1 nil
+               teco:exp-op 'start))
+     ;; get radix
+     (setq teco:exp-val1 teco:ctrl-r
+           teco:exp-flag1 t))))
+
+(teco:define-type-2
+ ?\^c                                  ; ^c
+ (if (teco:peek-command ?\^c)
+     ;; ^C^C stops execution
+     (throw 'teco:exit nil)
+   (if teco:macro-stack
+       ;; ^C inside macro exits macro
+       (teco:pop-macro-stack)
+     ;; ^C in command stops execution
+     (throw 'teco:exit nil))))
+
+(teco:define-type-2
+ ?\^x                                  ; ^x
+ ;; set/get search mode flag, which is case-fold-search
+ (let ((x-flag (if case-fold-search 0 -1)))
+   (teco:set-var 'x-flag)
+   (setq case-fold-search (= x-flag 0))))
+
+(teco:define-type-2
+ ?m                                    ; m
+ (let ((macro-name (teco:get-qspec nil
+                                   (teco:get-command teco:trace))))
+   (teco:push-macro-stack)
+   (setq teco:command-string (aref teco:qreg-text macro-name)
+         teco:command-pointer 0)))
+
+(teco:define-type-2
+ ?<                                    ; <
+ ;; begin iteration
+ (if (and teco:exp-flag1 (<= teco:exp-val1 0))
+     ;; if this is not to be executed, just skip the
+     ;; intervening stuff
+     (teco:find-enditer)
+   ;; push iteration stack
+   (teco:push-iter-stack teco:command-pointer
+                         teco:exp-flag1 teco:exp-val1)
+   ;; consume the argument
+   (setq teco:exp-flag1 nil
+         teco:at-flag nil)))
+
+(teco:define-type-2
+ ?>                                    ; >
+ ;; end iteration
+ (if (not teco:iteration-stack)
+     (teco:error "BNI"))
+ ;; decrement count and pop conditionally
+ (teco:pop-iter-stack nil)
+ ;; consume arguments
+ (setq teco:exp-flag1 nil
+       teco:exp-flag2 nil
+       teco:exp-op 'start
+       teco:at-flag nil))
+
+(teco:define-type-2
+ 59                                    ; ;
+ ;; semicolon iteration exit
+ (if (not teco:iteration-stack)
+     (teco:error "SNI"))
+ ;; if exit
+ (if (if (>= (if teco:exp-flag1
+                 teco:exp-val1
+               teco:search-result) 0)
+         (not teco:colon-flag)
+       teco:colon-flag)
+     (progn
+       (teco:find-enditer)
+       (teco:pop-iter-stack t)))
+ ;; consume argument and colon
+ (setq teco:exp-flag1 nil
+       teco:colon-flag nil
+       teco:exp-op 'start))
+
+(teco:define-type-2
+ ?\"                                   ; \"
+ ;; must be an argument
+ (if (not teco:exp-flag1)
+     (teco:error "NAQ"))
+ ;; consume argument
+ (setq teco:exp-flag1 nil
+       teco:exp-op 'start)
+ (let* (;; get the test specification
+        (c (aref teco:mapch-l (teco:get-command teco:trace)))
+        ;; determine whether the test is true
+        (test (cond ((eq c ?a)
+                     (/= (logand (aref teco:char-types teco:exp-val1)
+                                 1) 0))
+                    ((eq c ?c)
+                     (/= (logand (aref teco:char-types teco:exp-val1)
+                                 2) 0))
+                    ((eq c ?d)
+                     (/= (logand (aref teco:char-types teco:exp-val1)
+                                 4) 0))
+                    ((or (eq c ?e) (eq c ?f) (eq c ?u) (eq c ?=))
+                     (= teco:exp-val1 0))
+                    ((or (eq c ?g) (eq c ?>))
+                     (> teco:exp-val1 0))
+                    ((or (eq c ?l) (eq c ?s) (eq c ?t) (eq c ?<))
+                     (< teco:exp-val1 0))
+                    ((eq c ?n)
+                     (/= teco:exp-val1 0))
+                    ((eq c ?r)
+                     (/= (logand (aref teco:char-types teco:exp-val1)
+                                 8) 0))
+                    ((eq c ?v)
+                     (/= (logand (aref teco:char-types teco:exp-val1)
+                                 16) 0))
+                    ((eq c ?w)
+                     (/= (logand (aref teco:char-types teco:exp-val1)
+                                 32) 0))
+                    (t
+                     (teco:error "IQC")))))
+   (if (not test)
+       ;; if the conditional isn't satisfied, read
+       ;; to matching | or '
+       ;; ll counts the number of conditionals we are inside of
+       (let ((ll 1)
+             c)
+         (while (> ll 0)
+           ;; skip to the next significant character
+           (while (progn (setq c (teco:skipto))
+                         (and (/= c ?\")
+                              (/= c ?|)
+                              (/= c ?\')))
+             nil)
+           (if (= c ?\")
+               (setq ll (1+ ll))
+             (if (= c ?\')
+                 (setq ll (1- ll))
+               (if (= ll 1)
+                   (setq ll 0)         ; for immediate exit if | at ll=1
+                 ))))))
+   ;; clear at-flag
+   (setq teco:at-flag nil)))
+
+(teco:define-type-2
+ ?'                                    ; '
+ ;; no effect if executing
+ ;; clear at-flag
+ (setq teco:at-flag nil))
+
+(teco:define-type-2
+ ?|                                    ; |
+ (let ((ll 1)
+       c)
+   (while (> ll 0)
+     (while (progn (setq c (teco:skipto))
+                   (and (/= c ?\")
+                        (/= c ?\')))
+       nil)
+     (if (= c ?\")
+         (setq ll (1+ ll))
+       (setq ll (1- ll))))))
+
+(teco:define-type-2
+ ?u                                    ; u
+ (if (not teco:exp-flag1)
+     (teco:error "NAU"))
+ (aset teco:qreg-number
+       (teco:get-qspec 0 (teco:get-command teco:trace))
+       teco:exp-val1)
+ (setq teco:exp-flag1 teco:exp-flag2   ; command's value is second arg
+       teco:exp-val1 teco:exp-val2
+       teco:exp-flag2 nil
+       teco:exp-op 'start))
+
+(teco:define-type-2
+ ?q                                    ; q
+ ;; Qn is numeric val, :Qn is # of chars, mQn is mth char
+ (let ((mm (teco:get-qspec (or teco:colon-flag teco:exp-flag1)
+                           (teco:get-command teco:trace))))
+   (if (not teco:exp-flag1)
+       (setq teco:exp-val1 (if teco:colon-flag
+                               ;; :Qn
+                               (length (aref teco:qreg-text mm))
+                             ;; Qn
+                             (aref teco:qreg-number mm))
+             teco:exp-flag1 t)
+     ;; mQn
+     (let ((v (aref teco:qreg-text mm)))
+       (setq teco:exp-val1 (condition-case nil
+                               (aref v teco:exp-val1)
+                             (error -1))
+             teco:exp-op 'start)))
+   (setq teco:colon-flag nil)))
+
+(teco:define-type-2
+ ?%                                    ; %
+ (let* ((mm (teco:get-qspec nil (teco:get-command teco:trace)))
+        (v (+ (aref teco:qreg-number mm) (teco:get-value 1))))
+   (aset teco:qreg-number mm v)
+   (setq teco:exp-val1 v
+         teco:exp-flag1 t)))
+
+(teco:define-type-2
+ ?c                                    ; c
+ (let ((p (+ (point) (teco:get-value 1))))
+   (if (or (< p (point-min)) (> p (point-max)))
+       (teco:error "POP")
+     (goto-char p)
+     (setq teco:exp-flag2 nil))))
+
+(teco:define-type-2
+ ?r                                    ; r
+ (let ((p (- (point) (teco:get-value 1))))
+   (if (or (< p (point-min)) (> p (point-max)))
+       (teco:error "POP")
+     (goto-char p)
+     (setq teco:exp-flag2 nil))))
+
+(teco:define-type-2
+ ?j                                    ; j
+ (let ((p (teco:get-value (point-min))))
+   (if (or (< p (point-min)) (> p (point-max)))
+       (teco:error "POP")
+     (goto-char p)
+     (setq teco:exp-flag1 nil
+           teco:exp-flag2 nil))))
+
+(teco:define-type-2
+ ?l                                    ; l
+ ;; move forward by lines
+ (let* ((ll (teco:line-args))
+        (p (+ (car ll) (cdr ll) (- (point)))))
+   (if (or (< p (point-min)) (> p (point-max)))
+       (teco:error "POP")
+     (goto-char p))))
+
+(teco:define-type-2
+ ?\C-q                                 ; ^q
+ ;; number of characters until the nth line feed
+ (setq teco:exp-val1 (teco:lines (teco:get-value 1))
+       teco:exp-flag1 t))
+
+(teco:define-type-2
+ ?=                                    ; =
+ ;; print numeric value
+ (if (not teco:exp-flag1)
+     (teco:error "NAE"))
+ (teco:output (format
+               (if (teco:peek-command ?=)
+                   ;; at least one more =
+                   (progn
+                     ;; read past it
+                     (teco:get-command teco:trace)
+                     (if (teco:peek-command ?=)
+                         ;; another?
+                         (progn
+                           ;; read it too
+                           (teco:get-command teco:trace)
+                           ;; print in hex
+                           "%x")
+                       ;; print in octal
+                       "%o"))
+                 ;; print in decimal
+                 "%d")
+               teco:exp-val1))
+ ;; add newline if no colon
+ (if (not teco:colon-flag)
+     (teco:output ?\n))
+ ;; absorb argument, etc.
+ (setq teco:exp-flag1 nil
+       teco:exp-flag2 nil
+       teco:colon-flag nil
+       teco:exp-op 'start))
+
+(teco:define-type-2
+ ?\t                                   ; TAB
+ (if teco:exp-flag1
+     (teco:error "IIA"))
+ (let ((text (teco:get-text-arg)))
+   (insert ?\t text)
+   (setq teco:ctrl-s (- (1+ (length text)))))
+ ;; clear arguments
+ (setq teco:colon-flag nil
+       teco:exp-flag1 nil
+       teco:exp-flag2 nil))
+
+(teco:define-type-2
+ ?i                                    ; i
+ (let ((text (teco:get-text-arg)))
+   (if teco:exp-flag1
+       ;; if a nI$ command
+       (progn
+         ;; text argument must be null
+         (or (string-equal text "") (teco:error "IIA"))
+         ;; insert the character
+         (insert teco:exp-val1)
+         (setq teco:ctrl-s -1)
+         ;; consume argument
+         (setq teco:exp-op 'start))
+     ;; otherwise, insert the text
+     (insert text)
+     (setq teco:ctrl-s (- (length text))))
+   ;; clear arguments
+   (setq teco:colon-flag nil
+         teco:exp-flag1 nil
+         teco:exp-flag2 nil)))
+
+(teco:define-type-2
+ ?t                                    ; t
+ (let ((args (teco:line-args)))
+   (teco:output (buffer-substring (car args) (cdr args)))))
+
+(teco:define-type-2
+ ?v                                    ; v
+ (let ((ll (teco:get-value 1)))
+   (teco:output (buffer-substring (+ (point) (teco:lines (- 1 ll)))
+                                  (+ (point) (teco:lines ll))))))
+
+(teco:define-type-2
+ ?\C-a                                 ; ^a
+ (teco:output (teco:get-text-arg nil ?\C-a))
+ (setq teco:at-flag nil
+       teco:colon-flag nil
+       teco:exp-flag1 nil
+       teco:exp-flag2 nil
+       teco:exp-op 'start))
+
+(teco:define-type-2
+ ?d                                    ; d
+ (if (not teco:exp-flag2)
+     ;; if only one argument
+     (delete-char (teco:get-value 1))
+   ;; if two arguments, treat as n,mK
+   (let ((ll (teco:line-args)))
+     (delete-region (car ll) (cdr ll)))))
+
+(teco:define-type-2
+ ?k                                    ; k
+ (let ((ll (teco:line-args)))
+   (delete-region (car ll) (cdr ll))))
+
+(teco:define-type-2
+ ?\C-u                                 ; ^u
+ (let* ((mm (teco:get-qspec nil (teco:get-command teco:trace)))
+        (text-arg (teco:get-text-arg))
+        (text (if (not teco:exp-flag1)
+                  text-arg
+                (if (string-equal text-arg "")
+                    (char-to-string teco:exp-val1)
+                  (teco:error "IIA")))))
+   ;; if :, append to the register
+   (aset teco:qreg-text mm (if teco:colon-flag
+                               (concat (aref teco:qreg-text mm) text)
+                             text))
+   ;; clear various flags
+   (setq teco:exp-flag1 nil
+         teco:at-flag nil
+         teco:colon-flag nil
+         teco:exp-flag1 nil)))
+
+(teco:define-type-2
+ ?x                                    ; x
+ (let* ((mm (teco:get-qspec nil (teco:get-command teco:trace)))
+        (args (teco:line-args))
+        (text (buffer-substring (car args) (cdr args))))
+   ;; if :, append to the register
+   (aset teco:qreg-text mm (if teco:colon-flag
+                               (concat (aref teco:qreg-text mm) text)
+                             text))
+   ;; clear various flags
+   (setq teco:at-flag nil
+         teco:colon-flag nil)))
+
+(teco:define-type-2
+ ?g                                    ; g
+ (let ((mm (teco:get-qspec t (teco:get-command teco:trace))))
+   (if teco:colon-flag
+       (teco:output (aref teco:qreg-text mm))
+     (let ((text (aref teco:qreg-text mm)))
+       (insert text)
+       (setq teco:ctrl-s (- (length text)))))
+   (setq teco:colon-flag nil)))
+
+(teco:define-type-2
+ ?\[                                   ; \[
+ (let ((mm (teco:get-qspec t (teco:get-command teco:trace))))
+   (setq teco:qreg-stack
+         (cons (cons (aref teco:qreg-text mm)
+                     (aref teco:qreg-number mm))
+               teco:qreg-stack))))
+
+(teco:define-type-2
+ ?\]                                   ; \]
+ (let ((mm (teco:get-qspec t (teco:get-command teco:trace))))
+   (if teco:colon-flag
+       (setq teco:exp-flag1 t
+             teco:exp-val1 (if teco:qreg-stack -1 0))
+     (if teco:qreg-stack
+         (let ((pop (car teco:qreg-stack)))
+           (aset teco:qreg-text mm (car pop))
+           (aset teco:qreg-number mm (cdr pop))
+           (setq teco:qreg-stack (cdr teco:qreg-stack)))
+       (teco:error "CPQ")))
+   (setq teco:colon-flag nil)))
+
+(teco:define-type-2
+ ?\\                                   ; \
+ (if (not teco:exp-flag1)
+     ;; no argument; read number
+     (let ((p (point))
+           (sign +1)
+           (n 0)
+           c)
+       (setq c (char-after p))
+       (if c
+           (if (= c ?+)
+               (setq p (1+ p))
+             (if (= c ?-)
+                 (setq p (1+ p)
+                       sign -1))))
+       (cond
+        ((= teco:ctrl-r 8)
+         (while (progn
+                  (setq c (char-after p))
+                  (and c (>= c ?0) (<= c ?7)))
+           (setq p (1+ p)
+                 n (+ c -48 (* n 8)))))
+        ((= teco:ctrl-r 10)
+         (while (progn
+                  (setq c (char-after p))
+                  (and c (>= c ?0) (<= c ?9)))
+           (setq p (1+ p)
+                 n (+ c -48 (* n 10)))))
+        (t
+         (while (progn
+                  (setq c (char-after p))
+                  (and c
+                       (or
+                        (and (>= c ?0) (<= c ?9))
+                        (and (>= c ?a) (<= c ?f))
+                        (and (>= c ?A) (<= c ?F)))))
+           (setq p (1+ p)
+                 n (+ c (if (> c ?F)
+                            ;; convert 'a' to 10
+                            -87
+                          (if (> c ?9)
+                              ;; convert 'A' to 10
+                              -55
+                            ;; convert '0' to 0
+                            -48))
+                      (* n 16))))))
+       (setq teco:exp-val1 (* n sign)
+             teco:exp-flag1 t
+             teco:ctrl-s (- (point) p))
+       (goto-char p))
+   ;; argument: insert it as a digit string
+   (insert (format (cond
+                    ((= teco:ctrl-r 8) "%o")
+                    ((= teco:ctrl-r 10) "%d")
+                    (t "%x"))
+                   teco:exp-val1))
+   (setq teco:exp-flag1 nil
+         teco:exp-op 'start)))
+
+(teco:define-type-2
+ ?\C-t                                 ; ^t
+ (if teco:exp-flag1
+     ;; type a character
+     (progn
+       (teco:output teco:exp-val1)
+       (setq teco:exp-flag1 nil))
+   ;; input a character
+   (if (or (= (logand teco:et-flag 32) 0)
+           (input-pending-p))
+       ;; input is pending, or we must wait
+       (let* ((echo-keystrokes 0)
+              (c (read-char)))
+         (if (= (logand teco:et-flag 8) 0)
+             (teco:output c))
+         (setq teco:exp-val1 c
+               teco:exp-flag1 t))
+     ;; no input is pending, and ET bit 32 is set
+     (setq teco:exp-val1 -1
+           teco:exp-flag1 t))))
+
+(teco:define-type-2
+ ?w                                    ; w
+ (if teco:exp-flag1
+     (progn
+       (recenter teco:exp-val1)
+       (setq teco:exp-flag1 nil))
+   (redraw-display)))
+
+(teco:define-type-2
+ ?s                                    ; s
+ (let ((arg (teco:get-text-arg))
+       (count (if teco:exp-flag1 teco:exp-val1 1))
+       regexp)
+   (if (string-equal arg "")
+       ;; Retrieve last search string
+       (setq regexp teco:last-search-regexp
+             arg (aref teco:qreg-text ?_))
+     ;; Store this search string
+     (setq regexp (teco:parse-search-string arg)
+           teco:last-search-regexp regexp)
+     (aset teco:qreg-text ?_ arg))
+   (let ((result (cond
+                  ((eq teco:colon-flag 2)
+                   (looking-at regexp))
+                  ((> count 0)
+                   (re-search-forward regexp nil t count))
+                  ((< count 0)
+                   (re-search-backward regexp nil t (- count)))
+                  (t
+                   ;; 0s is always successful
+                   t))))
+     (if (and result
+              (< count 0)
+              (= (logand teco:es-flag 1) 1))
+         (goto-char (match-end 0)))
+     ;; set ctrl-s, if the match was successful
+     (if (and result
+              (or (/= count 0) (eq teco:colon-flag 2)))
+         (setq teco:ctrl-s (- (match-beginning 0) (match-end 0))))
+     ;; save result for later ';'
+     (setq teco:search-result (if result -1 0))
+     ;; if no real or implied colon, error if not found
+     (if (and (not result)
+              (not teco:colon-flag)
+              (not (teco:peek-command 59)))
+         (teco:error "SRH"))
+     ;; set return results
+     (if teco:colon-flag
+         (setq teco:exp-flag1 t
+               teco:exp-val1 teco:search-result)
+       (setq teco:exp-flag1 nil))
+     ;; clear other flags
+     (setq teco:exp-flag2 nil
+           teco:colon-flag nil
+           teco:at-flag nil
+           teco:exp-op 'start))))
+
+(defun teco:parse-search-string (s)
+  (let ((i 0)
+        (l (length s))
+        (r "")
+        c)
+    (while (< i l)
+      (setq r (concat r (teco:parse-search-string-1))))
+    r))
+
+(defun teco:parse-search-string-1 ()
+  (if (>= i l)
+      (teco:error "ISS"))
+  (setq c (aref s i))
+  (setq i (1+ i))
+  (cond
+   ((eq c ?\C-e)                       ; ^E - special match characters
+    (teco:parse-search-string-e))
+   ((eq c ?\C-n)                       ; ^Nx - match all but x
+    (teco:parse-search-string-n))
+   ((eq c ?\C-q)                       ; ^Qx - use x literally
+    (teco:parse-search-string-q))
+   ((eq c ?\C-s)                       ; ^S - match separator chars
+    "[^A-Za-z0-9]")
+   ((eq c ?\C-x)                       ; ^X - match any character
+    "[\000-\377]")
+   (t                                  ; ordinary character
+    (teco:parse-search-string-char c))))
+
+(defun teco:parse-search-string-char (c)
+  (regexp-quote (char-to-string c)))
+
+(defun teco:parse-search-string-q ()
+  (if (>= i l)
+      (teco:error "ISS"))
+  (setq c (aref s i))
+  (setq i (1+ i))
+  (teco:parse-search-string-char c))
+
+(defun teco:parse-search-string-e ()
+  (if (>= i l)
+      (teco:error "ISS"))
+  (setq c (aref s i))
+  (setq i (1+ i))
+  (cond
+   ((or (eq c ?a) (eq c ?A))           ; ^EA - match alphabetics
+    "[A-Za-z]")
+   ((or (eq c ?c) (eq c ?C))           ; ^EC - match symbol constituents
+    "[A-Za-z.$]")
+   ((or (eq c ?d) (eq c ?D))           ; ^ED - match numerics
+    "[0-9]")
+   ((eq c ?g)                          ; ^EGq - match any char in q-reg
+    (teco:parse-search-string-e-g))
+   ((or (eq c ?l) (eq c ?L))           ; ^EL - match line terminators
+    "[\012\013\014]")
+   ((eq c ?q)                          ; ^EQq - use contents of q-reg
+    (teco:parse-search-string-e-q))
+   ((eq c ?r)                          ; ^ER - match alphanumerics
+    "[A-Za-z0-9]")
+   ((eq c ?s)                          ; ^ES - match non-null space/tab seq
+    "[ \t]+")
+   ((eq c ?v)                          ; ^EV - match lower case alphabetic
+    "[a-z]")
+   ((eq c ?w)                          ; ^EW - match upper case alphabetic
+    "[A-Z]")
+   ((eq c ?x)                          ; ^EX - match any character
+    "[\000-\377]")
+   (t
+    (teco:error "ISS"))))
+
+(defun teco:parse-search-string-e-q ()
+  (if (>= i l)
+      (teco:error "ISS"))
+  (setq c (aref s i))
+  (setq i (1+ i))
+  (regexp-quote (aref teco:q-reg-text c)))
+
+(defun teco:parse-search-string-e-g ()
+  (if (>= i l)
+      (teco:error "ISS"))
+  (setq c (aref s i))
+  (setq i (1+ i))
+  (let* ((q (aref teco:qreg-text c))
+         (len (length q))
+         (null (= len 0))
+         (one-char (= len 1))
+         (dash-present (string-match "-" q))
+         (caret-present (string-match "\\^" q))
+         (outbracket-present (string-match "]" q))
+         p)
+    (cond
+     (null
+      "[^\000-\377]")
+     (one-char
+      (teco:parse-search-string-char c))
+     (t
+      (while (setq p (string-match "^]\\^" q)) ;FIXME
+        (setq q (concat (substring q 1 p) (substring q (1+ p)))))
+      (concat
+       "["
+       (if outbracket-present "]" "")
+       (if dash-present "---" "")
+       q
+       (if caret-present "^" ""))))))
+
+(defun teco:parse-search-string-n ()
+  (let ((p (teco:parse-search-string-1)))
+    (cond
+     ((= (aref p 0) ?\[)
+      (if (= (aref p 1) ?^)
+          ;; complement character set
+          (if (= (length p) 4)
+              ;; complement of one character
+              (teco:parse-search-string-char (aref p 2))
+            ;; complement of more than one character
+            (concat "[" (substring p 2)))
+        ;; character set - invert it
+        (concat "[^" (substring p 1))))
+     ((= (aref p 0) ?\\)
+      ;; single quoted character
+      (concat "[^" (substring p 1) "]"))
+     (t
+      ;; single character
+      (if (string-equal p "-")
+          "[^---]"
+        (concat "[^" p "]"))))))
+
+(defun teco:substitute-text-string (s)
+  (let ((i 0)
+        (l (length s))
+        (r "")
+        c)
+    (while (< i l)
+      (setq r (concat r (teco:substitute-text-string-1))))
+    r))
+
+(defun teco:substitute-text-string-1 ()
+  (if (>= i l)
+      (teco:error "ISS"))
+  (setq c (aref s i))
+  (setq i (1+ i))
+  (cond
+   ((eq c ?\C-e)                       ; ^E - special string characters
+    (teco:substitute-text-string-e))
+   ((eq c ?\C-q)                       ; ^Qx - use x literally
+    (teco:substitute-text-string-q))
+   (t                                  ; ordinary character
+    (char-to-string c))))
+
+(defun teco:substitute-text-string-q ()
+  (if (>= i l)
+      (teco:error "ISS"))
+  (setq c (aref s i))
+  (setq i (1+ i))
+  (char-to-string c))
+
+(defun teco:substitute-text-string-e ()
+  (if (>= i l)
+      (teco:error "ISS"))
+  (setq c (aref s i))
+  (setq i (1+ i))
+  (cond
+   ((eq c ?q)                          ; ^EQq - use contents of q-reg
+    (teco:substitute-text-string-e-q))
+   (t
+    (teco:error "ISS"))))
+
+(defun teco:substitute-text-string-e-q ()
+  (if (>= i l)
+      (teco:error "ISS"))
+  (setq c (aref s i))
+  (setq i (1+ i))
+  (aref teco:q-reg-text c))
+
+(teco:define-type-2
+ ?o                                    ; o
+ (let ((label (teco:get-text-arg))
+       (index (and teco:exp-flag1 teco:exp-val1)))
+   (setq teco:exp-flag1 nil)
+   ;; handle computed goto by extracting the proper label
+   (if index
+       (if (< index 0)
+           ;; argument < 0 is a noop
+           (setq label "")
+         ;; otherwise, find the n-th label (0-origin)
+         (setq label (concat label ","))
+         (let ((p 0)
+               q)
+           (while (and (> index 0)
+                       (setq p (string-match "," label p))
+                       (setq p (1+ p)))
+             (setq index (1- index)))
+           (setq q (string-match "," label p))
+           (setq label (substring label p q)))))
+   ;; if the label is non-null, find the correct label
+   ;; start from beginning of iteration or macro, and look for tag
+   (setq teco:command-pointer
+         (if teco:iteration-stack
+             ;; if in iteration, start at beginning of iteration
+             (aref (car teco:iteration-stack) 0)
+           ;; if not in iteration, start at beginning of command or macro
+           0))
+   ;; search for tag
+   (catch 'label
+     (let ((level 0)
+           c p l)
+       ;; look for interesting things, including !
+       (while t
+         (setq c (teco:skipto t))
+         (cond
+          ((= c ?<)                    ; start of iteration
+           (setq level (1+ level)))
+          ((= c ?>)                    ; end of iteration
+           (if (= level 0)
+               (teco:pop-iter-stack t)
+             (setq level (1- level))))
+          ((= c ?!)                    ; start of tag
+           (setq p (string-match "!" teco:command-string teco:command-pointer))
+           (if (and p
+                    (string-equal label (substring teco:command-string
+                                                   teco:command-pointer
+                                                   p)))
+               (progn
+                 (setq teco:command-pointer (1+ p))
+                 (throw 'label nil))))))))))
+
+(teco:define-type-2
+ ?a                                    ; :a
+ ;; 'a' must be used as ':a'
+ (if (and teco:exp-flag1 teco:colon-flag)
+     (let ((char (+ (point) teco:exp-val1)))
+       (setq teco:exp-val1
+             (if (and (>= char (point-min))
+                      (< char (point-max)))
+                 (char-after char)
+               -1)
+             teco:colon-flag nil))
+   (teco:error "ILL")))
+
+(teco:define-type-2
+ ?e                                    ; e
+ (let ((c (teco:get-command teco:trace)))
+   (cond
+    ((= c ?t) (teco:set-var 'teco:et-flag))
+    ((= c ?s) (teco:set-var 'teco:es-flag))
+    (t (teco:error "IEC")))))
+
+(teco:define-type-2
+ ?f                                    ; f
+ (let ((c (teco:get-command teco:trace)))
+   (cond
+    ((= c ?e) (teco:fe-command))
+    ((= c ?l) (teco:fl-command))
+    ((= c ?r) (teco:fr-command))
+    ((= c ?s) (teco:fs-command))
+    ((= c ?w) (teco:fw-command))
+    (t (teco:error "IFC")))))
+
+(defun teco:fe-command ()
+  "Aka fe."
+  (let ((text (teco:substitute-text-string (teco:get-text-arg))))
+    (prin1-to-string (eval (read text)))))
+
+(defun teco:fl-command ()
+  "Aka fl."
+  (let ((count (teco:get-value 1))
+        (start (point)))
+    ;; argument 0 is a no-op
+    (if (/= count 0)
+        (forward-sexp count))
+    ;; get the result values into the arguments
+    (setq teco:exp-val2 start
+          teco:exp-flag2 t
+          teco:exp-val1 (point)
+          teco:exp-flag1 t
+          teco:colon-flag nil)
+    ;; don't move the point
+    (goto-char start)))
+
+(defun teco:fr-command ()
+  "Aka fr."
+  (let ((text (teco:get-text-arg)))
+    ;; delete the previous match
+    (delete-char teco:ctrl-s)
+    ;; insert the argument
+    (insert text)
+    ;; set ^S for the insertion
+    (setq teco:ctrl-s (- (length text)))))
+
+(defun teco:fs-command ()
+  "Aka fs."
+  (let* ((args (teco:get-two-text-args))
+         (search (car args))
+         (replace (car (cdr args)))
+         regexp)
+    (if (string-equal search "")
+        ;; Retrieve last search string
+        (setq regexp teco:last-search-regexp
+              search (aref teco:qreg-text ?_))
+      ;; Store this search string
+      (setq regexp (teco:parse-search-string search)
+            teco:last-search-regexp regexp)
+      (aset teco:qreg-text ?_ search))
+    (let ((result (re-search-forward regexp nil t)))
+      ;; save result for later ';'
+      (setq teco:search-result (if result -1 0))
+      ;; if no real or implied colon, error if not found
+      (if (and (not result)
+               (not teco:colon-flag)
+               (not (teco:peek-command 59)))
+          (teco:error "SRH"))
+      ;; set return results
+      (if teco:colon-flag
+          (setq teco:exp-flag1 t
+                teco:exp-val1 teco:search-result)
+        (setq teco:exp-flag1 nil))
+      ;; clear other flags
+      (setq teco:exp-flag2 nil
+            teco:colon-flag nil
+            teco:at-flag nil
+            teco:exp-op 'start)
+      (if result
+          (progn
+            ;; delete the match
+            (delete-char (- (match-end 0) (match-beginning 0)))
+            ;; insert the argument
+            (insert replace)
+            ;; set ^S for the insertion
+            (setq teco:ctrl-s (- (length replace))))))))
+
+(defun teco:fw-command ()
+  "Aka fw."
+  (let ((count (teco:get-value 1))
+        (start (point)))
+    ;; argument 0 is a no-op
+    (if (/= count 0)
+        (progn
+          (forward-word count)
+          ;; If : is present, back off to the near side of the last word
+          ;; found.  Make sure we don't run past the starting position.
+          (if teco:colon-flag
+              (if (> count 0)
+                  ;; Searching forward
+                  (progn
+                    (forward-word -1)
+                    (if (< (point) start)
+                        (goto-char start)))
+                ;; Searching backward
+                (progn
+                  (forward-word 1)
+                  (if (> (point) start)
+                      (goto-char start)))))))
+    ;; get the result values into the arguments
+    (setq teco:exp-val2 start
+          teco:exp-flag2 t
+          teco:exp-val1 (point)
+          teco:exp-flag1 t
+          teco:colon-flag nil)
+    ;; don't move the point
+    (goto-char start)))
+
+;; Routines to get next character from command buffer
+;; getcmdc0, when reading beyond command string, pops
+;; macro stack and continues.
+;; getcmdc, in similar circumstances, reports an error.
+;; If pushcmdc() has returned any chars, read them first
+;; routines type characters as read, if argument != 0.
+
+(defun teco:get-command0 (trace)
+  "Get the next character TRACE."
+  (let (char)
+    (while (not (condition-case nil
+                    (setq char (aref teco:command-string teco:command-pointer))
+                  ;; if we've exhausted the string, pop the macro stack
+                  ;; if we exhaust the macro stack, exit
+                  (error (teco:pop-macro-stack)
+                         nil))))
+    ;; bump the command pointer
+    (setq teco:command-pointer (1+ teco:command-pointer))
+    ;; trace, if requested
+    (and trace (teco:trace-type char))
+    ;; return the character
+    char))
+
+(defun teco:get-command (trace)
+  "Get the next character TRACE."
+  (let ((char (condition-case nil
+                  (aref teco:command-string teco:command-pointer)
+                ;; if we've exhausted the string, give error
+                (error
+                 (teco:error (if teco:macro-stack "UTM" "UTC"))))))
+    ;; bump the command pointer
+    (setq teco:command-pointer (1+ teco:command-pointer))
+    ;; trace, if requested
+    (and trace (teco:trace-type char))
+    ;; return the character
+    char))
+
+;; peek at next char in command string, return 1 if it is equal
+;; (case independent) to argument
+
+(defun teco:peek-command (arg)
+  (condition-case nil
+      (eq (aref teco:mapch-l (aref teco:command-string teco:command-pointer))
+          (aref teco:mapch-l arg))
+    (error nil)))
+
+(defun teco:get-text-arg (&optional term-char default-term-char)
+  ;; figure out what the terminating character is
+  (setq teco:term-char (or term-char
+                           (if teco:at-flag
+                               (teco:get-command teco:trace)
+                             (or default-term-char
+                                 ?\e)))
+        teco:at-flag nil)
+  (let ((s "")
+        c)
+    (while (progn
+             (setq c (teco:get-command teco:trace))
+             (/= c teco:term-char))
+      (setq s (concat s (char-to-string c))))
+    s))
+
+(defun teco:get-two-text-args (&optional term-char default-term-char)
+  ;; figure out what the terminating character is
+  (setq teco:term-char (or term-char
+                           (if teco:at-flag
+                               (teco:get-command teco:trace)
+                             (or default-term-char
+                                 ?\e)))
+        teco:at-flag nil)
+  (let ((s1 "")
+        (s2 "")
+        c)
+    (while (progn
+             (setq c (teco:get-command teco:trace))
+             (/= c teco:term-char))
+      (setq s1 (concat s1 (char-to-string c))))
+    (while (progn
+             (setq c (teco:get-command teco:trace))
+             (/= c teco:term-char))
+      (setq s2 (concat s2 (char-to-string c))))
+    (list s1 s2)))
+
+;; Routines to manipulate the stacks
+
+;; Pop the macro stack.  Throw to 'teco:exit' if the stack is empty.
+(defun teco:pop-macro-stack ()
+  (if teco:macro-stack
+      (let ((frame (car teco:macro-stack)))
+        (setq teco:macro-stack (cdr teco:macro-stack)
+              teco:command-string (aref frame 0)
+              teco:command-pointer (aref frame 1)
+              teco:iteration-stack (aref frame 2)
+              teco:cond-stack (aref frame 3)
+              teco:at-flag nil))
+    (throw 'teco:exit nil)))
+
+;; Push the macro stack.
+(defun teco:push-macro-stack ()
+  (setq teco:macro-stack
+        (cons (vector teco:command-string
+                      teco:command-pointer
+                      teco:iteration-stack
+                      teco:cond-stack)
+              teco:macro-stack)))
+
+;; Pop the expression stack.
+(defun teco:pop-exp-stack ()
+  (let ((frame (car teco:exp-stack)))
+    (setq teco:exp-stack (cdr teco:exp-stack)
+          teco:exp-val1 (aref frame 0)
+          teco:exp-flag1 (aref frame 1)
+          teco:exp-val2 (aref frame 2)
+          teco:exp-flag2 (aref frame 3)
+          teco:exp-exp (aref frame 4)
+          teco:exp-op (aref frame 5))))
+
+;; Push the expression stack.
+(defun teco:push-exp-stack ()
+  (setq teco:exp-stack
+        (cons (vector teco:exp-val1
+                      teco:exp-flag1
+                      teco:exp-val2
+                      teco:exp-flag2
+                      teco:exp-exp
+                      teco:exp-op)
+              teco:exp-stack)))
+
+;; Pop the iteration stack
+;; if arg t, exit unconditionally
+;; else check exit conditions and exit or reiterate
+(defun teco:pop-iter-stack (arg)
+  (let ((frame (car teco:iteration-stack)))
+    (if (or arg
+            (and ;; without argument, iterate indefinitely
+             (aref frame 1)
+             ;; test against 1, since one iteration has already been done
+             (<= (aref frame 2) 1)))
+        ;; exit iteration
+        (setq teco:iteration-stack (cdr teco:iteration-stack))
+      ;; continue with iteration
+      ;; decrement count
+      (and (aref frame 1)
+           (aset frame 2 (1- (aref frame 2))))
+      ;; reset command pointer
+      (setq teco:command-pointer (aref frame 0)))))
+
+;; Push the iteration stack
+(defun teco:push-iter-stack (pointer flag count)
+  (setq teco:iteration-stack
+        (cons (vector pointer
+                      flag
+                      count)
+              teco:iteration-stack)))
+
+(defun teco:find-enditer ()
+  (let ((icnt 1)
+        c)
+    (while (> icnt 0)
+      (while (progn (setq c (teco:skipto))
+                    (and (/= c ?<)
+                         (/= c ?>))))
+      (if (= c ?<)
+          (setq icnt (1+ icnt))
+        (setq icnt (1- icnt))))))
+
+
+;; I/O routines
+
+(defvar teco:output-buffer (get-buffer-create "*Teco Output*")
+  "The buffer into which Teco output is written.")
+
+(defun teco:out-init ()
+  "Recreate the teco output buffer, if necessary."
+  (setq teco:output-buffer (get-buffer-create "*Teco Output*"))
+  (with-current-buffer teco:output-buffer
+    ;; get a fresh line in output buffer
+    (goto-char (point-max))
+    (insert ?\n)
+    ;; remember where to start displaying
+    (setq teco:output-start (point))
+    ;; clear minibuffer, in case we have to display in it
+    (save-window-excursion
+      (select-window (minibuffer-window))
+      (erase-buffer))
+    ;; if output is visible, position it correctly
+    (let ((w (get-buffer-window teco:output-buffer)))
+      (if w
+          (progn
+            (set-window-start w teco:output-start)
+            (set-window-point w teco:output-start))))))
+
+(defun teco:output (s)
+  ;; Do no work if output is "".  Also, this avoids an error condition.
+  (if (not (and (stringp s) (string-equal s "")))
+      (let ((w (get-buffer-window teco:output-buffer))
+            (b (current-buffer))
+            (sw (selected-window)))
+        ;; Put the text in the output buffer
+        (set-buffer teco:output-buffer)
+        (goto-char (point-max))
+        (insert s)
+        (let ((p (point)))
+          (set-buffer b)
+          (if w
+              ;; if output is visible, move the window point to the end
+              (set-window-point w p)
+            ;; Otherwise, we have to figure out how to display the text
+            ;; Has a newline followed by another character been added to the
+            ;; output buffer?  If so, we have to make the output buffer
+            ;; visible.
+            (if (with-current-buffer teco:output-buffer
+                  (backward-char 1)
+                  (search-backward "\n" teco:output-start t))
+                ;; a newline has been seen, clear the minibuffer and make the
+                ;; output buffer visible
+                (progn
+                  (save-window-excursion
+                    (select-window (minibuffer-window))
+                    (erase-buffer))
+                  (let ((pop-up-windows t))
+                    (pop-to-buffer teco:output-buffer)
+                    (goto-char p)
+                    (set-window-start w teco:output-start)
+                    (set-window-point w p)
+                    (select-window sw)))
+              ;; a newline has not been seen, add output to minibuffer
+              (save-window-excursion
+                (select-window (minibuffer-window))
+                (goto-char (point-max))
+                (insert s))))))))
+
+;; Output a character of tracing information
+(defun teco:trace-type (c)
+  (teco:output (if (= c ?\e)
+                   ?$
+                 c)))
+
+;; Report an error
+(defun teco:error (code)
+  ;; save the command with the error
+  (aset teco:qreg-text ?%
+        (substring teco:command-string 0 teco:command-pointer))
+  (let ((text (cdr (assoc code teco:error-texts))))
+    (teco:output (concat (if (with-current-buffer teco:output-buffer
+                               (/= (point) teco:output-start))
+                             "\n"
+                           "")
+                         ;; due to the test in teco:output and Emacs' handling
+                         ;; of trailing newlines in the minibuffer-window,
+                         ;; we can have a newline at the end of the error
+                         ;; message and it will not force the output display
+                         ;; from the minibuffer into the Teco output buffer
+                         "? " code " " text "\n"))
+    (beep)
+    (if debug-on-error (debug nil code text))
+    (throw 'teco:exit nil)))
+
+
+;; Utility routines
+
+;; Convert character to q-register name
+;; If file-or-search is t, allow special q-reg names
+(defun teco:get-qspec (file-or-search char)
+  ;; lower-case char
+  (setq char (aref teco:mapch-l char))
+  ;; test that it's valid
+  (if (= (logand (aref teco:qspec-valid char) (if file-or-search 2 1)) 0)
+      (teco:error "IQN"))
+  char)
+
+;; Set or get value of a variable
+(defun teco:set-var (variable)
+  ;; If there is an argument, then set the variable
+  (if teco:exp-flag1
+      (progn
+        (set variable
+             (if teco:exp-flag2
+                 ;; If there are two arguments, then they are bits to clear
+                 ;; and bits to set
+                 (logior (logand (lognot (symbol-value variable))
+                                 teco:exp-val2)
+                         teco:exp-val1)
+               ;; One argument is the new value alone
+               teco:exp-val1))
+        (setq teco:exp-flag1 nil
+              teco:exp-flag2 nil))
+    ;; No arguments mean to fetch the variable's value
+    (setq teco:exp-val1 (symbol-value variable)
+          teco:exp-flag1 t)))
+
+;; Get numeric argument
+(defun teco:get-value (default)
+  (prog1
+      (if teco:exp-flag1
+          teco:exp-val1
+        (if (eq teco:exp-op 'sub)
+            (- default)
+          default))
+    ;; consume argument
+    (setq teco:exp-flag1 nil
+          teco:exp-op 'start)))
+
+;; Get argument measuring in lines
+(defun teco:lines (r)
+  (- (save-excursion
+       (if (> r 0)
+           (if (search-forward "\n" nil t r)
+               (point)
+             (point-max))
+         (if (search-backward "\n" nil t (- 1 r))
+             (1+ (point))
+           (point-min))))
+     (point)))
+
+;; routine to handle args for K, T, X, etc.
+;; if two args, 'char x' to 'char y'
+;; if just one arg, then n lines (default 1)
+(defun teco:line-args ()
+  (prog1
+      (if teco:exp-flag2
+          (cons teco:exp-val1 teco:exp-val2)
+        (cons (point) (+ (point) (teco:lines (if teco:exp-flag1
+                                                 teco:exp-val1
+                                               1)))))
+    (setq teco:exp-flag1 nil
+          teco:exp-flag2 nil)))
+
+;; routine to skip to next ", ', |, <, or >
+;; skips over these chars embedded in text strings
+;; stops in ! if argument is t
+;; returns character found
+(defun teco:skipto (&optional arg)
+  (catch 'teco:skip
+    (let (;; "at" prefix
+          (atsw nil)
+          ;; temp attributes
+          ta
+          ;; terminator
+          term
+          skipc)
+      (while t                         ; forever
+        (while (progn
+                 (setq skipc (teco:get-command nil)
+                       ta (aref teco:spec-chars skipc))
+                 (cond
+                  ;; if char is ^, treat next char as control
+                  ((eq skipc ?^)
+                   (setq skipc (logand 31 (teco:get-command nil))
+                         ta (aref teco:spec-chars skipc)))
+                  ;; if char is E or F, pick up next char and interpret the
+                  ;; two-character sequence
+                  ((eq skipc ?e)
+                   (setq skipc (teco:get-command nil)
+                         ta (logand -259 (aref teco:spec-chars skipc)))
+                   (if (/= (logand ta 4) 0)
+                       (setq ta (logior ta 2))))
+                  ((eq skipc ?f)
+                   (setq skipc (teco:get-command nil)
+                         ta (logand -259 (aref teco:spec-chars skipc)))
+                   (if (/= (logand ta 8) 0)
+                       (setq ta (logior ta 2)))
+                   (if (/= (logand ta 512) 0)
+                       (setq ta (logior ta 256)))))
+                 (= (logand ta 307) 0))        ; read until something 
interesting
+                                        ; found
+          nil)
+        (if (/= (logand ta 32) 0)
+            (teco:get-command nil))    ; if command takes a Q spec,
+                                        ; skip the spec
+        (if (/= (logand ta 16) 0)      ; sought char found: quit
+            (progn
+              (if (= skipc ?\")                ; quote must skip next char
+                  (teco:get-command nil))
+              (throw 'teco:skip skipc)))
+        (if (/= (logand ta 1) 0)       ; other special char
+            (cond
+             ((eq skipc ?@)            ; use alternative text terminator
+              (setq atsw t))
+             ((eq skipc ?\C-^)         ; ^^ is value of next char
+                                        ; skip that char
+              (teco:get-command nil))
+             ((eq skipc ?\C-a)         ; type text
+              (setq term (if atsw (teco:get-command nil) ?\C-a)
+                    atsw nil)
+              (while (/= (teco:get-command nil) term)
+                nil))                  ; skip text
+             ((eq skipc ?!)            ; tag
+              (if arg
+                  (throw 'teco:skip skipc))
+              (while (/= (teco:get-command nil) ?!)
+                nil))                  ; skip until next !
+             ((or (eq skipc ?e)
+                  (eq skipc ?f))       ; first char of two-letter E or F
+                                        ; command
+              nil)))                   ; not implemented
+        (if (/= (logand ta 2) 0)       ; command with a text
+                                        ; argument
+            (progn
+              (setq term (if atsw (teco:get-command nil) ?\e)
+                    atsw nil)
+              (while (/= (teco:get-command nil) term)
+                nil)                   ; skip text
+              ))
+        (if (/= (logand ta 256) 0)     ; command with a double text
+                                        ; argument
+            (progn
+              (setq term (if atsw (teco:get-command nil) ?\e)
+                    atsw nil)
+              (while (/= (teco:get-command nil) term)
+                nil)                   ; skip text
+              (while (/= (teco:get-command nil) term)
+                nil)                   ; skip second text
+              (if (/= (logand ta 1024) 0)
+                  (setq atsw nil))             ; transfer command clears @-flag
+                                        ; after executing
+              ))))))
+
+
+;; Input handling
+
+(defvar teco:command-keymap
+  (list 'keymap (make-vector 128 'teco:command-self-insert))
+  "Keymap used while reading teco commands.")
+
+(define-key teco:command-keymap "\^c" 'teco:command-quit)
+(define-key teco:command-keymap "\^g" 'teco:command-quit)
+(define-key teco:command-keymap "\^l" 'teco:command-ctrl-l)
+(define-key teco:command-keymap "\^m" 'teco:command-return)
+(define-key teco:command-keymap "\^u" 'teco:command-ctrl-u)
+(define-key teco:command-keymap "\e" 'teco:command-escape)
+(define-key teco:command-keymap "\^?" 'teco:command-delete)
+(define-key teco:command-keymap "?" 'teco:command-query)
+(define-key teco:command-keymap "/" 'teco:command-slash)
+(define-key teco:command-keymap "*" 'teco:command-star)
+
+(defvar teco:command-display-table
+  (let ((table (make-display-table)))
+    (aset table ?\e [?$])
+    table)
+  "Display table used while reading teco commands.")
+
+(defun teco:copy-to-q-reg (char start end)
+  "Copy region into Teco q-reg REG.
+When called from program, takes three args: REG, START, and END.
+START and END are buffer positions indicating what to copy."
+  (interactive "cCopy region to q-reg: \nr")
+  (setq char (aref teco:mapch-l char))
+  ;; test that it's valid
+  (if (= (logand (aref teco:qspec-valid char) 1) 0)
+      (error "? IQN Invalid Q-reg name"))
+  (aset teco:qreg-text char (buffer-substring start end)))
+
+(defun teco:command ()
+  "Read and execute a Teco command string."
+  (interactive)
+  (let ((command (teco:read-command)))
+    (if command
+        (progn
+          (setq teco:output-buffer (get-buffer-create "*Teco Output*"))
+          (with-current-buffer teco:output-buffer
+            (goto-char (point-max))
+            (insert teco:prompt command))
+          (teco:execute-command command)))))
+(defalias 'teco 'teco:command)
+
+(defun teco:read-command ()
+  "Read a teco command string from the user."
+  (minibuffer-with-setup-hook
+      (lambda ()
+        (setq buffer-display-table teco:command-display-table))
+    (catch 'teco:command-quit
+      (read-from-minibuffer teco:prompt nil teco:command-keymap))))
+
+(defun teco:command-self-insert ()
+  (interactive)
+  (teco:command-insert-character last-command-event))
+
+(defun teco:command-quit ()
+  (interactive)
+  (beep)
+  (throw 'teco:command-quit nil))
+
+(defun teco:command-ctrl-l ()
+  (interactive)
+  (redraw-display))
+
+(defun teco:command-return ()
+  (interactive)
+  (setq last-command-event ?\n)
+  (teco:command-self-insert))
+
+(defun teco:command-escape ()
+  (interactive)
+  ;; Two ESCs in a row terminate the command string
+  (if (eq last-command 'teco:command-escape)
+      (throw 'teco:command-quit (minibuffer-contents-no-properties)))
+  (teco:command-insert-character 27))
+
+(defun teco:command-ctrl-u ()
+  (interactive)
+  ;; delete the characters
+  (kill-line 0)
+  ;; decide whether to shrink the window
+  (while (let ((a (insert ?\n))
+               (b (pos-visible-in-window-p))
+               (c (backward-delete-char 1)))
+           b)
+    (shrink-window 1)))
+
+(defun teco:command-delete ()
+  (interactive)
+  ;; delete the character
+  (backward-delete-char 1)
+  ;; decide whether to shrink the window
+  (insert ?\n)
+  (if (prog1 (pos-visible-in-window-p)
+        (backward-delete-char 1))
+      (shrink-window 1)))
+
+(defun teco:command-query ()
+  (interactive)
+  ;; first input char sees last-command equal to 't
+  (if (eq last-command t)
+      ;; if first character of command, insert erroneous command
+      (let* ((s (aref teco:qreg-text ?%))
+             (l (length s))
+             (i 0))
+        (while (< i l)
+          (teco:command-insert-character (aref s i))
+          (setq i (1+ i))))
+    ;; otherwise, just insert the character
+    (teco:command-self-insert)))
+
+(defun teco:command-slash ()
+  (interactive)
+  ;; first input char sees last-command equal to 't
+  (if (eq last-command t)
+      ;; if first character of command, insert last command
+      (let* ((s (aref teco:qreg-text ?#))
+             (l (length s))
+             (i 0))
+        (while (< i l)
+          (teco:command-insert-character (aref s i))
+          (setq i (1+ i))))
+    ;; otherwise, just insert the character
+    (teco:command-self-insert)))
+
+(defun teco:command-star ()
+  (interactive)
+  ;; first input char sees last-command equal to 't
+  (if (eq last-command t)
+      ;; if first character of command, offer to save previous command in
+      ;; q-register
+      (progn
+        ;; insert the * into the buffer
+        (teco:command-insert-character last-command-event)
+        ;; read the next character
+        (let ((c (read-char))
+              c1)
+          ;; test if it is a valid q-reg name
+          (setq c1 (aref teco:mapch-l c))
+          (if (/= (logand (aref teco:qspec-valid c1) 1) 0)
+              ;; if so, store the command, give a message, and abort command
+              (progn
+                (aset teco:qreg-text c1 (aref teco:qreg-text ?#))
+                (message "Last Teco command stored in q-register %c" c1)
+                (throw 'teco:command-quit nil))
+            ;; if q-reg name is invalid, just insert the character
+            (beep)
+            (teco:command-insert-character c))))
+    ;; otherwise, just insert the character
+    (teco:command-self-insert)))
+
+;; Insert a single command character
+(defun teco:command-insert-character (c)
+  (insert c)
+  (if (not (pos-visible-in-window-p))
+      (enlarge-window 1)))
+
+(provide 'teco)
+;;; teco.el ends here

Reply via email to