diff lisp/emulators/teco.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children b82b59fe008d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/emulators/teco.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,1921 @@
+;;; teco.el --- Teco interpreter for Gnu Emacs, version 1.
+
+(require 'backquote)
+;; This code has been tested some, but no doubt contains a zillion bugs.
+;; You have been warned.
+
+;; Written by Dale R. Worley based on a C implementation by Matt Fichtenbaum.
+;; Please send comments, bug fixes, enhancements, etc. to drw@math.mit.edu.
+
+;; Emacs Lisp version copyright (C) 1991 by Dale R. Worley.
+;; Do what you will with it.
+
+;; 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.
+
+;; To invoke directly, do:
+;; (global-set-key ?\C-z 'teco-command)
+;; (autoload teco-command "teco"
+;;   "Read and execute a Teco command string."
+;;   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.
+
+;; Command set:
+;;	NUL	Not a command.
+;;	^A	Output message to terminal (argument ends with ^A)
+;;	^C	Exit macro
+;;	^C^C	Stop execution
+;;	^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^G	(type-in) Kill command string
+;;	^G<sp>	(type-in) Retype current command line
+;;	^G*	(type-in) Retype current command input
+;;	TAB	Insert tab and text
+;;	LF	Line terminator; Ignored in commands
+;;	VT	Ignored in commands
+;;	FF	Ignored in commands
+;;	CR	Ignored in commands
+;;	^Nx	(match char) Match all but x
+;;	^O	Set radix to octal
+;;	^P	Find matching parenthesis
+;;	^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)
+;;	^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
+;;	^\	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
+;;	+	Addition
+;;	,	Argument separator
+;;	-	Subtraction or negation
+;;	.	Current pointer position
+;;	/	Division
+;;	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
+;;	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
+;;	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
+;;	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
+;;	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
+
+
+;; 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-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-exec-flags 0
+  "Flags for iteration in process, ei macro, etc.")
+
+(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-string ""
+  "Last string searched for.")
+
+(defvar teco-last-search-regexp ""
+  "Regexp version of teco-last-search-string.")
+
+(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) '(, 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) '(, 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 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          1          16         0	;    !  \"  # 
+   0          0          0          16	; $  %  &  ' 
+   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  :  ; 
+   16         0          16         0	; <  =  >  ? 
+   1          0          12         0	; @  A  B  C 
+   0          1          1          32	; D  E  F  G 
+   0          6          0          0	; H  I  J  K 
+   0          32         10         2	; L  M  N  O 
+   0          32         4          10	; P  Q  R  S 
+   0          32         0          4	; T  U  V  W 
+   32         0          0          32	; X  Y  Z  \[ 
+   0          32         1          6	; \  \]  ^  _ 
+   0          0          12         0	; `  a  b  c 
+   0          1          1          32	; d  e  f  g 
+   0          6          0          0	; h  i  j  k 
+   0          32         10         2	; l  m  n  o 
+   0          32         4          10	; 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")
+
+
+(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-exec-flags 0)
+	(teco-iteration-stack nil)
+	(teco-cond-stack nil)
+	(teco-exp-stack nil)
+	(teco-macro-stack nil)
+	(teco-qreg-stack nil))
+    ;; 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))))
+		;; 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 argument
+   (setq teco-exp-flag1 nil)
+   (setq teco-exp-op 'start)))
+
+(teco-define-type-1
+ ?!					; !
+ (while (/= (teco-get-command teco-trace) ?!)
+   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-ctrl-s)
+       teco-exp-val2 (point)
+       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
+ ?\^p					; ^p
+ (teco-do-ctrl-p))
+
+(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
+ (teco-set-var 'teco-ctrl-x))
+
+(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-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-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 '
+       (let ((ll 1)
+	     c)
+	 (while (> ll 0)
+	   (while (progn (setq c (teco-skipto))
+			 (and (/= c ?\")
+			      (/= c ?|)
+			      (/= c ?\')))
+	     (if (= c ?\")
+		 (setq ll (1+ ll))
+	       (if (= c ?\')
+		   (setq ll (1- ll))
+		 (if (= ll 1)
+		     (break))))))))))
+
+(teco-define-type-2
+ ?'					; '
+ ;; ignore it if executing
+ t)
+
+(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-flag2 nil))))
+
+(teco-define-type-2
+ ?l					; l
+ ;; move forward by lines
+ (forward-char (teco-lines (teco-get-value 1))))
+
+(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 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 nil)))
+   (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 1)))
+     (delete-region (car ll) (cdr ll)))))
+
+(teco-define-type-2
+ ?k					; k
+ (let ((ll (teco-line-args 1)))
+   (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 0))
+	(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-exp-flag1 nil
+	 teco-at-flag nil
+	 teco-colon-flag nil
+	 teco-exp-flag1 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))
+     (insert (aref teco-qreg-text mm)))
+   (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)))
+   ;; 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
+   (let* ((echo-keystrokes 0)
+	  (c (read-char)))
+     (teco-output c)
+     (setq teco-exp-val1 c
+	   teco-exp-flag1 t))))
+
+(teco-define-type-2
+ ?s					; s
+ (let ((arg (teco-get-text-arg))
+       (count (if teco-exp-flag1 teco-expr-val1 1))
+       regexp)
+   (if (not (string-equal arg ""))
+       (setq regexp (teco-parse-search-string arg)
+	     teco-last-search-string arg
+	     teco-last-search-regexp regexp)
+     (setq regexp (teco-last-search-regexp)
+	   arg teco-last-search-string))
+   (let ((p (point))
+	 (result (cond
+		  ((> count 0)
+		   (re-search-forward regexp nil t count))
+		  ((< count 0)
+		   (re-search-backward regexp nil t count))
+		  (t
+		   ;; 0s always is successful
+		   t))))
+     ;; if ::s, restore point
+     (if (eq teco-colon-flag 2)
+	 (goto-char p))
+     ;; if no real or implied colon, error if not found
+     (if (and (not result)
+	      (not teco-colon-flag)
+	      (/= (teco-peekcmdc) 34))
+	 (teco-error "SRH"))
+     ;; set return results
+     (setq teco-exp-flag2 nil
+	   teco-colon-flag nil
+	   teco-at-flag nil
+	   teco-exp-op 'start)
+     (if teco-colon-flag
+	 (setq teco-exp-flag1 t
+	       teco-exp-val1 (if result -1 0))
+       (setq teco-exp-flag1 nil)))))
+
+(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 reco: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 "^]\\^"))
+	(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 "]"))))))
+
+(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))
+	   (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")))
+
+
+;; 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
+  (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))
+
+;; 	while (cptr.dot >= cptr.z)		/* if at end of this level, pop macro stack
+;; 		{
+;; 		if (--msp < &mstack[0])		/* pop stack; if top level
+;; 			{
+;; 			msp = &mstack[0];		/* restore stack pointer
+;; 			cmdc = ESC;				/* return an ESC (ignored)
+;; 			exitflag = 1;			/* set to terminate execution
+;; 			return(cmdc);			/* exit "while" and return
+;; 			}
+;; 		}
+;; 	cmdc = cptr.p->ch[cptr.c++];		/* get char
+;; 	++cptr.dot;							/* increment character count
+;; 	if (trace) type_char(cmdc);			/* trace
+;; 	if (cptr.c > CELLSIZE-1)			/* and chain if need be
+;; 		{
+;; 		cptr.p = cptr.p->f;
+;; 		cptr.c = 0;
+;; 		}
+;; 	return(cmdc);
+;; 	}
+
+
+(defun teco-get-command (trace)
+  ;; get the next character
+  (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))
+
+;; char getcmdc(trace)
+;; 	{
+;; 	if (cptr.dot++ >= cptr.z) ERROR((msp <= &mstack[0]) ? E_UTC : E_UTM);
+;; 	else
+;; 		{
+;; 		cmdc = cptr.p->ch[cptr.c++];	/* get char
+;; 		if (trace) type_char(cmdc);		/* trace
+;; 		if (cptr.c > CELLSIZE-1)		/* and chain if need be
+;; 			{
+;; 			cptr.p = cptr.p->f;
+;; 			cptr.c = 0;
+;; 			}
+;; 		}
+;; 	return(cmdc);
+;; 	}
+
+
+;; 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)))
+
+;; int peekcmdc(arg)
+;; 	char arg;
+;; 	{
+;; 	return(((cptr.dot < cptr.z) && (mapch_l[cptr.p->ch[cptr.c]] == mapch_l[arg])) ? 1 : 0);
+;; 	}
+
+(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))
+
+
+;; 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-exec-flags (aref frame 2)
+	      teco-iteration-stack (aref frame 3)
+	      teco-cond-stack (aref frame 4)))
+    (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-exec-flags
+		      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
+	    (not (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
+      (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*"))
+  (save-excursion
+    (set-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)
+  (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 (save-excursion
+	      (set-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)
+  (let ((text (cdr (assoc code teco-error-texts))))
+    (teco-output (concat (if (save-excursion (set-buffer teco-output-buffer)
+					     (/= (point) teco-output-start))
+			     "\n"
+			   "")
+			 "? " code " " text))
+    (beep)
+    (if debug-on-error (debug nil code text))
+    (throw 'teco-exit nil)))
+
+
+;; Utility routines
+
+;; copy characters from command string to buffer
+(defun teco-moveuntil (string pointer terminate trace)
+  (let ((count 0))
+    (condition-case nil
+	(while (/= (aref string pointer) terminate)
+	  (and teco-trace (teco-trace-type (aref string pointer)))
+	  (insert (aref string pointer))
+	  (setq pointer (1+ pointer))
+	  (setq count (1+ count)))
+      (error (teco-error (if teco-macro-stack "UTM" "UTC"))))
+    count))
+
+;; Convert character to q-register name
+;; If file-or-search is t, allow _, *, %, #
+(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 (var)
+  (if teco-exp-flag1
+      (progn
+	(if teco-exp-flag2
+	    ;; if two arguments, they they are <clear bits>, <set bits>
+	    (set var (logior (logand (symbol-value var) (lognot teco-exp-val2))
+			     teco-exp-val1))
+	  ;; if one argument, it is the new value
+	  (set var teco-exp-val1))
+	;; consume argument(s)
+	(setq teco-exp-flag2 nil
+	      teco-exp-flag1 nil))
+    ;; if no arguments, fetch the value
+    (setq teco-exp-val1 (symbol-value var)
+	  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 (arg)
+  (if teco-exp-flag2
+      (cons teco-exp-val1 teco-exp-val2)
+    (cons (point) (+ (point) (teco-lines (if teco-exp-flag1
+					     teco-exp-val1
+					   1))))))
+
+;; 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))
+		 ;; if char is ^, treat next char as control
+		 (if (eq skipc ?^)
+		     (setq skipc (logand 31 (teco-get-command nil))
+			   ta (aref teco-spec-chars skipc)))
+		 (= (logand ta 51) 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
+	      ))))))
+
+
+(defvar teco-command-keymap
+  ;; This is what used to be (make-vector 128 'teco-command-self-insert)
+  ;; Oh well
+  (let ((map (make-keymap)) (n 127))
+    (while (>= n 0)
+      (define-key map (if (< n 32) (list 'control (+ n 32)) n)
+	'teco-command-self-insert)
+      (setq n (1- n)))
+    map)
+  "Keymap used while reading teco commands.")
+
+(define-key teco-command-keymap "\^g" 'teco-command-ctrl-g)
+(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)
+
+(defvar teco-command-escapes nil
+  "Records where ESCs are, since they are represented in the command buffer
+by $.")
+
+;;;###autoload
+(defun teco-command ()
+  "Read and execute a Teco command string."
+  (interactive)
+  (let* ((teco-command-escapes nil)
+	 (command (catch 'teco-command-quit
+		    (read-from-minibuffer teco-prompt nil
+					  teco-command-keymap))))
+    (if command
+	(progn
+	  (while teco-command-escapes
+	    (aset command (car teco-command-escapes) ?\e)
+	    (setq teco-command-escapes (cdr teco-command-escapes)))
+	  (setq teco-output-buffer (get-buffer-create "*Teco Output*"))
+	  (save-excursion
+	    (set-buffer teco-output-buffer)
+	    (goto-char (point-max))
+	    (insert teco-prompt command))
+	  (teco-execute-command command)))))
+
+(defun teco-read-command ()
+  "Read a teco command string from the user."
+  (let ((command (catch 'teco-command-quit
+		   (read-from-minibuffer teco-prompt nil
+					 teco-command-keymap)))
+	teco-command-escapes)
+    (if command
+	(while teco-command-escapes
+	  (aset command (car teco-command-escapes ?\e))
+	  (setq teco-command-escapes (cdr teco-command-escapes))))
+    command))
+
+(defun teco-command-self-insert ()
+  (interactive)
+  (insert last-command-char)
+  (if (not (pos-visible-in-window-p))
+      (enlarge-window 1)))
+
+(defun teco-command-ctrl-g ()
+  (interactive)
+  (beep)
+  (throw 'teco-command-quit nil))
+
+(defun teco-command-return ()
+  (interactive)
+  (setq last-command-char ?\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 (buffer-string)))
+  (setq teco-command-escapes (cons (1- (point)) teco-command-escapes))
+  (setq last-command-char ?$)
+  (teco-command-self-insert))
+
+(defun teco-command-ctrl-u ()
+  (interactive)
+  ;; delete the characters
+  (kill-line 0)
+  ;; forget that they were ESCs
+  (while (and teco-command-escapes (<= (point) (car teco-command-escapes)))
+      (setq teco-command-escapes (cdr teco-command-escapes)))
+  ;; 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)
+  ;; forget that it was an ESC
+  (if (and teco-command-escapes (= (point) (car teco-command-escapes)))
+      (setq teco-command-escapes (cdr teco-command-escapes)))
+  ;; decide whether to shrink the window
+  (insert ?\n)
+  (if (prog1 (pos-visible-in-window-p)
+	(backward-delete-char 1))
+      (shrink-window 1)))
+
+(provide 'teco)
+
+;;; teco.el ends here