Google
 

Trailing-Edge - PDP-10 Archives - steco_19840320_1er_E35 - 10,5676/teco/source/tecsrh.mac
There are 3 other files named tecsrh.mac in the archive. Click here to see a list.
	SUBTTL	Introduction

; Copyright (c) 1980 Stevens Institute of Technology,
; Hoboken, New Jersey, 07030


; This software may be used and copied provided that this copyright notice
;is included, and provided that copies of all modifications are sent to:
;
;	TECO Project
;	Computer Center
;	Stevens Institute of Technology
;	Castle Point Station
;	Hoboken, New Jersey    07030
;
;
; The information in this software is subject to change without notice
; and should not be construed as a commitment by the authors or their
; employers.

  ; Search needed universals

	SEARCH	TECUNV		; TECO universal file

  ; Generate the prologue


	TECVER==200		; Major version number
	TECMIN==1		; Minor version number
	TECEDT==1156		; Edit level
	TECWHO==0		; Last editor


	PROLOGUE(SRH,<TECO Pattern matching and searches>) ; Generate the TITLE and other stuff
	SUBTTL	Table of Contents

;+
;.pag.lit

;		Table of Contents for TECSRH - Search and Pattern processing
;
;
;			   Section			      Page
;   1. Introduction . . . . . . . . . . . . . . . . . . . . .    1
;   2. Table of Contents. . . . . . . . . . . . . . . . . . .    2
;   3. Revision History . . . . . . . . . . . . . . . . . . .    3
;   4. Searches
;        4.1.   Commands
;             4.1.1.     FS, FN, FD, and FK . . . . . . . . .    4
;             4.1.2.     _, N, and S. . . . . . . . . . . . .    5
;        4.2.   pattern source setup. . . . . . . . . . . . .    6
;        4.3.   Set up search matrix. . . . . . . . . . . . .    9
;        4.4.   New fast search routine . . . . . . . . . . .   28
;        4.5.   Old slow but sure routine . . . . . . . . . .   32
;        4.6.   pattern found . . . . . . . . . . . . . . . .   35
;        4.7.   Autotype after succesful searches . . . . . .   35
;        4.8.   Pattern not found in this buffer. . . . . . .   36
;   5. Data segment . . . . . . . . . . . . . . . . . . . . .   38
;   6. End of TECSRH. . . . . . . . . . . . . . . . . . . . .   39

;.end lit.pag
;-
	SUBTTL	Revision History

COMMENT	|

1000	Start of this version

1012	By: Nick Bush		On: 6-August-1980
	Fix the fast search algorithm. Under certain circumstances, it was looking
	one character too far in the buffer. If this character happened to match,
	it could claim that the string was found when in fact it did not exist
	in the buffer. This would also end up with PT set at Z+1 leading to
	all sorts of problems (including PTD stopcodes in video mode).
	
	Also make REE (reenter) save all of the Tx ac's before calling lower
	level routines which use them. There are numerous places where not
	doing this can cause random errors.
	Modules: TECPRS,TECSRH

1013	By: Nick Bush		On: 6-August-1980
	Make UPDLIN use SC$WLN to write out a line if the line on the screen is
	blank, and we didn't have a function block for the replacement.
	
	Make FS and FN searches only move the text in the text buffer once, not
	do a delete and an insert. It will now only delete characters if the
	new string is shorter, and only insert space if the new string is longer.
	Modules: TECVID,TECSRH

1057	By: Nick Bush		On: 3-December-1980
	Fix search failure code to check for an iteration correctly. Previous
	edits had changed the contents of the XS stack, but had not fixed
	TECSRH.
	Modules: TECSRH

1061	By: Nick Bush		On: 18-December-1980
	1) Finish and debug Tektronix 4025 support.
	2) Add FW command.
	3) Add capability of logging screen update info in log file.
	Modules: TECUNV,TECTEK,TECSRH,TECTBL,TECECM,TECVID,TECERR

1063	By: Nick Bush		On: 19-December-1980
	Fix FW command to clear current ^D column so we will remember a new one.
	Fix ] command to respect the settings of the QR$xxx flags and not
	store text into a value only Q-reg or a value into a text only Q-reg.
	Modules: TECCMD,TECSRH

1103	By: Nick Bush		On: 1-March-1981
	Fix SLOS.Z to handle -S^EL correctly.  It was always succeeding.
	Modules: TECSRH

1104	By: Nick Bush		On: 12-March-1981
	Fix FW command when going backwards from the end of the buffer. It was
	looking at the character after the end of the buffer.
	Fix FK/FS to work correctly with ^ES in the search argument.
	Modules: TECSRH

Start of Version 200A(1126)

1127	By: Nick Bush@SIT, Robert C. McQueen@SIT		On: 15-October-1981
	Add the following new features:
	- String arguments.  {...} is a string argument.
	- Make I take them, = and == return them.
	- Implement the FC command to define immediate command tables
	- Implement the E? command to return various items.
	- Start doing some work so that TECO will work on TOPS-20
	- Start doing some work so that TECO some day may run in a section
	  besides zero.
	Modules: TECUNV,TECERR,TECPRS,TECCMD,TECSRH,TECMEM,TECUUO,TECECM,TECMVM,TECCOM,TECINI

1155	By: Robert C. McQueen		On: 12-April-1982
	Minus search didn't work if you started at the end of the buffer.
	Modules: TECSRH

1156	By: Robert C. McQueen		On: 14-April-1982
	- Fix bug in the last edit.
	- Fix problem with screen updating if /MODE:ASCI and P command.
	Modules: TECSRH,TECCMD
|
	SUBTTL	SR$INI - Search initializtion

;+
;.hl1 SR$INI
; This routine will initialze the lowsegment data for TECSRH.
;-
	$CODE			; This is in the code section

SR$INI:	MOVEI	T1,SRHBLK	; Get the BLK address
	MOVEI	T2,SRHTPT	; And the TPT address
	PJRST	M$USEB		; Set up the pointer
	SUBTTL	Searches -- Commands -- FW - Find word

;+
;.hl2 FW - Find word
;This search routine will find the nth word.  It works a little differently
;than the normal search processing in TECO.  It will position to the beginning
;of the word always.
; For example, 1FW$ will find the beginning of the next word, -FW$ will find
;the beginning of the current word (if we are in the middle of it) or find
;the beginning of the previous word if we are not in the middle of a word.
;-


; Character mask for characters which may be in words

	BRINI$(WRD)			; First character position
	BRKCH$(WRD,"A","Z")		; Include uppercase
	BRKCH$(WRD,"a","z")		; And lowercase alphabetics
	BRKCH$(WRD,"0","9")		; Also numerics

WRDMSK:	BRGEN$(WRD)			; Generate the mask

FW$CMD:	LOAD.	P1,TPTADR,+$QRTPT+TXTBUF ; Get the current buffer address
	LOAD.	P2,BLKPT,(P1)		; Get the current address of PT
	MOVE	T1,P2			; Get the pointer address
	IDIVI	T1,5			; Get the word/byte offset
	ADDI	T1,.BKTLN(P1)		; Plus the base address
	HLL	T1,BTAB-1(T2)		; Get the byte pointer
	MOVE	P4,T1			; Into a safe place
	JUMPLE	A1,FWCM.3		; Backwards search?

	LOAD.	P3,BLKEND,(P1)		; No, get the number of chars to search
	SUB	P3,P2			;  .  .  .

FWCM.0:	JUMPE	P3,FWERR		; If nothing, give the error
	PUSHJ	P,FWCHR			; Get a character
	TDNN	T3,WRDMSK(T1)		; We want this char?
	 SOJA	P3,FWCM.0		; End of buffer yet?

; Here after we have found the first character that is part of a word.
;Now look for the first character not part of the word.

FWCM.1:	SOJLE	P3,FWCM.A		; If nothing left, check where we should be
FWCM.B:	PUSHJ	P,FWCHR			; Get a character
	TDNN	T3,WRDMSK(T1)		; Is this character allowed in a word?
	 JRST	FWCM.2			; No, all done with this word
	SOJG	P3,FWCM.B		; Yes, continue on
FWCM.A:	SOJG	A1,FWERR		; If this isn't the last pass, don't move PT

FWCM.2:	SOJG	A1,[SOJA P3,FWCM.0]	; Loop for the next word
	LOAD.	P2,BLKEND,(P1)		; Get the end position
	SUB	P2,P3			; And back up the correct number of chars
	STOR.	P2,BLKPT,(P1)		; Store it
	ZERO.	T1,BLKCOL,(P1)		; Flag we have changed columns
	POPJ	P,			; And return


; Here to search backwards for the n+1 word boundary. Note that
;a 0FW command will move to the start of the current word if it is within
;one. If it is currently in a word boundary, it will move to the
;start of the previous word.

FWCM.3:	CFMN.	,BLKEND,(P1),P2		; At end of buffer?
	 SOJA	P2,FWCM.4		; Yes, don't bother with the IBP
	IBP	P4			; Change the pointer to the correct place
FWCM.4:	JUMPL	P2,FWERR		; Error if nothing left
	PUSHJ	P,FWCHRB		; Get a character going backwards
	TDNN	T3,WRDMSK(T1)		; Is this a valid word character?
	 SOJA	P2,FWCM.4		; No, try the next


; Here when we found the tail end (or middle) of a word. Now we must
;search backwards for the start.

	JUMPE	P2,FWCM.9		; If at start of buffer, we are done
	SOJL	P2,FWERR		; If we hit the start of the buffer first, give up
FWCM.6:	PUSHJ	P,FWCHRB		; Get a character
	TDNN	T3,WRDMSK(T1)		; Valid in a word?
	 JRST	FWCM.7			; No, this is the start of the previous word
	SOJGE	P2,FWCM.6		; Yes, try the next
	JUMPN	A1,FWERR		; If we need more, don't bother moving pointer
	ZERO.	,BLKPT,(P1)		; Otherwise, clear the pointer
	ZERO.	T1,BLKCOL,(P1)		; Flag no remembered column
	POPJ	P,			; And return

FWCM.7:	AOJLE	A1,[SOJA P2,FWCM.4]	; If we need to search more, continue on
	AOJA	P2,FWCM.8		; Bump back up one char



FWCM.9:	AOJLE	A1,FWERR		; If we need more, punt
FWCM.8:	STOR.	P2,BLKPT,(P1)		; Otherwise, store the position
	ZERO.	T1,BLKCOL,(P1)		; Flag no remembered column
	POPJ	P,			; And return

; Here if we can't find enough word boundaries.

FWERR:	ERROR	E.WNF			; Give the error

; Routine to get characters for FW

FWCHRB:	LDB	T1,P4			; Get a character
	ADDX	P4,<INSVL.(7,BP.POS)>	; Back up the pointer
	JUMPGE	P4,FWCH.1		; Do we need to back up a word?
	HRLI	P4,(POINT 7,,34)	; Yes, do it
	SOJA	P4,FWCH.1		; And enter common routine

FWCHR:	ILDB	T1,P4			; Forward is easy

FWCH.1:	IDIVI	T1,^D32			; Break character into word/bit offsets
	MOVN	T2,T2			; Make it the amount to shift
	MOVX	T3,1B0			; Get a bit
	LSH	T3,(T2)			; And shift it
	POPJ	P,			; All done
	SUBTTL	Searches -- Commands -- FS - Find string and replace it

;+
;.hl1 FS Command
;This routine will handle the FS command processing.  It will search
;for the nth occurance of the string and change it.  If the string is
;not found in the current editing buffer an error will be given.
;-

FSSRCH:	PUSHJ	P,S$SETUP		; Do the initial set up
					;  and bounds defaulting
	 JRST	FSUP.E			; Error, go handle it
	PUSHJ	P,S$BUILD		; Build the search matrices

; Now deterine the length of the second string that was given

	TXZ	S,S.NCCT		; Refresh ^T flag
	MOVE	CH,ARGTRM		; Get delimiter
	TXO	F,F.SRCH		; Flag so insert knows we are a search
	PUSHJ	P,INSE.0		; Call the insert argument scanning
	MOVEM	T1,INSLEN		; Store the length

; Do the searching

	PUSHJ	P,S$SEARCH		; Do the search processing in the
					;   current buffer
	  JRST	S$FRETURN		; Failed, Clean up and error

; Here if the string was found.  Determine if the new string will fit
; over the old one, or if the space must be expanded or crunched.

	PUSHJ	P,FSNSUB		; Do the FS/FN subroutine
	PJRST	S$TRETURN		; Give a good return

; Here if an error occured during setup.  We should try to eat the search string
;since otherwise a : modified search may not recover properly.

FSUP.E:	PUSH	P,T1			; Save the error code
	PUSHJ	P,S$BUILD		; Build the string
	TXZ	S,S.NCCT		; Fix the ^T flag
	MOVE	CH,ARGTRM		; Get the delimeter
	TXO	F,F.SRCH		; Flag from a searh
	PUSHJ	P,INSE.0		; Get the insert string portion
	POP	P,T1			; Get the error code back
	.ERR	(T1)			; And give the error code
	SUBTTL	Searches -- Commands -- FSNSUB - FS/FN subroutine

;+
;.hl2 FSNSUB
;This routine will do the general fix up for the inserting of the second
;string.
;-

FSNSUB:	LOAD.	T1,TPTADR,+TXTBUF	; Get the text buffer address
	SUB	P2,P4			; Put us back at the start of the string
	STOR.	P2,BLKPT,(T1)		; And store the correct position
	ZERO.	A1,BLKCOL,(T1)		; Clear current column
	MOVE	A1,P4			; Get the length of the string
	SUB	A1,INSLEN		; Minus the length of the insert string
	SKIPLE	A1			; Really have to delete anything?
	 PUSHJ	P,DELETE		; Delete the items
	MOVE	CH,ARGTRM		; Get the argument terminator
	MOVE	T1,INSLEN		; Get the insert string length
	SUB	T1,P4			; And get the amount that still needs to be expanded
	JUMPGE	T1,.+2			; Is all the space there?
	 SETZ	T1,			; Yes, don't need to expand any
	MOVEI	T2,TXTBUF+$QRTPT	; Get the address of the pointer to the text
	SKIPE	INSLEN			; If no insert string, don't need to insert anything
	 PUSHJ	P,INSE.I		; Insert the string
	MOVE	CH,ARGTRM
	TXNE	S,S.NRAD		; Null insert?
	 CAXE	CH,.CHESC		; Terminator an altmode (Escape) ?
	  SKIPA				; Skip over
	TXO	F,F.DONE		; Flag done
	POPJ	P,			; Return to the caller
	SUBTTL	Searches -- Commands -- FN - Find string and replace it

;+
;.hl1 FN Command
;This routine will handle the FN command.  This command will cause the
;string specfied to be searched for and then replaced with the second
;string specified.  If the string is not found in the current editing
;buffer the next buffer will be read until the string is found or until
;an end of file is reached.
;-

FNSRCH:	TXO	F,F.NSRH!F.SRCH		; Flag that this is an N search
	PUSHJ	P,S$SETUP		; Do the initial set up
					;  and bounds defaulting
	 JRST	FSUP.E			; Error
	PUSHJ	P,S$BUILD		; Build the search matrices

; Now deterine the length of the second string that was given

	TXZ	S,S.NCCT		; Refresh ^T flag
	MOVE	CH,ARGTRM		; Get delimiter
	PUSHJ	P,INSE.0		; Call the insert argument scanning
	MOVEM	T1,INSLEN		; Store the length

; Do the searching

FNSH.1:	PUSHJ	P,S$SEARCH		; Do the search processing in the
					;   current buffer
	  JRST	FNSH.0			; Failed, read the next buffer
					;  if that is needed

; Here if we found the string

	PUSHJ	P,FSNSUB		; Call the subroutine to do the work
	PJRST	S$TRETURN		; Give a true return

; Here if the FN search has failed.  Check the negative search flag and give
; up if it is on.  Why it is on in the first place I don't know.

FNSH.0:	TXNE	S,S.MINS		; Negative search ?
	  JRST	FNSH.2			; Yes, give a fail return
	MOVEM	A1,SRHCNT		; Store the search cound
	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the current text buffer
	PUSHJ	P,GETFDI		; Get the input file descriptor
	  JRST	FNSH.2			; Common failure exit
	LOAD	T2,.FDFLG(T1),FD.EOF	; Check to see if at end of file
	JMPT	T2,FNSH.2		; We are, just get out
	PUSH	P,P1			; Save P1
	MOVEI	P1,1			; Punch one buffer full
	PUSHJ	P,PUNSCH		; Dump it
	POP	P,P1			; Restore P1
	MOVE	A1,SRHCNT		; Restore the count
	SETZM	LOWERB			; Clear the lower bound (new buffer)
	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the text buffer address
	LOAD.	T1,BLKEND,(T1)		; Get the new upper bounds
	MOVEM	T1,UPPERB		; Set it
	JRST	FNSH.1			; Loop back

FNSH.2:	MOVE	CH,ARGTRM		; Get the argument terminator
	CAXE	CH,.CHESC		; Escape ?
	  TXZ	S,S.NRAD		; Yes, Flag so second altmode gets
					;  put in *(q-reg)
	JRST	S$FRETURN		; Do the failure return processing
	SUBTTL	Searches -- Commands -- FD - Find string and delete it

;+
;.hl1 FD command
;This routing will handle the FD command.  This command will delete from
;the current pointer to the end of the string that was found.
;-

FDSRCH:	PUSHJ	P,S$SETUP		; Do the initial set up
					;  and bounds defaulting
	 JRST	SRUP.E			; Error, go skip the string
	PUSHJ	P,S$BUILD		; Build the search matrices
	PUSHJ	P,S$SEARCH		; Do the search processing in the
					;   current buffer
	  JRST	S$FRETURN		; Give a failure return
	MOVE	A1,SAVEPT		; Get the old PT
	SUB	A1,P2			; Determine the amount to delete
	PUSHJ	P,DELETE		; Do the delete
	PJRST	S$TRETURN		; Return the correct value
					;  and do the auto type out
	SUBTTL	Searches -- Commands -- FK - Find and kill the specified string

;+
;.hl1 FK Command
; The FK search command will search for the specified string and delete
;it.
;-

FKSRCH:	PUSHJ	P,S$SETUP		; Do the initial set up
					;  and bounds defaulting
	 JRST	SRUP.E			; Error, go skip the string
	PUSHJ	P,S$BUILD		; Build the search matrices
	PUSHJ	P,S$SEARCH		; Do the search processing in the
					;   current buffer
	  JRST	S$FRETURN		; Give a failure return
	LOAD.	T1,TPTADR,+TXTBUF	; Get the text buffer address
	SUB	P2,P4			; Put us back at the start of the string
	STOR.	P2,BLKPT,(T1)		; And store the correct position
	ZERO.	A1,BLKCOL,(T1)		; Clear current column
	MOVE	A1,P4			; Get the length of the string
	PUSHJ	P,DELETE		; Delete the items
	PJRST	S$TRETURN		; Do the normal return processing
	SUBTTL	Searches -- Commands --  _

;+
;.HL1 Backarrow search
;The underscore search processing search for the specified string discarding
;all buffers that do not contain the string.
;-

LARR:	PUSHJ	P,S$SETUP		; Do the initial set up
					;  and bounds defaulting
	 JRST	SRUP.E			; Error, go skip the string
	PUSHJ	P,S$BUILD		; Build the search matrices
LARR.0:	PUSHJ	P,S$SEARCH		; Do the search processing in the
					;   current buffer
	  JRST	LARR.1			; Here if the search has failed
	PJRST	S$TRETURN		; Give a good return

; Here if the string was not found in the buffer.  Yank in the next buffer
;from the input file if there is one.

LARR.1:	TXNE	S,S.MINS		; Minus search (-_?!?!?!)
	  JRST	S$FRETURN		; Yes, just fail
	MOVEM	A1,SRHCNT		; Store the count
	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the current text buffer
	PUSHJ	P,GETFDI		; Get the input FD
	  JRST	S$FRETURN		; No input FD, just fail
	LOAD	T1,.FDFLG(T1),FD.EOF	; Get the end of file flag
	JMPT	T1,S$FRETURN		; Just return if an EOF already

	PUSHJ	P,YANK			; Get the next buffer
	MOVE	A1,SRHCNT		; Restore the search counter
	SETZM	LOWERB			; Clear the lower bounds
	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the text buffer address
	LOAD.	T1,BLKEND,(T1)		; Get the end of it
	MOVEM	T1,UPPERB		; Store as the upper bounds
	JRST	LARR.0			; Loop back for more
	SUBTTL	Searches Commands -- N - Non-stop search

;+
;.HL2 N search
;The N search processing will search for the specified string reading
;and writting buffers until the string is found.
;-

N$CMD:	TXO	F,F.NSRH		; Flag N type search
	PUSHJ	P,S$SETUP		; Do the initial set up
					;  and bounds defaulting
	 JRST	SRUP.E			; Error, go skip the string
	PUSHJ	P,S$BUILD		; Build the search matrices
N$CM.0:	PUSHJ	P,S$SEARCH		; Do the search processing in the
					;   current buffer
	  JRST	N$CM.1			; Do the common failure processing
	PJRST	S$TRETURN		; Give a good return

; Here to do the failure processing.  This means we call the common code
; in the FN/N subroutine and loop back.

N$CM.1:	TXNE	S,S.MINS		; Negative search ?
	  JRST	S$FRETURN		; Yes, give a fail return

	MOVEM	A1,SRHCNT		; Store the search cound
	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the current text buffer
	PUSHJ	P,GETFDI		; Get the input file descriptor
	  JRST	S$FRETURN		; Common failure exit
	LOAD	T2,.FDFLG(T1),FD.EOF	; Check to see if at end of file
	JMPT	T2,S$FRETURN		; We are, just get out
	PUSH	P,P1			; Save P1
	MOVEI	P1,1			; Punch one buffer full
	PUSHJ	P,PUNSCH		; Dump it
	POP	P,P1			; Restore P1
	MOVE	A1,SRHCNT		; Restore the count
	SETZM	LOWERB			; Clear the lower bound (new buffer)
	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the text buffer address
	LOAD.	T1,BLKEND,(T1)		; Get the new upper bounds
	MOVEM	T1,UPPERB		; Set it
	JRST	N$CM.0			; Loop back
	SUBTTL	Searches -- Commands -- S - General search

;+
;.HL2 S search
;This command will search for the specfied string and stop when it is
;found.  If the string specified is not found it will give an error and
;leave the pointer untouched.
;-

SERCH:	PUSHJ	P,S$SETUP		; Do the initial set up
					;  and bounds defaulting
	 JRST	SRUP.E			; Error, go skip the string
	PUSHJ	P,S$BUILD		; Build the search matrices
	PUSHJ	P,S$SEARCH		; Do the search processing in the
					;   current buffer
	  JRST	S$FRETURN		; Do the failure return processing
	JRST	S$TRETURN		; Do the true return processing

; Here if S$SETUP failed

SRUP.E:	PUSH	P,T1			; Save T1
	PUSHJ	P,S$BUILD		; Build the search string
	POP	P,T1			; restore the error code
	.ERR	(T1)			; And tell the user
	SUBTTL	Searches -- S$SETUP - Set up for a search

;+
;.hl1 S$SETUP
;This routine will setup the search processing to do a search.  It will
;default the bounds according to the information that was given to it.
;It will save the pointer so that it can be restored later.
;-

S$SETUP:
	TXZ	S,S.MINS		; Flag not negative search (yet)
	LOAD.	T4,TPTADR,+$QRTPT+TXTBUF ; Get the buffer address
	LOAD.	T4,BLKPT,(T4)		; Get the character address of PT
	MOVEM	T4,SAVEPT		; Save in case the search fails
	MOVEM	T4,UPPERB		; PT is upper bound on backward searches
	TXZE	F,F.ARG2		; If two arguments then bounded search
	 JRST	BOUNDS			; Bounded search
	SETZM	LOWERB			; Save as default lower bound
	SKIPE	A1			; Zero ?
	 JRST	SERC33			; No - Skip this
	TXNN	F,F.ARG			; There must be no argument
	 JRST	SERC33			; There isn't, keep going

ISAERR:	MOVEI	T1,E.ISA##		; Get the error code
	POPJ	P,			; And return

SERC33:	SKIPGE	A1			; Get the argument where it is wanted
	 TXOA	S,S.MINS		; Negative search
	SETZM	UPPERB			; No upperbound on forward searches
	MOVMS	A1			; Correct in case of minus searching
	JRST	SERCHA			; Start the search

;Here if bounded search, set up bounds

BOUNDS:	JUMPL	A2,.+2			; Negative bounds are illegal
	 JUMPGE	A1,.+2			;  .  .  .
	  JRST	ISAERR			; Go return the error
	TXZ	F,F.NSRH!F.LARW!F.ARG	; FN and N go to FS and S
	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the address of the text buffer
	CFMG.	,BLKEND,(T1),A2		; Too big ?
	 LOAD.	A2,BLKEND,(T1)		; Yes - Make it the end
	STOR.	A2,BLKPT,(T1)		; Store the new pointer
	CAML	A1,A2			; Minus implied ?
	 JRST	SAVESH			; No - Save te mounds
	EXCH	A2,A1			; Yes - Exchage the arguments
	TXO	S,S.MINS		; Flag that it is now a minus search
SAVESH:	MOVEM	A2,LOWERB		; Save the lower bound
	MOVEM	A1,UPPERB		; Save the upper bound
	SETZ	A1,			; Assume first occurance in case bounded

;Adjust upper and lower bounds

SERCHA:	SETZ	T1,			; Good lower bound
	LOAD.	T2,TPTADR,+$QRTPT+TXTBUF ; Get the buffer address
	LOAD.	T2,BLKEND,(T2) 		; Get the end
	CAMLE	T1,LOWERB		; Is the lower bound too low ?
	 MOVEM	T1,LOWERB		; Yes - Adjust it
	CAMGE	T2,LOWERB		; Is the upper bound too low ?
	 MOVEM	T2,LOWERB		; Yes - Adjust it
	SKIPE	UPPERB			; Fix zero upper bound
	 CAMGE	T2,UPPERB		; Greater that the upper bound ?
	  MOVEM	T2,UPPERB		; Save the new upper bound
	CAMLE	T1,UPPERB		; Is the lower bound .LE. the upper ?
	 MOVEM	T1,UPPERB		; No - Fix it
	PJRST	.POPJ1			; Give a good return
	SUBTTL	Searches -- S$BUILD - Build the search matrix

;+
;.hl1 S$BUILD
;This routine will build the search matrices if needed.  It will only rebuild
;the search matrix if the previous search matrix used a ^G(q-reg) in the
;search.
;-

S$BUILD:
	MOVX	CH,.CHESC	; Use Escape as the delimiter if @ not seen
	TXZN	F,F.SLSL	; Atsign seen ?
	  JRST	SERCHB		; No - Terminator altmode
	PUSHJ	P,SKRCH		; Yes - Get the user specified delimiter
	ERROR	E.USR		; ++ Unterminated search command

; Determine whether we can use the previous pattern

SERCHB:	MOVEM	CH,T2		; T2:=Pattern source string delimiter
	MOVEM	CH,ARGTRM	; Save delimiter for FS insertion
	SETZM	SCNEST		; Search nest level is zero

	PUSHJ	P,SKRCH		; Look ahead 1 character
	 ERROR	E.USR
	CAIE	CH,(T2)		; Is it the delimiter?
	 JRST	SERCHT		; No, an argument is given

	SKIPL	SRHCTR		; Yes, use previous pattern string
				; unless there was none or last had error
	 ERROR	E.SNA		; ++ Initial search with no argument
	SKIPN	SCTLGA		; but not if remembered pattern source used ^Gi
	  POPJ	P,		; Ok, Use the previous matrices
; Move a new pattern source to storage

SERCHT:	TXZ	S,S.XMAT	; Clear exact match flag
	STORE	T1,SMATRX,SMATRX+SCLRLN-1,0 ; Clear previous matrices
	SETZM	SRHCTR		; Clear source pattern length counter
	SETZM	SCTLGA		; Assume pattern source doesn't use ^Gi
	MOVE	P4,[POINT 7,SRHARG] ; Point to start of storage area
	JRST	SERCHD		; 1st character already in

SERCHC:	PUSHJ	P,SKRCH		; Get next character of pattern source
	 ERROR	E.USR
SERCHD:	CHKEO	EO21,SERCHE	; If EO=1, ^R is just text
	CAXE	CH,$CHQOT	; Quoting character?
SERCHE:	CAXN	CH,.CHCNQ	; ^Q?
	 JRST	SERCHG		; Yes, next character is text
	CAIN	CH,(T2)		; The delimiter?
	 JRST	SERCH0		; Yes
	CAXN	CH,.CHCNT	; ^T?
	 JRST	SERCHU		; Yes
	TXNE	S,S.NCCT	; ^T flag on?
	 JRST	SERCHF		; Yes, ^V and ^W are just text
	CAXE	CH,.CHCNV	; ^V?
	 CAXN	CH,.CHCNW	; ^W?
	  TXO	S,S.XMAT	; Yes, set exact match flag
	CHKEO	EO200,SERCHF	; Don't check for ^A/^B if old mode
	CAXE	CH,.CHCNA	; Control-A
	 CAXN	CH,.CHCNB	; Or control-B?
	  TXO	S,S.XMAT	; Yes, flag exact match

SERCHF:	AOS	T1,SRHCTR	; Bump string counter
	CAILE	T1,^D80		; Still fit in store?
	 ERROR	E.STC
	IDPB	CH,P4		; Yes, store character
	JRST	SERCHC		; and go back for more

SERCHG:	AOS	T1,SRHCTR	; Count the ^R (^Q)
	CAILE	T1,^D80		; Will it fit?
	 ERROR	E.STC
	IDPB	CH,P4		; Yes, store it
	PUSHJ	P,SKRCH		; Get next character
	  ERROR	E.USR
	JRST	SERCHF		; and store it as text

SERCHU:	TXC	S,S.NCCT	; ^T, complement control command switch
	JRST	SERCHF
	SUBTTL	Searches -- Set up search matrix

SERCH0:	TXZ	S,S.NCCT	; Refresh ^T flag
	MOVEI	P1,0		; Start at beginning of pattern
	IDPB	P1,P4		; Store as a null for error typeout
	MOVE	T1,SRHCTR	; Get the number of characters
	STOR.	T1,BLKEND,+SRHBLK ; Store it
	MOVEI	T1,SRHBLK	; Get the block address
	SETZ	T2,		; And set up to read from first
	PUSHJ	P,SETINC	; character
	 JFCL			; Will never happen
	MOVEI	P4,SRHTPT	; Get the address of the pointer
	PUSHJ	P,SCH.0		; Set up the matrix
	LSH	P1,-2		; Change index to pattern length
	MOVEM	P1,PATLEN	; Save it
	MOVNS	SRHCTR		; Flag pattern is legal
	JRST	ROTATE		; And go rotate it
; Set up a 131 by 36 bit table based on the pattern source.
; The table is implemented as a four word by 36 table, with the first
; 32 bits of the words used for the four portions of the ASCII character
; set (i.e. 0-37, 40-77, 100-137, 140-177) and three of the bits left over
; in the last word used for the "bogus" characters BEGPAG, ENDPAG, and
; SPCTAB.  This is a little harder to set up for single letters in the
; pattern source, but is much easier for ranges and makes the fast search
; algorithm setup much faster.  The table is then rotated into the old
; TECO 36 by 131 bit table for the actual search matrix.

SCH.0:	LOAD.	T1,TPTADR,(P4)	; Get the address of the text
	PUSHJ	P,GETINC	; And get a character
	 POPJ	P,		; Return if we hit the end of the buffer
	PUSHJ	P,SCH.1		; Process the character
	 JRST	SCH.0		; Get the next character, this one didn't count
	ADDI	P1,4		; End of a pattern position, on to next
	CAILE	P1,^D36*4	; and error if more than 36 positions in pattern
	 ERROR	E.STL		; ++ Search too long
	JRST	SCH.0		; Otherwise, loop for the next character

; Here to build a single character entry

SCH.1:	MOVEI	T1,S2TABL	; Check for control character in string
	SKIPE	SCNEST		; Control-G allowed?
	 MOVEI	T1,S1TABL	; No, avoid it
	TXNE	S,S.NCCT	; ^T flag on?
	 MOVEI	T1,S3TABL	; Yes, use restricted table
	PUSHJ	P,NDISP1	; Go search table, don't return if found

SCH.9:	CHKEO	EO21,SCH.4	; Not control, if EO=1, force exact mode
	TXNN	S,S.NCCT	; If ^T flag on, all control characters are legal
	 PUSHJ	P,CKNCC		; OF, all other control characters are illegal
				; (Don't return if any)
SCH.2:	TXNE	S,S.EMAT	; Forced either match on?
	 JRST	SCH.3		; Yes, match either
	TXNN	S,S.XMAT	; No, want an exact match?
	 TXNE	F,F.PMAT	; No, want global exact match?
	  JRST	SCH.4		; Asked for exact match

SCH.3:	CAXL	CH,"a"		; Match either, is it lower case?
	 CAXLE	CH,"z"
	  JRST	.+2		; No
	SUBI	CH,"a"-"A"	; Yes, make it upper case
	CAIL	CH,"A"		; Is it upper case?
	 CAILE	CH,"Z"
	  JRST	SCH.5		; No
	MOVSI	T3,400000	; Yes, convert character to bit of 131
	MOVNI	T2,-"@"(CH)	; want - (<ch>-100) for LSH
	LSH	T3,0(T2)	; Position bit to letter range
	IORM	T3,BITMAT+2(P1)	; Set match on upper case
	IORM	T3,BITMAT+3(P1)	; and lower case characters
	JRST	SCH.6
SCH.4:	PUSHJ	P,CASE		; Exact mode, adjust pattern character case

SCH.5:	MOVSI	T1,400000	; Convert character to bit of 131
	MOVE	T2,CH		; Copy of character
	IDIVI	T2,^D32		; Using 32 bits per word, figure word and bit
	ADDI	T2,0(P1)	; Word plus current pattern position
	MOVNS	T3		; Negative of remainder for bit shift
	LSH	T1,0(T3)	; Position bit within 32 bit range for each word
	IORM	T1,BITMAT(T2)	; and include it in appropriate word

SCH.6:	AOS	(P)		; Increment the return address
	POPJ	P,		; Yes, return to caller
; Control character dispatch table for second scan of pattern source

S2TABL:	XWD	CNTLG,.CHBEL	; ^G
S1TABL:	XWD	CNTLE,.CHCNE	; ^E
	XWD	CNTLX,.CHCNX	; ^X
	XWD	CNTLN,.CHCNN	; ^N
	XWD	CNTLS,.CHCNS	; ^S
	XWD	CNTLA,.CHCNA	; ^A
	XWD	CNTLB,.CHCNB	; ^B
	XWD	CNTLV,.CHCNV	; ^V
	XWD	CNTLW,.CHCNW	; ^W
	XWD	CNTLBS,.CHCBS	; ^\
	XWD	CNTLCF,.CHCCF	; ^^

; Shorter table used for ^T on mode starts here

S3TABL:	XWD	CNTLT,.CHCNT	; ^T
	XWD	CNTLQ,.CHCNQ	; ^Q
	XWD	CNTLR,$CHQOT	; Quoting character?
	XWD	CNTLLB,.CHESC	; ESCape
	XWD	0,0		; End of list
; Macro to set bits in bit table. The three arguments are:
;	AC	- AC to use for doing the sets
;	NAME	- Name of break set
;	ADDRESS	- Address of four word entry

DEFINE	SETBRK(AC,NAME,ADDRESS)<
	$$$CUR==0			;; Current ac contents are zero
	IFN <BRWRD$(NAME,0)>,<		;; Any bits set in first word?
		$$$CUR==BRWRD$(NAME,0)	;; Yes, get the first word
		MOVX	T1,<BRWRD$(NAME,0)> ;; Get the first word
		IORM	T1,0+ADDRESS	;; Turn on the bits in the first word
	> ;; End of IFN <BRWRD$(NAME,0)>
	IFN <BRWRD$(NAME,1)>,<		;; Anything in second word?
		IFN $$$CUR^!<BRWRD$(NAME,1)>,< ;; Yes, is this the same as the previous?
			$$$CUR==BRWRD$(NAME,1) ;; Remember what we have
			MOVX	T1,<BRWRD$(NAME,1)> ;; And get it into the ac
		> ;; End of IFN $$$CUR^!<BRWRD$(NAME,1)>
		IORM	T1,1+ADDRESS	;; Turn on the bits
	> ;; End of IFN <BRWRD$(NAME,1)>
	IFN <BRWRD$(NAME,2)>,<		;; Anything in second word?
		IFN $$$CUR^!<BRWRD$(NAME,2)>,< ;; Yes, is this the same as the previous?
			$$$CUR==BRWRD$(NAME,2) ;; Remember what we have
			MOVX	T1,<BRWRD$(NAME,2)> ;; And get it into the ac
		> ;; End of IFN $$$CUR^!<BRWRD$(NAME,2)>
		IORM	T1,2+ADDRESS	;; Turn on the bits
	> ;; End of IFN <BRWRD$(NAME,2)>
	IFN <BRWRD$(NAME,3)>,<		;; Anything in second word?
		IFN $$$CUR^!<BRWRD$(NAME,3)>,< ;; Yes, is this the same as the previous?
			$$$CUR==BRWRD$(NAME,3) ;; Remember what we have
			MOVX	T1,<BRWRD$(NAME,3)> ;; And get it into the ac
		> ;; End of IFN $$$CUR^!<BRWRD$(NAME,3)>
		IORM	T1,3+ADDRESS	;; Turn on the bits
	> ;; End of IFN <BRWRD$(NAME,3)>
> ; End of DEFINE SETBRK


; Control S matches any separator character (i.e., any character not
; a letter, number, period, dollar, or percent)

	BRINI$(CTS,ALL)			; Initialize to all chars
	UNBRK$(CTS,.CHNUL)		; Don't allow nulls to match
	UNBRK$(CTS,"%")			; Except %
	UNBRK$(CTS,"$")			; Or dollar sign
	UNBRK$(CTS,".")			; Or dot
	UNBRK$(CTS,"0","9")		; Or a number
	UNBRK$(CTS,"A","Z")		; Or upper case letter
	UNBRK$(CTS,"a","z")		; Or lower case letter
	BRKCH$(CTS,BEGPAG)		; Or start of page
	BRKCH$(CTS,ENDPAG)		; Or end of page

CNTLS:	SETBRK	T1,CTS,BITMAT(P1) ; Set the break set
	JRST	SCH.6		; Go return

; Control X matches any single character

	BRINI$(CTX,ALL)		; Control-X matches all chars
	UNBRK$(CTX,.CHNUL)	; Except a null

CNTLX:	SETBRK	T1,CTX,BITMAT(P1) ; Set the correct bits
	JRST	SCH.6		; Go return
; Control R is the same as Control Q (Provided EO > 1)
; except it doesn't cause rubout problems

CNTLR:	CHKEO	EO21,SCH.5	; If EO=1, ^R is just text

; Control Q causes the next character to be taken as text, even if it is
; a control character or the delimiter

CNTLQ:	LOAD.	T1,TPTADR,(P4)	; Get the next character
	PUSHJ	P,GETINC	;  .  .  .
	 POPJ	P,		; Done
	JRST	SCH.2		; Process with special checks

; Control V causes the next character to be made lower case
; Two Control V's set lower case mode until further notice

CNTLA:	CHKEO	EO200,SCH.9	; If before version 200
CNTLV:	CHKEO	EO21,SCH.5	; If EO=1, ^V is just text
	PUSHJ	P,C.V		; Set ^V flags
	POPJ	P,		; All done

; Control W causes the next character to be taken without case conversion
; Two Control W's set standard case mode until further notice

CNTLB:	CHKEO	EO200,SCH.9	; Only version 200 and on
CNTLW:	CHKEO	EO21,SCH.5	; If EO=1, ^W is just text
	PUSHJ	P,C.W		; Set ^W flags
	POPJ	P,		; Done

; Control \ inverts case match mode, starting at accept either

CNTLBS:	CHKEO	EO21,SCH.5	; If EO=1, ^\ is just text
	TXC	S,S.EMAT	; Complement accept either flag
	POPJ	P,		; All done

; When searching for ALTmode under EO=1, both ESCape and ALTmode match

CNTLLB:	CHKEO	EO21,.+2	; EO=1?
	JRST	SCH.5		; No, accept ESCape only
	MOVEI	T1,000040	; Yes, mark ALTmode as an acceptable character
	IORM	T1,BITMAT+3(P1)
	JRST	SCH.5		; and ESCape

; Control circumflex causes immediately following @[\]^_ to be converted to
; the appropriate character in the lower case range

CNTLCF:	CHKEO	EO21,SCH.5	; If EO=1, ^^ is just text
	LOAD.	T1,TPTADR,(P4)	; Get the address
	PUSHJ	P,GETINC	; Get the next character
	 POPJ	P,		; All done
	PUSHJ	P,CVTSPC	; Convert it to lower case if appropriate
	JRST	SCH.2		; Go store this character
; Control T inverts the control character interpretation switch
; The initial setting is that all control character commands are active
; With the switch on, only ^Q, ^R, and ^T commands exist, but all other
; control characters are legal

CNTLT:	CHKEO	EO21,SCH.5	; If EO=1, ^T is just text
	TXC	S,S.NCCT	; Complement current setting
	POPJ	P,		; Return

; Control N - invert the sense of the following "character", i.e. accept
; anything but the specified character

CNTLN:	MOVSI	T1,-4		; Set AOBJN count for the 4 words of this position
	HRR	T1,P1		; of the pattern
CTLN.0:	PUSH	P,BITMAT(T1)	; Save the current status of the pattern (in case
				; of ^E[A,^N^EW] for example)
	SETZM	BITMAT(T1)	; Start over again
	AOBJN	T1,CTLN.0	; Loop through this position

CTLN.3:	LOAD.	T1,TPTADR,(P4)	; Get the buffer address
	PUSHJ	P,GETINC	; And get a character
	 JRST	CTLN.2		; None left, go restore entries
	PUSHJ	P,SCH.1		; Build the table for the character
	 JRST	CTLN.3		; Try again

CTLN.2:	MOVEI	T1,4		; Now go back through the 4 words
	MOVEI	T2,BITMAT+3(P1)	; starting at the high end 'cause of stack
CTLN.1:	SETCM	T3,0(T2)	; complementing the resulting setting
	TRZ	T3,17		;  (remembering only using 32 bits per word)
	POP	P,0(T2)		; Get back the original bits
	IORM	T3,0(T2)	; Include the new bits wanted
	SUBI	T2,1		; Back up to previous word (need a ASOBJN)
	SOJG	T1,CTLN.1	; and loop through all 4 words
	PJRST	.POPJ1		; Return
; Control Gi causes the text in Q register i to be substituted into
; the search string at this point

CNTLG:	CHKEO	EO21,SCH.5	; If EO=1, ^G is just text
	SETZB	T1,CTGSVE	; No default name
	MOVEI	T2,CTGCHR	; Get the character routine
	MOVEI	T3,CTGREA	; And the re-eat routine
	MOVEI	T4,(P4)		; Get the TPT address
	PUSHJ	P,REDQRG	; And get the Q-reg name (with parens)
	 JRST	[PUSHJ	P,CTGCHR	; If not, try normal one
		  ERROR	E.ICG		; Bad
		PUSHJ	P,QREGV2	; Get the block address
		JRST	.+1]		; And continue
	PUSH	P,P4		; Save source pointer
	MOVEI	P4,$QRTPT(T1)	; Get the TPT address
	LOAD.	T1,TPTADR,(P4)	; Get the Q-register
	JUMPE	T1,[ERROR	E.NTQ] ; No text in Q-reg
	SETZ	T2,		; Start from start of buffer
	PUSHJ	P,SETINC	;  .  .  .
	 JFCL			; Ignore error

	PUSHJ	P,SCH.0		; Go process the string as pattern source

	POP	P,P4
	POPJ	P,		; And return


; Routines to fetch chars for REDQRG

CTGCHR:	SETZ	CH,		; Clear CH
	EXCH	CH,CTGSVE	; Get the saved character
	JUMPN	CH,.POPJ1	; Got one, return
	$SAVE	<T1,T2>		; Save some ac's
	LOAD.	T1,TPTADR,(P4)	; get the buffer address
	PJRST	GETINC		; get a character

CTGREA:	MOVEM	CH,CTGSVE	; Save the character
	POPJ	P,		; And return
; Control E commands all go through here

CNTLE:	CHKEO	EO21,SCH.5	; If EO=1, ^E is just text
	LOAD.	T1,TPTADR,(P4)	; Get the address
	PUSHJ	P,GETINC	; And get the character
	 JRST	CNTLER		; Give the error
	MOVEI	T1,S4TABL	; Set to search for ^E command characters
	PUSHJ	P,NDISPT	;  and look for legal commands (no return if good)
CNTLER:	ERROR	E.ICE

; Dispatch table for ^E commands

S4TABL:	XWD	CNTLEA,"A"	; ^EA	accept any alpha
	XWD	CNTLEV,"V"	; ^EV	accept any lower case alpha
	XWD	CNTLEW,"W"	; ^EW	accept any upper case alpha
	XWD	CNTLED,"D"	; ^ED	accept any digit
	XWD	CNTLEL,"L"	; ^EL	accept any end of line character
	XWD	CNTLES,"S"	; ^ES	accept a string of spaces and/or TABs
	XWD	CNTLEN,74	; ^E<NNN> accept the ACSII character represented by <nnn>
	XWD	CNTLEB,133	; ^E[A,B,C] accept A or B or C or ...
	XWD	0,0		; End of list


; Control EA - accept any alphabetic character

CNTLEA:	SETBRK	T1,ALPH,BITMAT(P1) ; Flag all alphabetics
	JRST	SCH.6		; And advance to next char pos

; Control EV - accept any lower case alphabetic character

	BRINI$(LOW)		; Make mask for lower case
	BRKCH$(LOW,"a","z")	;  .  .  .

CNTLEV:	SETBRK	T1,LOW,BITMAT(P1) ; Flag lower case letters accepted
	PJRST	SCH.6		; Return

; Control EW - accept any upper case alphabetic character

	BRINI$(UPP)		; Make uppercase mask
	BRKCH$(UPP,"A","Z")	;  .  .  .

CNTLEW:	SETBRK	T1,UPP,BITMAT(P1) ; Flag uppercase valid
	PJRST	SCH.6		; Return

; Control ED - accept any digit

	BRINI$(DIG)		; Make digit mask
	BRKCH$(DIG,"0","9")	;  .  .  .

CNTLED:	SETBRK	T1,DIG,BITMAT(P1) ; Flag the digits valid
	PJRST	SCH.6		; Return

; Control EL - accept any end of line character (including buffer end)

	BRINI$(EOL)		; Make end of line mask
	BRKCH$(EOL,.CHLFD,.CHFFD) ; Flag the normal break chars
	BRKCH$(EOL,ENDPAG)	; And include the end of page character

CNTLEL:	SETBRK	T1,EOL,BITMAT(P1) ; Flag the end of line chars
	PJRST	SCH.6		; Return
; Control ES - accept any string of spaces and/or TABs

	BRINI$(SPC)		; Mask for spaces/tabs
	BRKCH$(SPC,.CHTAB)	; Flag the tab
	BRKCH$(SPC," ")		; And the space
	BRKCH$(SPC,SPCTAB)	; And the special bit

CNTLES:	SETBRK	T1,SPC,BITMAT(P1) ; Flag the space characters
	PJRST	SCH.6		; Return

; Control E[a,b,c,...] - accept any of "characters" a or b or c

CNTLEB:	AOS	SCNEST		; Remember our nesting level
CTLE.0:	LOAD.	T1,TPTADR,(P4)	; Get the buffer address
	PUSHJ	P,GETINC	; get next character
	 PJRST	CNTLER		; Error
	PUSHJ	P,SCH.1		; Process the next "character"
	 JRST	CTLE.0		; Wasn't anything we could store, try again
	LOAD.	T1,TPTADR,(P4)	; Get the buffer address
	PUSHJ	P,GETINC	; Get the next character
	 JRST	CNTLER		; Error
	SOJL	P2,CNTLER	; Error if off end of string
	CAIN	CH,","		; Another "character" to come?
	 JRST	CTLE.0		; Yes, go include it too
	CAIE	CH,"]"		; No, correct ending to ^E command?
	 ERROR	E.ICE
	PJRST	SCH.6		; Return

; Control E<nnn> - accept the ASCII character whose octal representation is nnn

CNTLEN:	MOVEI	P2,0		; Clear number accumulator
CTEN.1:	LOAD.	T1,TPTADR,(P4)	; Get a character
	PUSHJ	P,GETINC	;  .  .  .
	 JRST	CNTLER		; None there
	CAIN	CH,">"		; The other end of the number?
	 JRST	CTEN.2		; Yes, done
	CAIL	CH,"0"		; Is it an digit?
	 CAILE	CH,"7"
	  ERROR	E.ICE
	LSH	P2,3		; Yes, scale up the previous value
	ADDI	P2,-60(CH)	; and add in the new oit
	JRST	CTEN.1		; then go try for more

CTEN.2:	CAILE	P2,177		; Make sure it's legitimate
	 ERROR	E.ICE
	MOVE	CH,P2		; Copy the result as the character
	JRST	SCH.5		; and go set the appropriate bit
; Now we need to build up TECO's standard search table, a 36 bit by 131. word
; table with each pattern position being a slice of the 131 words, with all of
; the acceptable characters for each position marked by a bit on in the word
; reached by using the character directly as an index into the table (the extra
; 3 words are for "beginning of page", "end of page", and "this position matches
; strings of spaces and/or TABs").  At the same time we will set up the two
; simple tables for the fast search algorithm (DELTA0), since it is
; much quicker to do this now if we use the fast one.
; The conversion is done by rotating the 131. bit by 36 word table 90 degrees.
; Since that table was built first (instead of the normal TECO table as in
; standard TECO), the loop is only needed for as many times as there were
; pattern characters (doing it in the other order requires a loop through all
; 131 characters with no possibility for less).

; AC usage:
; P1	AOBJN pointer with "virtual" index into 131 by 36 table (word index/4)
; P2	actual word index into 131 by 36 table 
; P3	bit mask specifying pattern position we're currently doing
; P4	AOBJN pointer into the 131 bits of an entry of the 131 by 36 table
; A1	First arg
; T2+T3	current words worth of the 131 bits and the JFFO result

SLARGE==10777777		; A special large number for DELTA0 used for
				; the characters defining the rightmost pattern
				; position

ROTATE:	MOVN	P1,PATLEN	; Get the number of pattern positions used
	HRLZS	P1		; as an AOBJN pointer
	MOVEI	P2,0		; Clear the actual index

	MOVE	P3,PATLEN	; Initialize DELTA0 to the number
	MOVEM	P3,DELTA0	; of positions in the pattern
	MOVE	P4,[XWD	DELTA0,DELTA0+1]
	BLT	P4,DELTA0+SPCTAB

	SUBI	P3,1		; Pattern length - 1 is the distance we are from
	MOVEM	P3,ROTLEN	; the end of the pattern at the moment

	MOVSI	P3,400000	; Start mask at first pattern position
ROTA.1:	MOVSI	P4,-BITMLN	; Set AOBJN pointer into the 131 bits
ROTA.2:	SKIPE	T2,BITMAT(P2)	; Get 32 of those, seeing if any are on
ROTA.3:	JFFO	T2,[MOVSI T4,400000	; and if any are, see which the first one is
		MOVN	T1,T3		; Got one, make a mask to turn it off
		LSH	T4,0(T1)
		ANDCM	T2,T4		; and do so

		ADDI	T3,0(P4)	; Add 0, 32, 64, or 96 to the bit number
		IORM	P3,SMATRX(T3)	; and turn on the position bit for the character
		SKIPN	T1,ROTLEN	; Get the current distance from the right end of the pattern
		MOVX	T1,SLARGE	; At the right, change to the special number
		MOVEM	T1,DELTA0(T3)	; Set that in fast loop table
		JRST	ROTA.3]		; On to next bit

	ADDI	P2,1		; Finished a word of the 131 bit string
	ADDI	P4,^D31		; Next word is 32 farther into the 36 by 131 table
	AOBJN	P4,ROTA.2	; Loop until all 131 bits done

	LSH	P3,-1		; On to the next pattern mask position
	SOS	ROTLEN		; and distance from the end
	AOBJN	P1,ROTA.1	; and loop through all used pattern position
; Now determine which search method we will use.  If ^ES appeared we have to use
; the old slow method.  Initially if we need to match BEGPAG or ENDPAG, we will
; use the old method.  Also we will arbitrarily select 3 as the shortest string
; which will benefit from using the new search.  As an aid, turn off the BEGPAG
; and ENDPAG bits which don't appear at the appropriate end of the pattern,
; since they obviously won't match except there.

FIGSCH:	SETZB	P3,SCHTYP	; Assume an old style search

	MOVN	P1,PATLEN	; Generate a bit mask for the last pattern
	MOVSI	P4,400000	; position used in this search
	ANDM	P4,SMATRX+BEGPAG; (with a side effect of clearing extra begin page bits)
	LSH	P4,1(P1)

	MOVE	P1,PATLEN	; Now see how long the pattern is
	CAILE	P1,2		; If it is fewer than 3 positions long,
	SKIPE	SMATRX+SPCTAB	; or if there were any ^ES positions,
	  POPJ	P,			;  just go return and user old search

	ANDM	P4,SMATRX+ENDPAG; We know pattern is longer than 1, so clear extra end page bits
	SKIPN	SMATRX+BEGPAG	; If either end of buffer will match,
	SKIPE	SMATRX+ENDPAG	; ...
	  POPJ	P,			; Just return and use the old method

	SETOM	SCHTYP		; We win with the new one, remember that in case
				; this was an nSFOO$ type
; We are going to use the new search, set up the more costly DELTA2 table.
; This table is based on the arrangement of characters in the pattern.
; It uses the existence (or non-existance) of matching substrings in the
; pattern to be able to shift the pattern farther than would be indicated by
; DELTA1, e.g. if the pattern is ACACACACACAABC and the part of the searched
; string being examined is CABC, DELTA1 will only shift the pattern right 3
; positions, while "looking" at the pattern will tell a human observer that
; the pattern can be shifted its whole length without missing any possible
; matches.

; A few bits in P2 for use during the DELTA2 setup
NEDSET==1B35		; This position of DELTA2 still needs setting up
WNTOFF==1B34		; We shifted off the end of the pattern this pass
FSTIME==1B33		; This is the first pass - use a special value instead of
			; having to initialize the index matrix (INDMAT)

; AC usage (see comment at ROTATE)
; T4	the highest entry currently being used in INDMAT
; P4	a number used to indicate how far the pattern can be shifted when we
; 	 find a mismatch between sub-pattern strings
; P2	used for the above flag bits
; A2	index into INDMAT for updates to it as matches occur in sub-patterns
; CH	index into INDMAT for loop
; P3	index into pattern (*4 since 4 words per pattern position)
; J	temporary index into pattern (also *4)
; P1	AOBJN pointer
; A1	1st argument for search

; We want to look for sub-strings in the pattern matching rightmost sub-strings
; of the pattern.  If none are found, then as in the above example when more than
; one pattern position has been matched we know we can shift farther than to
; the next occurance of single pattern characters.  If some matches are found
; then we can try them next immediately.
; The examination is implemented by using an array of pointers (indices into
; the pattern) (INDMAT), stored in decreasing order and overwritten each pass
; by the pointers for the next pass.  When I points to the beginning of the
; rightmost n characters of the pattern, then each pointer in INDMAT points
; to the beginning of a sub-string which matches those n characters.  When
; INDMAT has been emptied, all of these sub-strings have been matched and the
; rest of DELTA2 can be set to shift the pattern its entire length.  The
; initial setting of INDMAT (implemented by FSTIME) is such that every pattern
; position is examined on the first pass.

	MOVEI	T4,-1(P1)	; Start the top of INDMAT at pattern length - 1
	MOVEI	P4,-1(P1)	; Start the non-match shift at pattern length
				; (adjusted because a 0-index is subtracted from it)
	MOVX	P2,NEDSET!FSTIME ; The first setting is needs setting, first pass,
				; and haven't gone off the end

	MOVEI	P3,-1(P1)	; Start at right end of pattern (0-indexed)
	LSH	P3,2		; adjusted for being 4 word bit strings
	MOVEI	P1,0(P3)	; Set initial INDMAT value to shift all less 1
				; remembering the first SUBI 4
SET2.2:	MOVEI	A2,0		; Start used INDMAT entry index off at none
	MOVN	CH,T4		; Make an AOBJN pointer for loop through INDMAT
	HRLZS	CH

SET2.3:	TXNN	P2,FSTIME	; Get the appropriate INDMAT entry
	SKIPA	P1,INDMAT(CH)	; Not the first time, use the real array
	SUBI	P1,4		; The first pass, use our fake value

	MOVE	T2,BITMAT(P3)	; Figure out if any of the characters matched
	AND	T2,BITMAT(P1)	; by the position we are looking at at highest
	MOVE	T3,BITMAT+1(P3)	; level (P3) also match at the position indicated
	AND	T3,BITMAT+1(P1)	; by the substring table (INDMAT - D)
	OR	T2,T3
	MOVE	T3,BITMAT+2(P3)	; (AND the strings together, if result is zero
	AND	T3,BITMAT+2(P1)	; then no characters match)
	OR	T2,T3
	MOVE	T3,BITMAT+3(P3)
	AND	T3,BITMAT+3(P1)
	OR	T2,T3
	JUMPE	T2,SET2.5	; If zero, no matches here

	JUMPE	P1,SET2.4	; Did we just match with the leftmost position?
	MOVEI	T1,-4(P1)	; No, update the index matrix to check the position
	MOVEM	T1,INDMAT(A2)	; in front of this for finding substrings
	AOSA	A2		; Remember we used another element of INDMAT

SET2.4:	TXO	P2,WNTOFF	; We matched at the left end, that goes off the end

SET2.5:	TXNN	P2,NEDSET	; Do we still need to set up this position?
	JRST	SET2.6		; No, skip all the logical stuff
	MOVE	T2,BITMAT(P1)	; Yes, then we need to figure out if the substring
	ANDCM	T2,BITMAT(P3)	; indicated position (P1) character set is a
	MOVE	T3,BITMAT+1(P1)	; subset of the high level (P3) character set
	ANDCM	T3,BITMAT+1(P3)	; (Done by D .AND. .NOT. I .NE. 0)
	OR	T2,T3
	MOVE	T3,BITMAT+2(P1)
	ANDCM	T3,BITMAT+2(P3)
	OR	T2,T3
	MOVE	T3,BITMAT+3(P1)
	ANDCM	T3,BITMAT+3(P3)
	OR	T2,T3
	JUMPE	T2,SET2.6	; Skip out if it's not
	TXZ	P2,NEDSET	; It is, don't do this again
	MOVNI	T1,4(P1)	; We now know that we can shift at least as much
	ASH	T1,-2		; as the distance from here to the right end
	ADD	T1,PATLEN	; since no substrings matched from here to there
	MOVE	T3,P3		; Figure out where to put it with
	LSH	T3,-2		; a word table
	MOVEM	T1,DELTA2(T3)	; Put it there
SET2.6:	AOBJN	CH,SET2.3	; Loop through current index matrix
	TXZ	P2,FSTIME	; Finished the first pass
	MOVE	T4,A2		; Remember the highest index matrix element used

	TXOE	P2,NEDSET	; Do we still need to set this position?
	 JRST	[MOVE	T1,P4		; Yes, then we can shift it based on how
		MOVE	T2,P3		; far the highest level loop is from
		LSH	T2,-2		; the right end of the pattern
		SUB	T1,T2
		ADD	T1,PATLEN
		MOVEM	T1,DELTA2(T2)
		JRST	.+1]

	TXZE	P2,WNTOFF	; Did this pass go off the end of the pattern
	JRST	[MOVEI	P4,-4(P3)	; Yes, need to adjust the amount we
		LSH	P4,-2		; can shift when NEDSET is used
		JRST	.+1]		; immediately above

	SUBI	P3,4		; Now look a position to the left of last loop
	SKIPE	T4		; unless there is no need to cause no matches
	JUMPGE	P3,SET2.2	; or because we looked at all  of them

	JUMPL	P3,SET2.E	; Did we look at all of them?

	ADD	P4,PATLEN	; No, need to fill in the rest with the largest
	LSH	P3,-2		; possible number based on how far we are from
	SUB	P4,P3		; the right end of the pattern and how far the
				; setup got
	MOVEM	P4,DELTA2(P3)
	ADDI	P4,1		; Each position to the left can shift one farther
	SOJGE	P3,.-2

SET2.E:	POPJ	P,		; Return with matrix set up
	SUBTTL	Searches -- S$SEARCH -- Execute the matrix

;+
;.hl1 S$SEARCH
;This routine will do the searching.  It will determine if the slow or the
;fast algorithm is to be used.  It will then dispatch to the correct routine
;to do the searching.
;-

S$SEARCH:

SCHLOP:	TXNN	S,S.MINS	; If this time is minus search, force old search
	 SKIPN	SCHTYP		; Which one are we using?
	  JRST	SLOSCH		; The old one
; 	JRST	FSTSCH		; The new one, fall into it
	SUBTTL	Searches -- S$SEARCH -- Fast search

; This is an implementation of the algorithm of Boyer and Moore, published
; in the Communications of the ACM, October 1977, Vol. 20 Number 10, page 762.
; This article serves as the primary documentation for this routine (and the
; DELTA? table setup routines).

; This is the actual search, which uses the numbers in DELTA0, DELTA1, and
; DELTA2 for determining where in the searched string to look.  The actual
; character comparisons are done in the tried and true TECO way, with TECO's
; original bit map (there can't be a better way).

; AC usage (ditto)
; A1	Updates first arg for nSFOO$$ search
; A2	A negative index into the byte pointer table
;
; P1	The length of the string to be searched
; P2	The base register into the byte pointer table, including 
; P3	The current character address within the buffer.
; P4	Contains the bit mask for the last pattern position
;
; T1	During the slow loop, shifts the bit mask through the pattern
; T2	The word address of the first byte of the portion of the searched
; 	 string currently being examined
; T3	The value of A2 (negative character index) at the start of the current
;	FAST and SLOW loop execution
; T4	During the slow loop, counts down through the pattern

; The bytes in the searched string are obtained through a window by a table of
; constant byte pointers indexed into by P2, T2, and A2.

FSTSCH:	MOVN	T1,PATLEN	; Generate the bit mask for the rightmost
	MOVMM	T1,CSRLEN	; Save the current length in case of success
	MOVX	P4,1B0		; pattern position
	LSH	P4,1(T1)
	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the address of the buffer
	LOAD.	P3,BLKPT,(T1)	; Get the character address of PT

	MOVE	P1,UPPERB	; Figure out how many characters are to be searched
	SUB	P1,P3		; i.e. the length of the searched string

;[1012]; Fix calculation of "searched string" length to be correct. This
;[1012];avoids having the final character of the search string match the
;[1012];character after the end of the string.
;[1012];	ADDI	P1,1

	TXNE	F,F.ARG		; Is this an nSFOO$$?
	JUMPLE	A1,SCHFND	; Yes, done if we've found that many

	MOVX	P2,<0(A2)>	; Start the byte pointer base at 0(C)
FSTS.1:	MOVE	T2,P3		; Convert I into a word and byte address
	IDIVI	T2,5
	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the address of the current text buffer
	ADDI	T2,.BKTLN(T1)	; Update it

	MOVE	T1,P1		; Figure the current byte pointer window length
	CAILE	T1,SCHBPL	; the length of string left
	MOVEI	T1,SCHBPL	; or the window size, whichever is less

				; Fall through to next page...
	MOVN	A2,T1		; Copy that as negative index into table
	ADD	T1,T3		; Add the byte in word offset
	ADDI	T1,SCHBPT-1	; plus the address of the start of the table
	HRR	P2,T1		; equals the base address to be negatively
				; indexed from

	ADD	A2,PATLEN	; This search starts at the right end of the pattern
	JUMPG	A2,SCHNFD	; If that is to the right of the last character
				; of the string, then we didn't find it

	MOVE	T3,A2		; Save C at the start of the loop, so we can
				; tell how many characters we've skipped

; FAST:				; The fast loop in the ACM article
FSTS.3:	LDB	CH,@P2		; Get a character from the searched string
	ADD	A2,DELTA0(CH)	; Shift down based on its existance in the pattern
	JUMPLE	A2,FSTS.3	; and loop unless it is in the rightmost position
				; i.e. if we just had a match (see LARGE)

	TLON	A2,-1		; Did it match (SLARGE makes the left half 7)
	JRST	FSTS.5		; No, we used up our window (left half is zero)

	MOVE	T1,P4		; Start at right end of pattern bit mask
	MOVE	T4,PATLEN	; for that many characters

; SLOW:				; SLOW loop in ACM article
FSTS.4:	SOJLE	T4,FSTS.6	; If we run out of pattern characters, it matched
	LSH	T1,1		; Set bit mask to previous pattern position
	LDB	CH,@P2		; Get the next searched string character
	TDNE	T1,SMATRX(CH)	; Does it match?
	SOJA	A2,FSTS.4	; Yes, back up byte pointer index one and loop

	MOVE	T1,DELTA0(CH)	; No, figure which table shift us the most
	TLNN	T1,-1		; If we just got SLARGE, use DELTA2 always
	CAMGE	T1,DELTA2-1(T4)
	MOVE	T1,DELTA2-1(T4)
	ADD	A2,T1		; Update our current position by that much
	JUMPL	A2,FSTS.3	; and go back to FAST unless we exceeded window
; Search failed in this window, see what to do

FSTS.5:	JUMPLE	P1,SCHNFD	; Not found if there is no searched string left

	SUB	A2,T3		; See how many characters we skipped
	ADDI	P3,0(A2)	; Update current position by that much
	SUBI	P1,0(A2)	; and amount left by that much
	JRST	FSTS.1		; and try again

; Here when string found, decide where the right end of the pattern is

FSTS.6:	SUB	A2,T3		; How much we moved
	ADD	P3,A2		; Adjust pointer by that much
	ADD	P3,PATLEN	; but we scanned back by that much too
	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the address of the text buffer
	STOR.	P3,BLKPT,(T1)	; Store the new address
	ADD	P3,PATLEN	; Now get to right end of pattern
	JRST	SCHFND		; We did it

; Build the fixed byte pointer table.  The following code is done again
; under an XLIST

SCHBPL==^D200			; Length of window of byte pointers
$A==0				; Start the base address at zero

; SCHBPT:REPEAT	SCHBPL/5+1,<	; Build 5 for each word of bytes, plus extra for
; 				; the fact that the first byte may be one of 5
; 	$M==177B6		; A mask for the current character of the word
; 		REPEAT	5,<		; For each word of bytes
; 			POINTR	$A(T2),$M	; Build 5 pointers
; 		$M==$M_-7			; Moving mask each time
; 		>
; 	$A==$A+1		; To next word
; 	>
	XLIST
SCHBPT:
IF1,<
	BLOCK	SCHBPL+5
>
IF2,<
	REPEAT	SCHBPL/5+1,<
$M==177B6
	REPEAT	5,<
	POINTR	$A(T2),$M
$M==$M_-7
>
$A==$A+1
>
>
	LIST
	SUBTTL	Searches -- S$SEARCH -- Slow algorithm

;+
;.HL1 SLOSCH
;This routine will do the slow search through the text buffer.  It does the
;search as a character by character match.
;-

SLOSCH:	LOAD.	P2,TPTADR,+$QRTPT+TXTBUF ; Get the address of the text buffer
	MOVN	T1,PATLEN	; Figure old end of search comparator
	MOVX	P4,1B0		; Get a bit to shift (1st character)
	LSH	P4,0(T1)	; Which is bit one past end of pattern
	LOAD.	P3,BLKPT,(P2)	; Get the character address of PT
S1:	TXNE	F,F.ARG		; Is there an argument ?
	 JUMPLE	A1,SCHFND	; Yes - Seen string N times yet ?
	MOVE	T2,P3		; No - Form the byte pointer which will be used
	SUBI	T2,1		; It will be incremented before use
	IDIVI	T2,5		; Determine the byte offset
	ADDI	T2,.BKTLN(P2)	; Add in the offset to the text
	HLL	T2,BTAB(T3)	; Fill in the rest of the byte pointer
	MOVX	P1,1B0		; Start seeking match for first character
	MOVE	T3,T2		; Set the dynamic pointer to the static
	CAML	P3,UPPERB	; Any room left to search?
	  JRST	SLOS.X		; No, all done (unless backwards)
	JUMPG	P3,S4A		; At the beginning of the buffer ?
	SKIPL	SMATRX+BEGPAG	; And 1st search character
				;  is the beginning of buffer character ?
	 JRST	S4A		; No
	MOVX	P1,1B1		; Yes - Start the search at the second character
	MOVE	T3,T2		; Set the dynamic pointer to be the static
	SETOM	BCOUNT		; Flag first is the beginning of the page
	JRST	S4B		; Enter the search loop

S3:	MOVX	P1,1B0		; Start seeking match for first character
	MOVE	T3,T2		; Set the dynamic pointer to the static
	JRST	S4A

S4:	TDNE	P1,SMATRX+SPCTAB ; IS SPACE/TAB STRING BIT SET?
	 JRST	SPTB		; YES
S4E:	CAML	P3,UPPERB	; DON'T ALLOW I OUTSIDE BOUNDS
	 JRST	S4D		; ...
	ADDI	P3,1		; LOOK AT NEXT LOC, XCEPT 1ST TIME THRU
S4C:	LSH	P1,-1		; ADVANCE TO NEXT CHAR POSITION
S4B:	CAMN	P1,P4		; END OF SEARCH TABLE?
	 JRST	SCHFND		; YES.

S4A:	ILDB	CH,T3		; NO, GET NEXT CHAR
	TDNE	P1,SMATRX(CH)	; IS IT A MATCH?
	 JRST	S4		; YES, GO TO NEXT TABLE ENTRY.
S4D:	AOSN	BCOUNT		; IF WE FAILED WITH BEGPAG
	 JRST	S3		; THEN TRY AGAIN WITH 1ST CHAR
	TXNE	S,S.MINS	; BACKWARDS SEARCH
	JRST	SR4A
	CAML	P3,UPPERB	; TOO FAR?
	  JRST	SLOS.Z		; Check the boundary condition
	INCR.	P3,BLKPT,(P2)	; Increment the pointer
	IBP	T2		; Move the static byte pointer
	JRST	S3		; And keep looking

SR4A:	DECR.	P3,BLKPT,(P2)	; Decrement the pointer
	CAMGE	P3,LOWERB	; Done ?
	  JRST	SLOS.Z		; Check the boundary condition
	ADD	T2,[7B5]	; Decrement the byte pointer
	JUMPGE	T2,S3		; Jump it needs no fix it up
	HRLI	T2,(POINT 7,,34); Fix it up
	SOJA	T2,S3		; Decrement the address and go
; Skip over a string of spaces and/or TABs while searching

SPTB:	CAIE	CH," "			; But was the character we matched a
	 CAXN	CH,.CHTAB		;  space or a TAB?
	  JRST	SPTB.1			; Yes, then accept more
	JRST	S4E			; No, look at next pattern position
SPTB.1:	ADDI	P3,1			; Advance to next buffer location
	CAML	P3,UPPERB		; End of buffer?
	 JRST	S4C			; Yes, no more then
	MOVE	T4,T3			; Save current byte pointer
	ILDB	CH,T3			; Look at next character
	CAIE	CH," "			; Is it a space?
	 CAXN	CH,.CHTAB		; or a TAB?
	  JRST	SPTB.1			; Yes, keep on trucking
	MOVE	T3,T4			; No, End of string, restore
					;  the pointer to last space or tab
	JRST	S4C			; and continue search

; Here to check to see if we have a minus search and are starting at the
; end of the buffer.  It we are then we just check to see if the end of
; page is a valid match and continue backwards.

SLOS.X:	TXNN	S,S.MINS		; Minus search?
	  JRST	SLOS.Z			; No, try for matching Z in foward
	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the address of the text buffer
	TDNN	P1,SMATRX+ENDPAG	; End of page allowed?
	  JRST	SR4A			; No, continue on
	CFME.	,BLKEND,(T1),P3		; Yes, but only if we are at Z
	  JRST	SR4A			; No, continue on
	JRST	SCHFND			; Found


; Here to do a boundary check for the end of the buffer and the end
;being allowed.

SLOS.Z:	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the address of the text buffer
	TDNN	P1,SMATRX+ENDPAG	; End of page good match for a match here ?
	  JRST	SCHNFD			; It is not, just fail
	CFME.	,BLKEND,(T1),P3		; Yes, but only if we're at Z
	 JRST	SCHNFD			; Not found, give up
	FALL	SCHFND			; Found it, do end of search bookkeeping
	SUBTTL	Searches -- S$SEARCH -- Found string and loop

SCHFND:	SETOM	SFINDF			; Flag we found at least one
	LOAD.	P1,TPTADR,+$QRTPT+TXTBUF ; Get the address of the current text buffer
	LOAD.	P4,BLKPT,(P1)		; Get the address of PT
	SUBM	P3,P4			; Determine the length
	MOVE	P2,P3			; Save the current pointer
	TXNE	S,S.MINS		; Is this a minus search ?
	 DECR.	P3,BLKPT,(P1)		; Decrement PT
	STOR.	P3,BLKPT,(P1)		; Store the new PT
	SOJG	A1,SCHLOP		; Loop if more to look for
	MOVEM	P4,CSRLEN		; Save the length of the search string

	ZERO.	T1,BLKCOL,(P1)		; Clear the column
	TXNE	S,S.MINS		; Minus search?
	 TXNN	S,S.RVRS		; Yes, want to leave at start?
	  JRST	SFND.1			; Either not minus, or want to stay at end
	INCR.	,BLKPT,(P1)		; Minus search, leave pointer at start
	PJRST	.POPJ1			; And return

SFND.1:	STOR.	P2,BLKPT,(P1)		; Update it otherwise
	JRST	.POPJ1			; Just return if we found them all
	SUBTTL	Searches -- Here for the true return for a search

;+
;.hl1 S$TRETURN
;This routine is called to do the normal found routine for a search.
;It will do the auto type out processing and return the correct value
;if the search is in a loop and depending on the EO level.
;-

S$TRETURN:
	PUSHJ	P,S$AUTOTYPE		; Do the auto type out
	MOVE	T1,CSRLEN		; Get the length of the string we found
	MOVEM	T1,SRHLEN		; Save it
	TXZE	F,F.COLN		; Colon search ?
	  JRST	RTONES			; Yes - Return a minus one
	CHKEO	EODEC,S$TR.0		; If old TECO, must check for < ... >
	 POPJ	P,			; Just return
S$TR.0:	CFXE.	T1,XSBTYP,(XS),$XELOP	; In an interaction ?
	 POPJ	P,			; Just return
	JRST	RTONES			; Return a minus one
	SUBTTL	Searches -- Autotype after succesful searches
;+
;.hl1 S$AUTOTYPE
;This routine will do the auto type out after a successful search.
;-

S$AUTOTYPE:
	TXNE	F,F.COLN		; Auto type out on colon searches ?
	  POPJ	P,			; No - Just return
	CFXE.	T1,XSBTYP,(XS),$XELOP	; In an interaction ?
	 SKIPN	AUTOF			; Auto type out wanted ?
	  POPJ	P,			; No - Return
	TXO	F,F.ARG			; Flag we have an argument
	MOVX	A1,0			; It is a zero
	PUSHJ	P,TYPE			; Go type it
	HRRZ	CH,AUTOF		; Get the flag character
	SKIPL	AUTOF			; Do we want it ?
	 PUSHJ	P,T$OCHR		; Yes - Type it
	MOVX	A1,1			; Get the 1T command
	PUSHJ	P,TYPE			; Type it
	TXZ	F,F.ARG			; Clear the flag
	POPJ	P,			; Return to the caller
	SUBTTL	Searches -- S$SEARCH -- Not found, clear the pointer

SCHNFD:	MOVE	T1,PATLEN		; Get the length of the string
	MOVMM	T1,CSRLEN		; Save the length
	LOAD.	T1,TPTADR,+$QRTPT+TXTBUF ; Get the address of the current text buffer

	ZERO.	,BLKPT,(T1)		; Make the pointer be at the begining of the buffer
	ZERO.	T2,BLKCOL,(T1)		; Clear current column
	SETZM	SFINDF			; SFINDF=0
	POPJ	P,			; Return to the caller
	SUBTTL	Searches -- S$FRETURN - Common failure return

;+
;.HL1 S$FRETURN
;This is the common failure return exit routine.  This routine will restore
;the pointer if needed.
;-

S$FRETURN:
	CHKEO	EODEC,S$FR.1	; Leave pointer at top for EO of 2 or less
	MOVE	T4,SAVEPT	; Get the old PT
	STOR.	T4,BLKPT,(T1)	; Restore it

S$FR.1:	TXZE	F,F.COLN	; Colon modified?
	 JRST	S$FR.0		; Yes, return a 0
	CFXE.	T1,XSBTYP,(XS),$XELOP ; Is the last thing stacked an iteration?
	 ERROR	E.SRH		; No, give error message
	CHKEO	EODEC,S$FR.0	; Must return value in iteration?
	TXNE	S,S.NRAD	; Null insert ?
	 TXO	F,F.DONE	; Flag done
	POPJ	P,		; Return to the caller

S$FR.0:	TXNE	S,S.NRAD	; Null insert?
	 TXO	F,F.DONE	; Yes, flag we are done
	PJRST	RETZER		; And return a zero


SRHMOD:	EXP	SRCHSW		; DEFAULT SEARCH MODE
	SUBTTL	Data segment

; The following is the impure storage for TECSRH

	$IMPURE			; Go to the IMPURE PSECT
	LOWVER(SRH,4)		; Low segment version number

; Info for ^G q-register name scanning

CTGSVE:	BLOCK	1		; Saved character
SRHTPT:	BLOCK	1		; TPT for current part of search string
SRHBLK:	BLOCK	.BKTLN+^D36	; Header for search string
SRHARG==SRHBLK+.BKTLN	; Address of text portion of buffer

; Search bounds

UPPERB:	BLOCK	1		; Upper bound
LOWERB:	BLOCK	1		; Lower bounds
SAVEPT:	BLOCK	1		; Saved value of PT

; Misc

SRHLEN:	BLOCK	1		; Length of last search string found
CSRLEN:	BLOCK	1		; Length of current search string
				; (including possible ^Es items)
SRHSTA:	BLOCK	1		; Start of the search area
SFINDF:	BLOCK	1		; Search flag
INSLEN:	BLOCK	1		; Length of the string
BCOUNT:	BLOCK	1		; Flag for checking BEGPAG in slow search
SRHCTR:	BLOCK	1		; Number of characters in search argument
				; (Must precded SRHARG)

ARGTRM:	BLOCK	1		; FS, FN 2nd argument terminator
SRHCNT:	BLOCK	1		; Search count store
PNT.P2:	BLOCK	1		; Pointer store (I)

; Search table stuff
; *** DO NOT SEPERATE ***
; VVVVVVV
SMATRX:	BLOCK	SMATLN		; The old search bit table
BITMAT:	BLOCK	BITMLN*^D36	; The new rotated search bit table
DELTA2:	BLOCK	^D36		; The table which knows what the pattern looks like
SCLRLN==.-SMATRX		; Length
; ^^^^^^^
; *** DO NOT SEPERATE ***

DELTA0:	BLOCK	SMATLN		; The tables which known shere character are in the pattern
INDMAT:	BLOCK	^D36		; A table of indicies into the pattern

PATLEN:	BLOCK	1		; Number of positions in the pattern
ROTLEN:	BLOCK	1		; Current distance from the right end
				; of the pattern
SCNEST:	BLOCK	1		; Nest level counter during searches, 0 if none
SCHTYP:	BLOCK	1		; 0 if old TECO search, -1 if new search
SCTLGA:	BLOCK	1		; 0 If pattern has no ^G, -1 if it does
	SUBTTL	End of TECSRH

	END			; End of TECSRH