OBJNAM XFORCE.LIT ; extended replacement for FORCE.LIT command
VMAJOR=2        ; Created 12-Dec-84, last modified 19-Feb-86
VMINOR=2        ; By Irv Bromberg, Medic/OS Consultants
VEDIT=13.       ; 78 Wildginger Way, Toronto, CANADA  M3H 5X1
               ; Phone (416) 586-4499

IF EQ,1

Extensions implemented --
(1) -- delete vestigial error: AND @TCB,D3 prior to TRMICP
(2) -- allow control codes to be forced using ^ as leadin
(3) -- reduce waiting time if job locked in J.TOW state
(4) -- if job locked in J.TOW state then flush output for duration of force
(5) -- if flushing output queue wait for job to reach sleep or external wait
      or terminal input wait state before restoring original terminal driver.
(6) -- don't force trailing spaces/tabs (even if followed by comment symbol)
(7) -- pause after each control code so TDV will not think multi-char sequence

WARNING -- If output could be immediately inhibited do not use KILL command
prior to FORCE as it will not finish -- use FLUSH command instead.

Application note:  When forcing control codes consider what T.STS mode the
target job's terminal is in, else the forced control codes may be ignored or
may not have their desired effect.  Often it is necessary to put forced control
codes on the next line after a monitor-level command, for example:

XFORCE JOB2
VUE FILNAM      ; no control codes on this line -- T.STS is NIL, would be lost
^[^J^J^Z^[F^M   ; this line is not forced until JOB2 reaches J.TIW state and
               ; by that time T.STS is DAT!ECS!XLT so control codes work.

ENDC

SEARCH SYS
SEARCH SYSSYM
SEARCH TRM

JCB=A0
User=A1
Buffer=A2
SavPos=A3
TCB=A5
Atemp=A6

Char=D1
JName=D3
Count=D3
Flags=D5
; 0 set for single-line force
; 1 set on error - job guarded or not attached and user is running CMD file
; 2 set if force is to self
; 3 set if output queue flushed through FLUSH.TDV

HT=9.
CR=13.
SPACE=32.

       PHDR    -1,0,PH$REE!PH$REU

       SUBW    #6,SP                   ; get some workspace
       FILNAM  @SP,XXX                 ; get job name
       MOV     @SP,JName
       ADDW    #6,SP
       MOV     JOBTBL,A3
       CLR     Flags
       JOBIDX
       CMP     JName,JOBNAM(A6)
       BNE     NxtJob
       BSET    #2,Flags                ; set = forcing to self
NxtJob: MOV     (A3)+,D7
       BEQ     NxtJob                  ; skip unallocated jobs
       CMP     D7,#-1                  ; end of job table?
       BNE     Srch
       TYPECR  <?Nonexistent job>
       CALL    ChkCMZ
       BR      Process
Srch:   MOV     D7,JCB
       CMP     JName,JOBNAM(JCB)
       BNE     NxtJob
       MOV     JOBTRM(JCB),TCB         ; get TCB
       MOV     TCB,D7                  ; test if attached
       BNE     Process
       TYPECR  <?No terminal attached to job>
       CALL    ChkCMZ
Process:BTST    #2,Flags
       BNE     ModeChk
       MOVW    JOBTYP(JCB),D7
       ANDW    #J.GRD,D7
       BEQ     ModeChk
       JOBIDX
       CMPW    JOBUSR(A6),#^H0102      ; OPR: can force to guarded job
       BEQ     ModeChk
       TYPECR  <?Guarded>
       CALL    ChkCMZ
ModeChk:BYP                             ; check if single or multi-line
       LIN
       BEQ     NxtCmd
       BSET    #0,Flags                ; set = single-line command
       BTST    #1,Flags
       JNE     Done
       BR      Force

NxtCmd: KBD     Done                    ; get next command to force
       BYP
       CMPB    @Buffer,#';             ; skip if comment
       BEQ     NxtCmd
       LIN                             ; empty line?
       BNE     Force                   ; no, force it
Done:   MOVB    #CR,Char
       TTY
       BTST    #3,Flags                ; was output flushed through FLUSH.TDV?
       BEQ     Quit                    ; no, exit
       ; wait for job to reach SLP or EXW or TIW state before restoring TDV
       ; maximum wait = 10 seconds (50 x 200 ms)
       MOVW    #50-1,Count             ; -1 for DBF loop
Loop:   SLEEP   #2000.
       CTRLC   More                    ; not done but do it anyway if ^C hit
       MOVW    JOBSTS(JCB),D7          ; test for SLP or EXW or TIW state
       ANDW    #<J.MSG!J.SLP!J.EXW!J.TIW>,D7
       DBNE    Count,Loop
       BNE     DoIt
More:   TYPECR  <?Output flush incomplete>
DoIt:   POP     T.TDV(TCB)              ; then restore original terminal driver
Quit:   EXIT

Force:  BTST    #1,Flags                ; error from command file?
       BNE     NxtCmd                  ; yes, discard until empty line
       BTST    #2,Flags                ; forcing to self?
       JNE     Move                    ; yes, know it's OK to do it
       MOV     #10.,Count              ; try 0.5 x 10 = 5 seconds
ChkECC: TST     T.ECC(TCB)              ; don't force more until echo done
       BNE     Wait                    ; wait for echo to finish
       MOVW    #J.TIW,D7               ; wait until job is waiting for
       ANDW    JOBSTS(JCB),D7          ; terminal input
       JNE     Move                    ; yes, TIW state, go do TRMICP
Wait:   CTRLC   Done
       SLEEP   #5000.                  ; wait 1/2 sec and try again
       SUB     #1,Count
       BNE     ChkECC
       MOVW    #J.TOW,D7               ; check for TOW state lockup
       ANDW    JOBSTS(JCB),D7
       BNE     DoFlush                 ; yes, output locked, flush it
       TST     T.ECC(TCB)              ; check for echo lockup
       BNE     DoFlush                 ; yes, echo locked, flush output

Busy:   TYPECR  <?Job busy>
       CALL    ChkCMZ
       JMP     Force

DoFlush:TYPECR  <%Output inhibited -- flushing output queue>
       BSET    #3,Flags                ; remember we did it
       PUSH    T.TDV(TCB)              ; save terminal driver
       LEA     Atemp,FLUSH             ; index FLUSH.TDV
       MOV     Atemp,T.TDV(TCB)        ; switch to FLUSH.TDV
       CLR     T.ECC(TCB)              ; discard echo count
       JRUN    J.TOW                   ; make locked job run & flush output
       JMP     Force                   ; go back to do output flush

NxtChr: ADD     #1,Buffer
Move:   MOVB    @Buffer,Char
       CMPB    Char,#HT                ; don't force trailing spaces/tabs
       BEQ     ChkTrl
       CMPB    Char,#SPACE
       BEQ     ChkTrl
       CMPB    Char,#'^                ; check for CTRL-code leadin
       BNE     ChkCmt
       INC     Buffer                  ; skip to next character
       MOVB    @Buffer,Char            ; get char to be CTRL'ed
       UCS                             ; normalize
       CMPB    Char,#'@                ; must be within range @ to _
       BLO     ChkCmt                  ; else pass through unchanged
       CMPB    Char,#'_
       BHI     ChkCmt
       SUBB    #'@,Char                ; convert to control-code
       BR      Inp

ChkTrl: ; space/tab found, check if it is a trailing character, discard if so
       MOV     Buffer,SavPos           ; save position in buffer
       BYP                             ; bypass blanks/tabs
       LIN                             ; end of line?
       BEQ     EndCmd                  ; yes, leave buffer pointing to end
       MOV     SavPos,Buffer           ; restore position in buffer
       BR      Inp                     ; and force the legitimate space/tab

ChkCmt: CMPB    Char,#';                ; don't force comments
       BNE     Inp
EndCmd: MOVB    #CR,Char                ; terminate command with CR
Inp:    CMPB    Char,#SPACE             ; If char is a control-code then
       BHIS    Normal                  ; pause long enough so that TDV will
       TRMICP                          ; not think it is a lead-in to a multi-
       SLEEP   #600.                   ; character sequence.
       BR      ChkLIN
Normal: TRMICP                          ; force input character
ChkLIN: LIN                             ; command line finished?
       BNE     NxtChr                  ; no, go back to move more
       BTST    #0,Flags                ; was it a single-line command?
       JEQ     NxtCmd                  ; no, go back for more
       JMP     Done

ChkCMZ: JOBIDX  User                    ; if command file in progress must
       TSTW    JOBCMZ(User)            ; discard lines until empty line
       BNE     10$
       POP                             ; discard RTN address
       JMP     Done
10$:    BSET    #1,Flags                ; signal error in command file
       RTN

; FLUSH.TDV is like PSEUDO.TDV but has NULL output bit set in terminal
; attributes word:
       WORD    [FLU],[SH ]             ; FLUSH driver name
FLUSH:  WORD    ^O300                   ; null output, local echo
       RTN                             ; No Input routine.
       RTN                             ; No OUTPUT routine.
       BR      Echo                    ; ECHO routine.
       RTN                             ; No CRT routine.
Echo:   CLR     T.ECC(TCB)              ; cancel any echo characters
       RTN

       END