 
                .title DCLCOMPLETE      tcsh style filename completion for DCL
;++
;
; The following code provides command and filename completion in DCL.
; It works by hooking the $GET RMS service and figuring out whether the
; call is a DCL command line.  Yes, it's a hack.
;
; TAB triggers both command and filename completion.  A partial command
; is looked up in the CLI tables.  (Someone might like to add a symbol
; search to get foreign commands.)  If the last element of the command
; is not the first "word", it's treated as a file, and completed.
;
; HERE BE DRAGONS!  This is supervisor mode code, with exec and kernel
; mode stuff to load it.  I don't think it does anything nasty, but when
; was the last time you saw 100% bug-free code?
;
; Compiling/linking:
;       $ MACRO DCLCOMPLETE
;       $ LINK DCLCOMPLETE
;
; Installing:
;       $ RUN DCLCOMPLETE
;
; Note: This requires CMKRNL privilege to install.  Once installed in your
; process requires no privileges, thus DCLCOMPLETE can be INSTALLed with
; CMKRNL.  Remember that installed images must be linked /NOTRACEBACK.
;
; Note too that DCLCOMPLETE installs itself into the current process; it
; does not install itself into subprocesses automatically.
;
; This code is copyright Don Stokes 1993.  Non-commercial distribution
; is allowed, but contributions to my hacking fund are always welcome.
;
; Questions, bug reports, money, bug fixes, kudos, money, offers, money
; etc to:
;               Don Stokes, Network Manager
;               Computing Services Centre
;               Victoria University of Wellington
;               New Zealand.
;
;               Phone +64 4 495-5052
;               Phax  +64 4 471-5386
;
;               Email don@zl2tnm.gen.nz (Home), don@vuw.ac.nz (Work)
;
; Note: VMS hacking is my hobby, not my job.  Don't hold VUW responsible for
; your problems.  (Don't give 'em the credit either.)  You can hold me
; responsible if it makes you feel better, but if you think I'm going to
; guarantee this crap, you've got another thing coming.... 8-)
;
; Kudos to Joe Meadows for VERB, part of which I've ripped off to do the
; command completion, and a useful tool all round.
;
; Note: 15-Jul-94 Ramon Curiel
;       I don't know if this is true with the original version, but it
;       would appear to be the case.  When using "application keypad"
;       mode - redrawing the current line (e.g completing a filename)
;       will not work correctly.  The changes I've made shouldn't affect
;       this part of the code and this appears to be interaction problem
;       with this code and the terminal driver(?)...
;       
;
; Changes:
;        2 Aug 94       Ramon M. Curiel
;                       Fix problem with qualifier completion on negation.
;
;       21 Jul 94       Ramon M. Curiel
;                       Incorporate Jerry Leichters code to use ?<tab> as
;                       the list sequence.  This is a neat hack that allows
;                       us to use non-terminating codes to change things.
;                       Make it a compile time option (Jerry Mode). Make
;                       lowercasing file names compile time option (Don mode)
;                       and lowercasing commands compile time option (Ray mode).
;
;       20 Jul 94       Ramon M. Curiel
;                       Fix problem with display when quoting a "special"
;                       char.  Do this by ^H and Space to blank quote char.
;                       Add code to parse a top-level qualifier.  Now we
;                       have three levels of completion Filenames, command,
;                       and top-level qualifiers.  Next should be parameters
;                       and then second level qualifiers the keywords...
;
;       15 Jul 94       Ramon M. Curiel
;                       Use Don's suggestion of not echoing the termination
;                       character.  We then explicitly check to see if
;                       the termination char is CR.  If it is then we do a
;                       $QIO to write a CR.  
;
;       13 Jul 94       Ramon M. Curiel
;                       Didn't like using Ctrl Chars to do lists - use
;                       "?" for what it was created for.  Need to allow
;                       users to quote this, so if we're inside quotes
;                       let it through.  Check previous char for "\" our
;                       quote character if there let it through...Allow
;                       <tab> to be quoted...
;                       
;                       Add input redirection in here.  Note: order that
;                       redirection is attempted is important. Output
;                       redirection must be last or it won't work...
;                       Restructure redirection checks...
;
;        6 Jul 94       Ramon M. Curiel
;                       Change redirection code and allow piping and
;                       multiple commands.  Piping and multiple commands
;                       combined with redirection require an "assist"
;                       insert the string "PIPE " infront of string and
;                       pass to do_get...
;
;       20 Jun 94       Ramon M. Curiel SRI
;                       Add code to allow redirection of output.  This
;                       code looks for " >> " and assigns sys$output 
;                       to a string following this...
;
;       18 Jun 94       Ramon M. Curiel SRI
;                       Make <cntrl-n> list possible matching commands...
;
;       17 Jun 94       Ramon M. Curiel SRI
;                       Didn't like the way file list wildcarded subdirs.
;                       When looking for a list see if we got a DIR END
;                       if not then the wildcard is changed and need to
;                       fixup the display.  Also make "<dir." wildcards
;                       work by changing the "<" to "["...
;
;       15-16 Jun 94    Ramon M. Curiel SRI
;                       Check for DECNet node names and make <CNTRL-N>
;                       list the filenames that match the filespec...
;
;       9/9/93/dcs      Made system service remapping code *much* more
;                       paranoid.
;
;--

                .link "SYS$SYSTEM:SYS.STB"/selective_search
                .link "SYS$SYSTEM:DCLDEF.STB"/selective_search
                .library "SYS$LIBRARY:LIB.MLB"

;
; The following definitions change certain aspects of DCLCOMPLETE
;  NOTE: when completing or get a list of Qualifiers the partial
;        qualifier or completed qualifier is ALWAYS uppercased!
;
Don             = 1                     ; lowercase filenames
Ray             = 1                     ; Don't lowercase Commands
;Jerry          = 1                     ; Change how we do list via terminator
;                                       ; off list_char or list_char complete_char



                                        ; System definitions:
                $IODEF                  ;       $QIO function codes
                $TRMDEF                 ;       Terminal function item codes
                $PRTDEF                 ;       Page protection codes
                $PSLDEF                 ;       PSL symbols (processor modes)
                $RMSDEF                 ;       RMS stuff
                $FABDEF                 ;       FAB stuff
                $NAMDEF                 ;       NAM stuff
                $XABDEF                 ;       Standard XAB stuff
                $RABDEF                 ;       RAB stuff
                $XABTRMDEF              ;       Terminal XAB stuff
                $IPLDEF                 ;       IPL definitions
                $LNMDEF                 ;       LNM definitions
                $SSDEF                  ; DEFINE SYSTEM MESSAGES
                $CLIMSGDEF              ; DEFINE ERROR/STATUS VALUES
                $STSDEF                 ; DEFINE STATUS CODE FIELDS

;
; Some constants
;
complete_char   = 9                     ; File completion character (TAB)
CR_CHAR         = 13                    ; Carriage Return
list_char       = ^A/?/                 ; File List Character (Ctrl-P)
Mult_Char       = ^A/;/                 ; Command Separator...
Pipe_char       = ^A/|/                 ; Pipe command character (|)
Qual_Char       = ^A"/"                 ; Qualifiers
Quote_char      = ^A/\/                 ; \
Redi_char       = ^A/</                 ; redirection input char
Redo_Char       = ^A/>/                 ; Redirection output char
reloc_sigval    = 12345678              ; Signature value
cr_ctrl         = ^X00010000            ; CR/LF for QIO
ReDirects       = ^A/|;></              ; Output Redirection Chars...
P_Com           = ^A/PIPE/              ; PIPE AND MULTI COMMAND COMMAND
PCOM_SZ         = 4

;
; From V4 Fiche for Keyword lookup
;
V_NEGAT = 0
M_NEGAT = 1
V_KEYWD = 1
M_KEYWD = 2
V_SYNTAX = 2
M_SYNTAX = 4
V_AMBIG = 3
M_AMBIG = 8




;
; Local variables allocated on (supervisor) stack
;
                tmp=.                   ; Define some local variables
                .=0
itmlst:                                 ; Itemlist for XABTRM
        itm_len = 0                     ;               length field
        itm_cod = 2                     ;               code field
        itm_adr = 4                     ;               address field
        itm_ret = 8                     ;               retaddress field
itmlst_mod:     .blkb 12                ;       Modifiers
itmlst_prompt:  .blkb 12                ;       Prompt
itmlst_termsk:  .blkb 12                ;       Terminator Mask
itmlst_inistr:  .blkb 12                ;       Initial String
itmlst_offset:  .blkb 12                ;       Cursor offset
itmlst_len      = .-itmlst
prompt:         .blkb 40                ; Prompt string (we fiddle with this)
;
; Flags and terminators added by RMCJ to do extended
;       terminators, but wasn't able to figure out
;       how to allow quoted strings and ? might be
;       needed - so use <CTRL-N> to list files...
; Flags is used to inform code we want a complete list...
;
Terminators:    .blkb 8                 ; include ?
  Term_Len = .-Terminators              ;get size of last block
Flags:          .long 0                 ;Use this to flag <?> or <tab>
VecTbl:         .long 0                 ;This is a copy of R8 we'll need
QualBlk:        .long 0                 ;Address of Qualifier Entity block
KeyBlk:         .long 0                 ;Future use
ParBlk:         .long 0                 ;Future use
TQBuf:          .long 0                 ;Temp Qualifer buffer
SQBuf:          .long 0                 ;Saved one used for completion
APT:            .long 0
                                        ; Some temporaries
rab_address:    .blkl                   ; Address of callers RAB
xab_address:    .blkl                   ;    "    "     "    XABTRM
itmlst_address: .blkl                   ;    "    "     "    item list
itmlst_length:  .blkl                   ; Length  "     "     "    "

fab:            .blkb FAB$K_BLN         ; FAB for file lookups
nam:            .blkb NAM$K_BLN         ; NAM for same
rsa:            .blkb 256               ; Resultant string address
esa:            .blkb 256               ; Expanded string address
scratch:        .blkb 256               ; Scratch buffer
dirlen:         .blkl 1                 ; Directory length

                local=.
                .=tmp                   ; End of local variables

;
;Local Vars for Pipe_it
;
                PTmp = .
                     . = 0
InChan:         .Word   0               ;MBX Input Chan
OutChan:        .Word   0               ;MBX Output chan
InNam:          .Blkb   64              ;InMBX Name
OutNam:         .blkb   64              ;OutMBx Name
PItmLst:        .blkb   6*12            ;roome for 6 Items...

                PLocal = .
                        . = PTmp


;
; Variables used by install code
;
pagetweak:      .blkl 2         ; Virtual address of page containing SYS$SETDDIR

vectmp:         .blkb 512       ; Temp storage while we tear the system service
                                ; vectors down and rebuild 'em.


tty:            .ascid "SYS$COMMAND:"   ; Terminal name to assign channel to









;
; Mainline code -- install completion code and revector $GET to point
; to it.
;
        .entry dclcomplete, ^M<>
;;;             movaq   -(sp),r2                ; get a Descriptor
;;;             movzwl  #8,(r2)                 ;
;;;             movaq   -(sp),4(r2)             ;Allocate input buffer
;;;             pushl   r2                      ;return length
;;;             pushl   #0                      ;don't prompt
;;;             pushl   r2                      ;one shot if there use
;;;             Calls   #3,G^Lib$Get_Foreign    ;okay...
;;;             blbc    r0,100001$
;;;             Tstw    (r2)                    ;Anything
;;;             beql    100001$                 ;nope continue
;;;             movc3   (r2),@4(r2),comp_ch     ;overwrite defaults
                
100001$:        cmpl @#SYS$GET,#^x9F170FFC      ; Check that $GET has not been
                bneq 1$                         ; revectored...
                                                ; 9f170ffc = .entry; jmp @#
                movl @#SYS$GET+4, R1            ; If so, find vectored code
                movl #SS$_ABORT, R0
                cmpl reloc_sig-reloc(R1), #reloc_sigval
                bneq 99$                        ; Check signature value
                                                ; If not ours, abandon ship NOW!
                $CMKRNL_S routin=deinstall_code ; Get rid of the old stuff.
                blbc R0, 99$
1$:             $CMKRNL_S routin=install_code   ; Install routine into P1 pool
                blbc R0, 99$

99$:            ret                             ; All done


;
; Install $GET revector routine in P1 pool, assign channel to terminal.
; Exec mode, image context.
;
;       .macro movo src,dst
;       movq    src,dst
;       movq    src+8,dst+8
;       .endm
        .entry install_code, ^m<R2,R3,R4,R5,R6>
                movo @#SYS$GET, real_get        ; Save real vector

                $ASSIGN_S chan=ttychan, devnam=tty, acmode=#PSL$C_SUPER
                blbc R0, 98$                    ; Give us a supervisor channel
                                                ; for later use

                movl #reloc_len, R1             ; R1=length of allocated block
                jsb @#EXE$ALOP1PROC             ; Get P1 pool
                blbs R0, 1$                     ; Got it?
                movl #SS$_INSFMEM, R0           ; Not enuff pool....
98$:            ret
1$:             movl R1, reloc_alloc_len        ; Save length of block
                movl R2, R6                     ; R6=address of block in P1
                movc3 #reloc_len, reloc, (R2)   ; Copy reloc into P1

;
; This is where me prepare the vectors to be written to.
; The steps are:
;       Make a copy of the page where the $GET vector lives
;       Create a kernel mode demand-zero page over the top of it
;       Copy the saved vectors back over it
;       Set protection to URKW
;       Lock it into the working set
;
; NOTE: Once $CRETVA has done its thing, several important system services
; are unreachable.  It would be bad if we run into problems before
; restoring the saved vectors into the page, and setting the page
; protection to URKW.  We really want the pages locked down fairly soon
; too.  We don't want any ASTs delivered to us here, so crank IPL up to ASTDEL.
;
; If something fails in here, go into a loop (at IPL 2!).  This way we can
; be probed with SDA (priorities willing!) while letting the rest of the
; system continue, protected from being forced into continuing with a fubared
; process.  We don't know if the unreachable system services will be called
; during rundown, for example.
;
; We don't need to do this stuff if the page is already writable.  (DEBUG
; can do this for us, for example.)
;
                probew #0, #8, @#SYS$GET
                bneq 2$

                bicl3 #^x000001FF, #SYS$GET, pagetweak
                movl pagetweak, pagetweak+4     ; Calculate page address that
                                                ; $GET lives in
                movc3 #512, @pagetweak, vectmp  ; Save page to be mapped

                SETIPL #IPL$_ASTDEL             ; Crank up IPL shields
                                                ; We don't wanna be killed now!
                $CRETVA_S inadr=pagetweak,acmode=#PSL$C_KERNEL
                blbc R0, 99$                    ; Create memory over the page
                movc3 #512, vectmp, @pagetweak  ; Restore vectors from copy
                $SETPRT_S inadr=pagetweak,acmode=#PSL$C_KERNEL,prot=#PRT$C_URKW
                blbc R0, 99$                    ; Set page protection to URKW
                $LKWSET_S inadr=pagetweak,acmode=#PSL$C_KERNEL
                blbc R0, 99$                    ; Lock the buggers down
                SETIPL #0                       ; Shields down
;
; Things are safe(ish) now.  Re-write the $GET vector.
;
2$:                                             ; Point vector at our code in P1
                movl #^x9F170FFC, @#SYS$GET     ; .entry $GET ; JMP @#
                movl R6, @#SYS$GET+4            ;                     code

                movl #SS$_NORMAL, R0            ; Success.
                ret

99$:            brb 99$                         ; Emergency stasis field:

;
; Remove $GET revector code, deassign channel to terminal
; Kernel mode, image context
;
        .entry deinstall_code, ^m<R2>
                movl @#SYS$GET+4, R2            ; Get location of code in P1

                $DASSGN_S chan=ttychan-reloc(R2)
                blbc R0, 99$

                movl reloc_alloc_len-reloc(R2), R1      ; Get length of P1 block
                movo real_get-reloc(R2), @#SYS$GET      ; Restore $GET vector
                movl R2, R0
                jsb @#EXE$DEAP1                 ; Deallocate P1 block
                movl #SS$_NORMAL, R0
99$:            ret









;
; Code & stuff relocated into P1 pool.
; This must be relocatable, and is read-only in supervisor mode (the page
; protection is UREW).
;
                .psect code_reloc, long,pic,wrt,exe,noshr
;
; Preamble, used for identification etc.
;
reloc:          brw code                        ; Entry point at start of code
                .align long
reloc_sig:      .long reloc_sigval              ; Signature value
reloc_alloc_len:.blkl                           ; Length of alocated P1 block
real_get:       .blkb 2                         ; $GET vector code: entry mask
real_get_code:  .blkb 14                        ;                   actual code
ttychan:        .blkw                           ; Channel to command terminal

Comp_ch:        .byte   complete_char
List_ch:        .byte   List_char
Quot_ch:        .byte   Quote_Char
Pipe_Ch:        .byte   Pipe_Char
Mult_Ch:        .byte   Mult_Char
Redo_Ch:        .byte   Redo_char               ;
Redi_ch:        .byte   Redi_char               ;
                .byte   0                       ;keep things aligned

;
; Read-only data
;
tchars:         .ascii " ,+(@=/"                ; Command element delimiters
tchars_term_l = .-tchars                        ; File name delimiters
                .word ^A":"                     ; Device terminators
                .word ^A"<["                    ; Directory opener
tdchars:        .word ^A">]"                    ; Directory close
tdchar_sz     = .-tdchars
                .word ^A"."                     ; Type specifier
tchars_len    = .-tchars

                                        ; Flag values
        fv_typ  = 0                     ;       Type
        fm_typ  = 1
        fv_die  = 1                     ;       Directory end
        fm_die  = 2
        fv_dib  = 2                     ;       Directory begin
        fm_dib  = 4
        fv_dev  = 3                     ;       Device
        fm_dev  = 8

compflags:      .byte   fm_dev, fm_typ!fm_die!fm_dib    ; Device
                .byte   fm_dib, fm_typ!fm_die           ; Directory start
                .byte   fm_die, fm_typ                  ; Directory end
                .byte   fm_typ, 0                       ; Type

DirWildList:    .ascii  "%*.dir"
DirWLst_Sz    = .-DirWildList                   

wildcard_d:     .ascii "%*]"            ; Bits of the wildcard spec...
wildcard_f:     .ascii "*."
wildcard_t:     .ascii "*"
wildcard_e:

wildcard_tbl:                                   ;       DirB    DirE    Typ
                .byte   wildcard_e-wildcard_f   ;
                .byte   wildcard_e-wildcard_t   ;                       X
                .byte   wildcard_e-wildcard_f   ;               X
                .byte   wildcard_e-wildcard_t   ;               X       X
                .byte   wildcard_e-wildcard_d   ;       X
                .byte   wildcard_e-wildcard_d   ;       X               X
                .byte   wildcard_e-wildcard_f   ;       X       X
                .byte   wildcard_e-wildcard_t   ;       X       X       X

bel:            .byte 7                         ; A spare BEL to beep with
Erase:          .byte   8,32                    ; ^H,Space

AOut:           .Ascii  "SYS$OUTPUT"                    ;Used for redirection
AOut_Sz       = . - AOut                                ;Need size to...
AIn:            .Ascii  "SYS$INPUT"                     ;Used for redirection
AIn_Sz        = . - AIn                                 ;Need size to...

LTbl:           .ascii  "LNM$PROCESS"
LT_SZ         = . - LTBL


RDSize        = <2*3*4>                 ;CRELNM ItmLst Size
                                                ; 2 Items * 3 Longwords/Itme * 4 Bytes/LW
                                                ;Actually a single item plus termination
                                                ;longword plus a ACMODE Byte...

                .align long
fab_proto:      $FAB fop=ppf                    ; FAB & NAM for file search
nam_proto:      $NAM ess=255, rss=255









;
; All calls to $GET come here.  *ALL*!
; Decide what to do with it.
; The $GETs we're interested in come at us in supervisor mode, with a RAB
; carrying a XABTRM, who's prompt string points to the DCL prompt.  The DCL
; prompt must not be a continuation prompt (_$).
;
; DCL plays silly buggers with its prompt.  The prompt string specified by
; SET PROMPT comes with a three character preamble; the first two characters
; may or may not be CR/LF, the third is eithe nul or '_' if it's a continuation
; prompt.
;
code:           movpsl R0                       ; Abandon ship if not supervisor
                bbc #<PSL$V_CURMOD+1>, R0, 99$
                bbs #<PSL$V_CURMOD>, R0, 99$

                movl 4(AP), R6                  ; R6=RAB address
                movl RAB$L_XAB(R6), R7          ; R7=XAB address from RAB
                beql 99$                        ; No XABs?  bye bye!

                cmpb XAB$B_COD(R7), #XAB$C_TRM  ; XABTRM?
                bneq 99$                        ; No?  bye bye!

                movzwl XAB$W_ITMLST_LEN(R7), R8
                cmpw R8, #24                    ; Two item itemlist?
                beql 1$                         ; Yes, OK
                cmpw R8, #48                    ; Four item itemlist?
                bneq 99$                        ; No?  exit
1$:             movl XAB$L_ITMLST(R7), R9       ; R9=address of itemlist

                moval @#CTL$AG_CLIDATA, R1      ; locate start of prompt
                movl PPD$L_PRC(R1), R1
                movab PRC_G_PROMPT-3(R1), R1

                cmpl R1, 16(R9)                 ; Does XAB's prompt point there?
                bneq 99$                        ; No?  bye bye!

                tstb 2(R1)                      ; 0 in continuation field?
                beql dcl_input                  ; Y: we've got you!

99$:            brw real_get_code               ; Bounce off to the real $GET









;
; Come here when we've got a real live DCL input
;
; On entry:     R6 = RAB address
;               R7 = XABTRM address
;               R8 = XABTRM itemlist length
;               R9 = XABTRM itemlist address
;
; Build a new XABTRM item list
;

dcl_input:      subl2 #local, SP                ; Allocate some space on the
                movl SP, R11                    ; stack.  R11=base pointer

                movc5 #0, dcl_input, #0, #itmlst_len, itmlst(R11)
                movw #TRM$_MODIFIERS, itmlst_mod+itm_cod(R11)
                movl #TRM$M_TM_NORECALL!TRM$M_TM_ESCAPE!TRM$M_TM_TRMNOECHO,-
                      itmlst_mod+itm_adr(R11)
                                                ; Clear out the new itemlist

                movc3 12(R9), @16(R9), prompt(R11)
                movw #TRM$_PROMPT, itmlst_prompt+itm_cod(R11)
                movw 12(R9), itmlst_prompt+itm_len(R11)
                movab prompt(R11), itmlst_prompt+itm_adr(R11)
                                                ; Copy the prompt to it

                movw #Term_Len,-
                         Itmlst_termsk+Itm_Len(r11);ADDed by RMCJ
                movc5 #0, dcl_Input,#0,-
                         #Term_Len, terminators(r11)    ;Added by RMCJ

                movw #TRM$_TERM, itmlst_termsk+itm_cod(R11)
                     ;  _^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@
                movab Terminators(r11),-
                        itmlst_termsk+itm_adr(R11)

                movl #^b00000100000000000010000000000100,-
                        Terminators(r11)
                bbss    #complete_char,Terminators(r11),10001$
10001$:
        .If Not_Defined Jerry
                bbss    #List_char,Terminators(r11),10002$      
10002$:
        .endc
                                                ; Put in the terminator mask

                movw #12*3, XAB$W_ITMLST_LEN(R7); Set the length of the itmlst
                cmpw R8, #24                    ; Is this a long (48 byte) or
                beql 1$                         ; short (24 byte) form itemlist?

                movc3 #24, 24(R9), itmlst_inistr(R11)
                movw #12*5, XAB$W_ITMLST_LEN(R7); append the rest of the long
                                                ; itmlst to the new one
1$:

                movab itmlst(R11), XAB$L_ITMLST(R7)

                movl R6, rab_address(R11)       ; Save some stuff for later
                movl R7, xab_address(R11)
                movl R8, itmlst_length(R11)
                movl R9, itmlst_address(R11)

;
; Perform the $GET with the supplied RAB, decide whether we need to
; complete the character
;
do_get:         callg (AP), real_get            ; Do it
                blbc R0, 99$

                movl rab_address(R11), R6       ; Get RAB location
                bbc #DEV$V_TRM, RAB$L_CTX(R6), 99$ ; If this was not a
                                                ; terminal read, leave now.

                clrl    Flags(r11)              ;Added by RMCJ <?> or <tab>
                cmpb RAB$W_STV0(R6), #complete_char
                beql check_list                 ; Check termination character

        .if not_defined JERRY
                cmpb RAB$W_STV0(R6), #List_char ;is this a "?" RMCJ
                bneq 99$                        ;Nope Skip stuff
                movl #1,flags(r11)              ;Signal we want list
                brb  check_List                 ;see if we're quoted...
        .EndC


99$:
                cmpb RAB$W_STV0(R6), #CR_char   ;is this a CR RMCJ
                BNEQ    100$
                $QIOW_S chan=ttychan, func=#IO$_WRITEVBLK, -
                        p1=RAB$W_STV0(R6), p2=#0, p4=#^X008D0000        ; DO CR
                
100$:           Calls   #0,Check_ReDirect       ;

                movl xab_address(R11), R7       ; Going out, restore the things
                movl itmlst_length(R11), XAB$W_ITMLST_LEN(R7) ; we diddled with
                movl itmlst_address(R11), XAB$L_ITMLST(R7)

                ret

;
; Code from here to comments before complete_file added by RMCJ
; 13-Jul-94
;We're using ? to list characters, this could be a valid character in
;some instances - DCL may want it some where - in those cases it needs
;to be quoted.  Also, if the preceding char is "\" also let it through,
;but replace the "\"...

Check_list:
        movl    rab$l_rbf(r6),r8                ;address of buffer
        movzwl  rab$W_RSZ(r6),r9
        bneq    10$
1$:     brw     Error                           ;Empty line
10$:    clrq    r2                              ;use this to count "'s
20$:    cmpb    (r8)+,#^A/"/
        bneq    30$
        incl    r2
30$:    sobgtr  r9,20$                          ;tight loop  till done
        cmpb    #Quote_char,-1(r8)              ;check last char
        .if     not_defined Jerry
                bneq    33$                     ;this is ray mode
        .endc
        .if     defined Jerry
                beql    32$
                cmpb    #List_char,-1(r8)               ;check for list char here
                bneq    33$
                movl    #1,Flags(r11)                   ;set list mode
                decw    rab$W_rsz(r6)                   ;remove last char
                brb     Complete_File                   ;go complete the file
        32$:
                .endc

        decw    rab$W_rsz(r6)                   ;remove last char
        decl    r8                              ;adjust pointer
        brb     35$
33$:
        blbc    r2,complete_file                ;not quoted all done get list
        cmpw    rab$w_rsz(r6),-
                rab$w_usz(r6)
        bgeq    1$                              ;beep no room
        
35$:
        cmpl    rab$l_ubf(r6),rab$l_rbf(r6)     ;Same Buffer
        beql    37$                             ;yes skip
        movc3   Rab$W_RSZ(r6),-                 ;
                @Rab$L_Rbf(r6),-
                @Rab$L_UBf(r6)                  ;copy to input buffer           
        movl    r3,r8                           ;save pointer here
        movl    rab$l_ubf(r6),rab$l_rbf(r6)     ;
37$:    movb    Rab$W_Stv0(r6),(r8)             ;set this
39$:    incw    rab$w_rsz(r6)                   ;add it to the end

        brw     error1                          ;this isn't a real error...


;
; Come here when the user hits the TAB key
;
complete_file:  movc3 #FAB$K_BLN, fab_proto, fab(R11)   ; Fill FAB & NAM
                movc3 #NAM$K_BLN, nam_proto, nam(R11)
                movab nam(R11), fab+FAB$L_NAM(R11)      ; Fixup pointers
                movab esa(R11), nam+NAM$L_ESA(R11)
                movab rsa(R11), nam+NAM$L_RSA(R11)

                movl RAB$L_RBF(R6), R8          ; R8=address of text
                movzwl RAB$W_RSZ(R6), R9        ; R9=length of text to search
                bneq 22$
                brw error                       ; Empty cmd line? Beep!
22$:
;
; This is a bit tricky.
; R10 contains a bit mask of the filespec components we have seen as we
; run backwards down the length of the partial filename.
; R7 contains a mask of bits _not_ to be set.  Mainly, this is so that
; once we've started into a directory (ie crossed a ']' or '>') we don't
; signal that we've found the delimiter between the name & type.
;
                clrl R10                        ; R10=Filespec components flags
                clrl R7                         ; R7=Mask
                clrl R4                         ; R4=Directory end location
                movl r8,r1                      ; need an address in r1
                movl R9, R2                     ; Search backward through text
1$:             decl R2                         ; for beginning of filespec
                blss 2$                         ; R2=index
                locc (R8)[R2], #tchars_len, tchars
                beql 1$
                subl3 R0, #tchars_len, R0
                subl2 #tchars_term_l, R0
                blss 2$
;Note this stuff is added by RMCJ < with out trailing > don't work
;change < to [ 
                cmpb (r1),#^A/</
                bneq 1001$
                movb #^A/[/,(r8)[r2]            ;Replace < with [
1001$:          bicl #1, R0
                movab compflags, R3
                movb (R3)[R0], R1               ; Get bits to set
                bicb R7, R1                     ; Mask off bits already in use
                bisb R1, R10
                bisb 1(R3)[R0], R7
                cmpb R1, #fm_die                ; Dir end char?
                bneq 1$                         ; No, loop
                movb #^A/]/,(r8)[r2]            ; Make die "]"
                movb R2, R4                     ; Yes, note its location
                incl R4
                brb 1$

                                                ; Come here when we've hit
2$:             incl R2                         ; a delimiter or BOL.
                bneq 21$                        ; If BOL then go and complete
201$:           brw  complete_cmd               ; the command.



;
; We're completing the filespec.
; We now need to decide what sort of wildcard spec to feed to $SEARCH.
; This is determined by taking the bits we set above and using the wildcard_x
; table to decide how much of the wildcard extension we need.
;
21$:
                cmpb -1(r8)[r2],#Qual_Char              ; Qualifier
                bneq 2100$
                BRW  Complete_Qual
2100$:          addl2 R2, R8                    ; R8=address of filespec
                subl2 R2, R9                    ; R9=length  "     "
                subl3 R2, R4, dirlen(R11)

                movc3 R9, (R8), scratch(R11)    ; Copy filename to scratch buf
                movab scratch(R11), R2          ; R2=address of scratch buffer

                bicl3 #fm_dev, R10, R0
;
; code from here to 23$ added to handle directory list
;
                blbc    flags(r11),23$          
                bbc     #fv_dib,r0,23$          ;if didn't see start of dir skip
                bbs     #fv_die,r0,23$          ;saw end of dir then skip
                movab   DirWildList,r6
                movzbl  #<DirWLst_Sz>,-
                        r7
                movl    r9,r2
2301$:          decl    r2
                blss    23$                     ;didn't find it skip to normal...
                cmpb    scratch(r11)[r2],#^a/./ ;end of dir
                bneq    2301$                   ;keep looking till we find
                bbss    #FV_Die,flags(r11),2303$
2303$:          
                movb    #^a/]/,scratch(r11)[r2] ;assume "["
                brb     24$
23$:            movab wildcard_tbl, R7
                movzbl (R7)[R0], R7             ; R7=length of wildcard portion
                movab wildcard_e, R6
                subl2 R7, R6                    ; R6=address of wildcard portion
24$:            movc3 R7, (R6), scratch(R11)[R9]
                addl3 R7, R9, R3                ; R3=length of wildcard spec

;
; Set up the FAB & NAM, parse & search.
;
                movab fab(R11), R6              ; R6=address of FAB
                movab nam(R11), R7              ; R7=address of NAM

                movab scratch(R11), FAB$L_FNA(R6)
                movb R3, FAB$B_FNS(R6)          ; Set up search file nam in FAB

                $PARSE (R6)                     ; Parse
                blbs R0, 9$
8$:             brw error                       ; Not found or error, beep.

9$:             $SEARCH (R6)                    ; and search
;
; Code from here to 10$ added by RMCJ to get list of possible
;       filenames.  Don't know if this might cause errors later
;       on since we don't go through and clean up later, but errors
;       may not need to do this....
;

                blbs    Flags(r11),900$         ; get a list? no then complete
                brw     999$
900$:
                movl    Nam$L_Rsa(r7),r3
                blbs    r0,901$                 ;check status move line if all done
                clrl    r2
                brb     909$
        
901$:           movzbl  Nam$B_RSL(r7),r2        
                bbc     #fv_die,flags(r11),909$
                movc3   r2,(r3),Scratch(r11)    ;Save this cuz we goin to muck it
                movzbl  Nam$B_RSL(r7),r2        
                movab   scratch(r11),r3
                movl    r2,r1                   ;
903$:           decl    r1
                bleq    909$                    ;alldone here
                cmpb    (r3)[r1],#^a/./         ;version ?
                bneq    905$
                addl3   #1,r1,r2                ;save for later
                movb    #^a/]/,(r3)[r1]         ;make it a dir
                brb     903$
905$:           cmpb    (r3)[r1],#^a/]/         ;
                bneq    903$
                movb    #^a/./,(r3)[r1]         ;make it a dir

909$:
                $QIOW_S chan=ttychan, func=#IO$_WRITEVBLK, -
                        p1=(r3), p2=r2, p4=#CR_CTRL        ; show filespec
                TstL    r2
                bneq    9$
                brw     error

;
; Decide what to do with the result.
; If there was a start '[' (or '<'), but no closing bracket, we need to
; fill out the directory spec -- do elsewhere.
; We then work out the amount of stuff that needs to be taken from the
; original line, and the parts of the searched spec we need to add.
;
999$:           blbs r0,10$
                brw     error
10$:
                clrl R6                         ; R6=preamble length
                bbs #fv_die, R10, 3$            ; if ']' found, process
                bbs #fv_dib, R10, directory     ; if '[' found -- must be dir
                bbc #fv_dev, R10, 11$           ; Strip device name off if
                locc #^A":", R9, (R8)           ; device found but no dir
;
; code from here to 1002$ added by RMCJ to deal with DECNET...
;
                tstl r0
                beql 1002$                      ;end of string? skip
                cmpb 1(R1),#^A/:/               ; is this a node?
                bneq 1002$                      ; no then device
                decl r0
                beql 1002$                      ;End of String?
                movl    r0,r2                   ;save incase no dev found
                decl r0
                addl #2,r1
                locc #^A":", R0, (R1)           ; device found but no dir
                bneq    1002$                   ; found okay
                movl    r2,r0                   ; restore dev not found
1002$:          subl3 R0, R9, R6
                incl R6
                brb 4$
3$:             movl dirlen(R11), R6            ; Come here if dir found
4$:             movc3 R6, (R8), scratch(R11)    ; Copy dir/dev to filespec
11$:            movzbl NAM$B_NAME(R7), R9       ; R9=filename lenght
                bbc #fv_typ, R10, 5$            ; Add type if a '.' found.
                addb NAM$B_TYPE(R7), R9

5$:             movc3 R9, @NAM$L_NAME(R7), scratch(R11)[R6]
                addl2 R6, R9                    ; Copy the name to the end
                                                ; of the spec
                brw done                        ; and off to common code

;
; Come here if a directory was found.
; This is a bit yucky...
;
directory:      movl R9, R6                     ; R6 = counter
1$:             decl R6                         ; BOL?
                bleq 2$                         ; Y: done
                cmpb (R8)[R6], #^A"<"           ; Looking for first directory
                beql 2$                         ; delimiter...
                cmpb (R8)[R6], #^A"["
                beql 2$
                cmpb (R8)[R6], #^A"."
                bneq 1$
2$:             incl R6                         ; R6=beginning of last dir
                                                ;    name
                movl NAM$L_DIR(R7), R5
                movzbl NAM$B_DIR(R7), R4
                movl R4, R0

3$:             decl R0                         ; Find a '.' in found directory
                bleq 4$                         ; spec, or beginning of dir
                cmpb (R5)[R0], #^A"."
                bneq 3$

4$:             incl R0                         ; R0=start of dir spec
                subl R0, R4                     ; R4=length of last dir name
                addl R0, R5                     ; R5=address "  "    "   "
                decl R4
                addl3 R4, R6, R9
                movc3 R4, (R5), scratch(R11)[R6]; Copy to new filespec

;
; Come here when we have a filespec
; Lowercase the string (R9 = length)
;
done:
        .if     defined DON                     ; Don Likes Lowercased strings - Ray doesn't :-)
                movl R9, R0                     ; R0=loop counter
1$:             decl R0
                blss 2$
                cmpb scratch(R11)[R0], #^A"A"   ; A-Z?
                blss 1$
                cmpb scratch(R11)[R0], #^A"Z"
                bgtr 1$
                bisb #32, scratch(R11)[R0]      ; Yes, make it a-z.
                brb 1$
2$:
        .EndC

;
; Now call $PARSE/$SEARCH with a null filespec.  This is to avoid a nasty
; little problem with running out of PPF space.
;
3$:             movab fab(R11), R6              ; R6=FAB address
                movab scratch(R11), FAB$L_FNA(R6)
                clrb FAB$B_FNS(R6)              ; Null filespec
                $PARSE (R6)                     ; Don't care if these "succeed"
                $SEARCH (R6)                    ; or "fail"
                
;
; Insert the rest of the command line in front of our new filename.
;
10$:            movl rab_address(R11), R7       ; R7 = input RAB
                subl3 RAB$L_RBF(R7), R8, R6     ; R6 = preamble length
                movc3 R9, scratch(R11), scratch(R11)[R6] ; Move filename up
                movc3 R6, @RAB$L_RBF(R7), scratch(R11)   ; Move preamble in

                addl3 R6, R9, R8                ; R8=length of preamble+filespec

;
; This is common code for the command and filename completion stuff.
; Diddle the inistrng part of the itemlist to point to our scracth buffer.
; Resubmit the $GET.
;
fix_itmlst:     movl xab_address(R11), R7       ; Get the XAB address
                bsbw prepare_retry              ; Fiddle the itemlist readty to
                                                ; retry the $GET

                movab scratch(R11), itmlst_inistr+itm_adr(R11)
                movw R8, itmlst_inistr+itm_len(R11)
                                                ; Point the inistr at our line
                brw do_get                      ; Round we go again...









;
; Come here if we need to complete the command rather than the filespec.
; This runs through the command tables looking for a command that matches
; the typed portion.
;
; This code is ripped off from Joe Meadows' VERB, and I haven't really
; paid much attention to how it works, hence the relative lack of comments. 8-)
;
complete_cmd:
                movl @#CTL$AG_CLITABLE,R0
                addl3 R0, VEC_L_COMDPTR(R0),R1
                movzwl VEC_W_TRO_COUNT(R1), R6
                addl3 #8,R1,R3

                clrl R4

10$:            movl (R3)[R4],R5                ; comand_block TRO
                addl @#CTL$AG_CLITABLE, R5
                movzwl CMD_W_NAME(R5),R0        ; name BRO
                addl R0,R5                      ; ascic all names
                movzbl (R5),R2

20$:            incl R5                         ; Ascic verb name
                movzbl (R5), R7

                movl R9, R1                     ; R1=loop counter from cmd len
                cmpl R1,R7                      ; Bigger than cmd?  Forget it
                bgtr 8$
1$:             decl R1                         ; Case-insensitive string
                blss 99$                        ; compare (CLI table is upcase)
                bicb3 #32, (R8)[R1], R0
                cmpb R0, 1(R5)[R1]
                beql 1$

8$:             decl R2
                subb (R5),R2
                bleq 30$

                addl R7,R5
                brb 20$

30$:            aoblss R6,R4,10$                ; If this falls thru, forget
                blbc    flags(r11),35$
                clrl    r0
                bsbw    205$
35$:            brw error                       ; it.

99$:
                blbc    flags(r11),100$
                bsbw    200$
                brb     30$
100$:
                movab 1(R7), R8                 ; Come here when command found
                movb #32,scratch(R11)[R7]       ; Put a space at the end of
        .If     defined RAY
                movc3   r7,1(r5),scratch(r11)   ; Copy command
        .Endc
                                                ; the command
        .if     Not_defined Ray
2$:             decl R7                         ; Copy and lowercase the command
                blss 3$                         ; to the command buffer
                bisb3 #32, 1(R5)[R7], scratch(R11)[R7]
                brb 2$
3$:
        .endc
             brw fix_itmlst                  ; Done.

200$:
                movzbl  r7,r0
205$:
End_List:       movab   1(r5)[r7],r1
                subl2   r0,r1
                $QIOW_S chan=ttychan, func=#IO$_WRITEVBLK, -
                        p1=(r1), p2=r0, p4=#CR_CTRL        ; show filespec
                rsb








;
; Come here on error.  Ring the terminal bel, and redisplay the current
; line.  Note: Error1 redisplays using rab$L_rbf and rab$l_rsz without
; ringing the bell - we do this on a quoted ? and its not a real error.
;

Error1:
        $QIOW_S chan=ttychan, func=#IO$_WRITEVBLK!IO$M_NOFORMAT, -
                p1=Erase, p2=#2           ; Backspace,space
        BRB     Error2

error:
          $QIOW_S chan=ttychan, func=#IO$_WRITEVBLK!IO$M_NOFORMAT, -
                        p1=bel, p2=#1           ; Beep!

Error2:
                movl rab_address(R11), R6       ; Get some stuff back
                movl xab_address(R11), R7
                bsbw prepare_retry              ; Fix up the itemlist

                movl RAB$L_RBF(R6), itmlst_inistr+itm_adr(R11)
                movw RAB$W_RSZ(R6), itmlst_inistr+itm_len(R11)
                                                ; And point the inistring at
                                                ; read buffer
                brw do_get                      ; And around we go.

;
; This prepares the prompt and itemlist for a retry.
; We put CR/NULL in the carriage control portion to return to the cursor
; to the margin before reprompting -- we only make the line longer so
; clearing to EOL is not necessary.
;
prepare_retry:  cmpw XAB$W_ITMLST_LEN(R7), #12*5 ; First try?
                beql 1$                         ; No? not much to do.

                movw #TRM$_INISTRNG, itmlst_inistr+itm_cod(R11)
                movw #TRM$_INIOFFSET, itmlst_offset+itm_cod(R11)
                movw #12*5, XAB$W_ITMLST_LEN(R7)

1$:             movb #13, prompt(R11)           ; Set carr control to CR NUL
                clrb prompt+1(R11)
                rsb

;++++
; We come here when we are looking to complete or list
; potential qualifiers /<string><tab> or /<string><?>
; Need to get the command first inorder to get the
; quals that match - at this time only do toplevel
; quals...


Complete_Qual:
        addl2   R2, R8                  ; R8=address of Qual
        subl2   R2, R9                  ; R9=length  "     "
        
        movzwl  #4,r2                   ;Assume more then 4
        cmpw    Rab$W_Rsz(r6),#4        ;
        bgeq    1$                      ;only use first 4 chars
        movzwl  Rab$W_RSZ(r6),r2        ;Limit of chars to check
1$:     movl    Rab$L_Rbf(r6),r3        ;expecting things there...
        movab   Scratch(r11),r5
        clrl    r4                      ;zero index
2$:     locc    (r3)[r4],#tchars_len, tchars
        bneq    10$                     ;
        bicb3   #32,(r3)[r4],(r5)[r4]   ;move and upcase the byte
        aoblss  r2,r4,2$
10$:    bsbw    Locate_DCL              ;match DCL command
        blbc    r0,200$                 ;not found 
        movl    r9,r1                   ;
        movl    r8,r2
        movl    QualBlk(r11),r8
        BEQL    200$                    ;IF NO BLOCK! SKIP
        clrl    SQBuf(r11)              ;Zero this for later
        bsbw    Find_KeyWord            ;Find keyword
        blbs    Flags(r11),20$          ;Already have list...
;;;;    blbc    r0,200$                 ;ambiguous keword
        movl    r2,r8                   ;restore this
        movl    SQBuf(r11),r5           ;get unique match
        beql    200$
        cmpw    (r5),(r8)               ;
        beql    15$                     ;Not equal means negated
        subl2   #2,r9                   ;strip NO
        addl2   #2,r8                   ;advance past NO
15$:    movzbl  -1(r5),r4               ;crete descriptor
        beql    200$
        BRW     Move_End                ;move it and retry
20$:    clrl    r0                      ;need to zero this
        bsbw    End_List
200$:   BRW     ERROR                   ;ring bell and reprompt


Move_End:
        subl3   r9,r4,r1                ;get length to move
        bleq    100$                    ;nothing to do
        addw3   Rab$W_RSZ(r6),-
                r1,r2
        cmpw    r2,rab$w_Usz(r6)        ;how much room
        bleq    10$                     ;okay to procede
        subw3   rab$w_rsz(r6),-
                rab$w_usz(r6),r1        ;space we have to move
10$:    addw2   r1,Rab$W_Rsz(r6)
        movab   (r8)[r9],r2
        movab   (r5)[r9],r3
        movc3   r1,(r3),(r2)            ;copy it
100$:   brw     Error2


;---
;Locate qualifers for DCL command
;Input:
;       r4/r5   - DCL command to find
;output:
;       r1      - Address of QualBlock
;---
Locate_DCL:
        pushr   #^M<r2,r3,r4,r5,r6,r7,r8,r9>    ;Save scratch regs
        movl    @#CTL$AG_CLITABLE,R8
;
; FIND BASE AND SIZE OF VERB AND POINTER TABLES.
;
20$:
        ADDL3   VEC_L_COMDPTR(R8),R8,R7         ;GET BASE OF POINTER TABLE
        ADDL    #VEC_K_HEADER_LENGTH,R7         ;SKIP PAST POINTER TABLE HEADER

        ADDL3   VEC_L_VERBTBL(R8),R8,R3         ;GET BASE OF VERB TABLE
        CVTWL   VEC_W_SIZE(R3),R2               ;GET SIZE OF TABLE + HEADER
        SUBL    #VEC_K_HEADER_LENGTH,R2         ;REMOVE HEADER
        DIVL3   #4,R2,R6                        ;GET SIZE IN VERBS (4 BYTES PER VERB)
        ADDL    #VEC_K_HEADER_LENGTH,R3         ;SKIP PAST VERB TABLE HEADER

;
; SEARCH VERB TABLE FOR SPECIFIED VERB.
;
22$:    MATCHC  R4,(R5),R2,(R3)                 ;SCAN FOR VERB MATCH
        BEQL    25$                             ;BR IF VERB MATCH
        movl    #CLI$_IVVERB,r0
        brb     50$                             ;return

;
; IF MATCH WAS NOT ON LONGWORD BOUNDARY, THEN KEEP LOOKING.
;
25$:    SUBL3   R4,R3,R0                        ;GET ADDRESS OF MATCHED VERB
        BICL3   #3,R0,R1                        ;ROUND ADDR TO LONGWORD ADDR
        CMPL    R0,R1                           ;IS IT SAME AS ORIGINAL?
        BEQL    27$                             ;YES, THEN DONE
        SUBL3   #1,R4,R0                        ;START SEARCH ONE CHAR AFTER
        SUBL    R0,R3                           ; FIRST CHAR OF MATCH   
        ADDL    R0,R2                           ;  
        BRB     22$                             ;KEEP LOOKING

;
; VERB MATCH FOUND - GET ADDRESS OF CMD BLOCK.
;
27$:
        movl    r8,VecTbl(r11)                  ;Save this for later
        DIVL3   #4,R2,R9                        ;CALCULATE INVERSE COMMAND INDEX
        SUBW3   R9,R6,R9                        ;CALCULATE VERB INDEX
        SUBW    #1,R9                           ;ZERO BASE THE RESULT
        ADDL3   r8,(R7)[R9],R9                  ;GET ADDRESS OF COMMAND BLOCK



        MOVL    CMD_L_QUALS(R9),R0              ;GET OFFSET TO FIRST QUALIFIER DESC
        BEQL    40$                             ;IF EQL NONE
        ADDL    R8,R0                           ;CALCULATE ACTUAL ADDRESS
40$:    MOVL    R0,QualBlk(r11)                 ;SET ADDRESS OF QUALIFIER DESCRIPTORS
        MOVL    #SS$_NORMAL,R0                  ;IT'S OKAY NOW...
50$:    popr    #^M<r2,r3,r4,r5,r6,r7,r8,r9>    ;Save scratch regs
        RSB     

;++++
; FIND_KEYWORD - Look up a keyword in specified list
;
; This routine is mostly from the V4 fiche of parsent.mar modified
; to work here
;
; INPUTS:
;
;       R1/R2   - Descriptor of keyword to look up
;       R6      - 0 if qualifier keyword, M_KEYWD if regular keyword
;       R8      - Address of list of descriptor blocks
;       R11     - Scratch area
;
; OUTPUTS:
;
;       SQBuf   - Points Buffer of matched keyword
;               - if list mode is on outputs matching keywords to
;               - screen.
;-

FIND_KEYWORD:

        PUSHR   #^M<r2,R3,R4,R5,r6,r7,r8,R9>    ;SAVE REGISTERS
        movq    r1,r3
1$:     decl    r3
        blss    2$                              ;okay so far    
        bicb2   #32, (R4)[R3]                   ;uppper case it
        brb     1$                              ;string is done

;
; IF KEYWORD LIST, THEN SKIP PAST TYPE BLOCK.
;
2$:     CMPB    ENT_B_TYPE(R8),#BLOCK_K_TYPE    ;TYPE BLOCK?
        BNEQ    5$                              ;NO, THEN SKIP
        ADDL3   VecTbl(R11),-                   ;GET ADDR OF FIRST ENT BLOCK
                TYPE_L_KEYWORDS(R8),R8          ;

;
; SAVE ADDRESS OF LIST OF DESCRIPTOR BLOCKS.
;
5$:     MOVL    R8,R9                           ;SAVE LIST ADDRESS

;
; INITIALIZE SEARCH PARAMETERS.
;
10$:    CLRL    R7                              ;CLEAR QUALIFIER NUMBER
        CLRQ    -(SP)                           ;SET NULL AS QUALIFIER FOUND

;
; IF QUALIFIER THEN ONLY CHECK FIRST FOUR CHARACTERS OF KEYWORD,
; ELSE CHECK THE WHOLE THING.
;
        MOVL    R1,APT(r11)             ;SAVE ORIGINAL TOKEN SIZE
;
; GET NEXT QUALIFIER IN LIST
;
30$:    CVTWL   ENT_W_NAME(R8),R0       ;GET OFFSET TO ASCIC QUALIFIER NAME
        MOVAB   (R8)[R0],R5             ;FIND ADDRESS OF QUALIFIER KEY LENGTH
        MOVAB   1(R8)[R0],TQBuf(R11)    ;FIND ADDRESS OF QUALIFIER KEY TEXT and save
        MOVQ    R1,R3                   ;COPY QUALIFIER STRING DESCRIPTOR
        movzbl  (r5),r0
;
; COMPARE THE QUALIFIER WITH THE INPUT
;
;;      pushr   #^M<r0,r1>
;;      movl    TQBuf(r11),r1
;;      $QIOW_S chan=ttychan, func=#IO$_WRITEVBLK, -
;;                p1=(r1), p2=r0, p4=#CR_CTRL        ; show filespec
;;      popr    #^M<r0,r1>
        cmpB    R3,R0                      ; Bigger than cmd?  Forget it
        bgtr    70$
        movl    TQBuf(r11),r0
31$:    decl    R3                         ; Case-insensitive string
        blss    63$                        ; compare (CLI table is upcase)
        cmpb    (R0)[r3], (r4)[r3]
        bneq    70$
        brb     31$                     ;it was equal continue

;
; WHAT KIND OF MATCH DO WE HAVE?
;
63$:    bsbw    500$
        CMPB    apt(r11),(R5)           ;IS TABLE QUALIFIER SAME LENGTH
        BNEQ    65$                     ;NO, THEN SKIP
        ADDL    #8,SP                   ;YES, THEN WE HAVE A UNIQUE MATCH
        BRB     83$                     ;RESTORE STACK, BRANCH TO EXIT

;
; CHECK FOR AMBIGUITY AND SAVE MATCH.
;
65$:    TSTL    4(SP)                   ;FIND MATCH BEFORE?
        BEQL    67$                     ;BR IF NOT AMBIGUOUS
        BISL    #M_AMBIG,R6             ;SET AMBIGUOUS BIT
67$:    MOVQ    R7,(SP)                 ;SAVE MATCHED QUALIFIER VALUES

;
; CHECK NEXT QUALIFIER IN LIST.
;
70$:    INCL    R7                      ;INCREMENT QUALIFIER NUMBER
        MOVL    ENT_L_NEXT(R8),R8       ;GET OFFSET TO NEXT QUALIFIER DESCRIPTOR
        BEQL    75$                     ;IF EQL THEN DONE
        ADDL    VecTbl(R11),R8          ;CALCULATE ADDRESS
        BRW     30$                     ;CHECK NEXT QUALIFIER
 
;
; ALL QUALIFERS HAVE BEEN CHECK WITHOUT AMBIGUITY, NOW SEE IF ANY MATCHED.
;
75$:    movl    #CLI$_ABKEYW,r0         ;ASSUME AMBIGUOUS
        BBSC    #V_AMBIG,R6,85$         ;BR IF TRUE
        MOVQ    (SP)+,R7                ;RESTORE MATCHED QUALIFER PARAMETERS
        BNEQ    83$                     ;BR IF ONE WAS FOUND
 
;
; QUALIFIER DESCRIPTOR TABLE EXHAUSTED - TRY NEGATION
;
77$:    XORL    #M_NEGAT,R6             ;COMPLEMENT NEGATION FLAG
        SUBL3   #2,apt(r11),R1          ;REDUCE CHARACTER COUNT
        BLEQ    80$                     ;IF LEQ NO MATCH POSSIBLE
        MOVL    R9,R8                   ;RESTORE ADDRESS OF DESCRIPTOR LIST
        CMPW    #^A/NO/,(R2)+           ;KEYWORD START WITH 'NO'?
        BNEQ    80$                     ;IF EQL YES
;;;;    bsbw    500$
        brw     10$
 
;
; SET STATUS, CLEAN UP, AND RETURN
;
80$:    movl    #CLI$_IVQUAL,r0         ;ASSUME INVALID QUALIFIER
        BBC     #V_KEYWD,R6,90$         ;BRANCH IF QUALIFIER PROCESSING
        movl    #CLI$_IVKEYW,r0         ;SET INVALID KEYWORD STATUS
        BRB     90$                     ;EXIT WITH ERROR STATUS

83$:    movl    #SS$_Normal,r0
        BRB     90$                     ;EXIT WITH ERROR STATUS

85$:    MOVQ    (SP)+,R7                ;RESTORE MATCHED QUALIFIER PARAMETERS
90$:    bbss    #FV_DIB,Flags(r11),95$  ;if this is set we already tried no
        tstl    SQBuf(r11)              ;Did we find a match?
        beql    77$                     ;No the try stripping NO
95$:    INCL    R7                      ;ADJUST TO ACTUAL KEYWORD NUMBER
        POPR    #^M<r2,R3,R4,R5,r6,r7,r8,R9>    ;RESTORE REGISTERS
        RSB                             ;RETURN FROM SUBROUTINE


500$:
        pushr   #^m<r0,r1>
        movl    r0,SQBuf(r11)           ;Save for later
        blbc    Flags(r11),510$         ;not listing just return
        movzbl  (r5),r1
        bleq    510$
        $QIOW_S chan=ttychan, func=#IO$_WRITEVBLK, -
                p1=(r0), p2=r1, p4=#CR_CTRL        ; show filespec
510$:   popr    #^M<r0,r1>
        RSB



;+++
; check_Redirect:
;       Okay here we go with the piping and redirection routines.
;       This requires some cooperation with an outside program.
;       This routine basically checks for special characters and
;       modifies the input buffer to insert the pipe command infront
;       of the input command.  It also checks to see if this was
;       already done before (doesn't need to do it again if it did).
;       
;       If multiple command or pipe chars aren't found it will check
;       for redirection and do that if necessary here - point a user
;       mode sys$output logical name to file.  No attempt is made
;       to determine if new file or append should be used (I think
;       this can be done by using a supervisor level translation,
;       but I'm punting that for now...)
;
;   Note:
;       The order in which checking is done is important in that
;       output redirection needs to be last...
;
; Input:
;       R6      - Points to input RAB.
;       r11     - points to scratch area..
; Output:
;       r0      - SS$_Normal in most cases or CRELNM errors...
;                 RMS$_TNS on truncation if not enough room
;                 to add "PIPE "...
;
; Side Affects:
;       if Redirection in affect Sys$output points to a file (logical name
;       assigned in User Mode...Change Size Rab$W_Rsz to new Length.
;       If piping or multiple commands add PIPE to beginning and update
;       rab$w_rsz...
;
;---
        .Entry  Check_Redirect,-
                ^M<r2,r3,r4,r5,r6,r7,r8,r9,r10> ;don't muck with these
        clrl    r10
        movaq   -(Sp),r9                        ;Allocate descriptor
        movaq   -(Sp),r8                        ;allocate longword for
        subl2   #RDSize,SP
        movl    sp,r7                           ;itmlst in r5
        movl    #^a/    /,(r8)                  ;Redirect chars...
        brw     200$

50$:
        movb    #redo_char,1(r8)                ;Output Redirection
60$:
        MatchC  #3,(r8),-                       ;Check for
                Rab$W_Rsz(r6),-                 ; Redirect Chars in
                @Rab$L_Rbf(r6)                  ; Input Buffer
        bneq    90$                             ;Nope all Done
        movl    r2,r0                           ;Did we get  filename?
        beql    90$                             ;no, then all done
        addl2   #3,r0                           ;GET total length not needed
        subw2   r0,Rab$W_Rsz(r6)                ;Change length to strip end...
        movzwl  #LT_Sz,(r8)                     ;Create static descriptor
        movab   LTbl,4(r8)                      ;point to buffer
        blbs    r10,70$
        movzwl  #AOut_Sz,(r9)
        movab   AOut,4(r9)                      ;
        brb     80$
70$:    movzwl  #AIn_Sz,(r9)
        movab   AIn,4(r9)                       ;
80$:    movl    r7,r0                           ;Now Build Itmlst
        movw    r2,(r0)+                        ;Length of equivalence
        movw    #LNM$_String,(r0)+              ;Item Code
        movl    r3,(r0)+                        ;Address of buffer
        clrq    (r0)+                           ;Clear last item and terminate list
        movzbl  #PSL$C_User,(r0)                ;set user mode...
        $crelnm_s -
                TabNam=(r8),-
                LogNam=(r9),-
                AcMode=(r0),-
                ItmLst=(r7)                     ;All Done
        blbc    r0,101$                         ;If error let user know...
90$:    blbs    r10,100$                        ;all done
        movl    #^a/    /,(r8)                  ;Redirect chars...
        movb    #redi_char,1(r8)                ;Input Redirection
        incl    r10
99$:    brb     60$
100$:
        movl    #SS$_Normal,r0                  ;Assume all okay...
101$:   Ret

200$:
        movb    #Pipe_char,1(r8)                ; " > " -->> " |  "
205$:   incl    r10
        MatchC  #3,(r8),-                       ;Check for
                Rab$W_Rsz(r6),-                 ; Redirect Chars in
                @Rab$L_Rbf(r6)                  ; Input Buffer
        beql    210$                            ;Nope check for "pipe"
        blbs    r10,207$
        brw     50$
207$:   movb    #MULT_char,1(r8)                ; " > " -->> " ; "
        brb     205$    
210$:
        movl    r2,r0
        beql    100$                            ;skip if 
        movl    #P_Com,(r8)                     ;
        movl    Rab$l_rbf(r6),r1
        movl    r8,r2
        movl    #3,r3
214$:   bicb3   #32, (R1)+, R0
        cmpb    r0,(r2)+
        bneq    215$
        sobgeq  r3,214$
        brb     100$                            ;Did start with PIPE...
215$:   movl    #ss$_Normal,r7
        addw3   #<PCom_Sz+1>,rab$W_rsz(r6),r0   ;remove stuff starting from
        cmpw    r0,rab$w_usz(r6)                ;more than we have?
        bleq    230$                            ;Safe to move
        movw    rab$w_usz(r6),r0                ;use max size
        movl    #rms$_tns,r7                    ;terminator not seen
220$:
        subw    rab$W_rsz(r6),r0
        bgeq    230$                            ;should be okay to move
        addw2   r0,rab$w_rsz(r6)                ;adjust it...
230$:   movl    rab$L_Ubf(r6),r1                ;get buffer area
        movc3   rab$w_rsz(r6),-                 ;amount of data to move
                @rab$l_ubf(r6),-
                <pcom_sz+1>(r1)                 ;move it here
        movl    rab$l_ubf(r6),r1
        movl    #P_Com,(r1)+                    ;put pipe command in front
        movb    #32,(r1)                        ; of input string
        addw2   #<pcom_sz+1>,rab$W_rsz(r6)      ;add pipe to command
        movl    r7,r0
        brw     101$



;
; And that, friends, is all.
;
reloc_len = .-reloc                             ; Length of relocatable stuff

                .end dclcomplete
