;       SCCSID = @(#)ffix.asm	6.3 92/05/20

        page    ,132
        title   FFIX

; This is a TSR that allows program that invoke Int 21 subfunction 5701 with
; a file open read-only to to run. Programs that experience problems with
; Find first when the volume bit along with other bits are set in the CX
; register and an '*' is included in the file spec are allowed to run.
;
;
; The strategy is simple:
;    Hook int 21 then watch for the opens. If the file is being open read-only
;    Open it for write also.
; In the Find first case:
;    watch for int 21 function 4e. If the volume bit is set, other bits are set,
;    and there is an '*' in the file spec, do a find first without the
;    volume label. If the call succeeds then we're ok. If the call fails then
;    do another find first with the volume bit on.
;    This TSR should be unloaded as soon the program completes. Programs that
;    run up to these problems are typically installation programs and when
;    they complete, the TSR should be removed from memory.
;
;    Since we do not want people to be running this TSR out of curiosity
;    passwords are embedded in it to prevent unwarranted users from running it.
;    The passwords are "date" for the touch problem, "find" for the find first
;    problem and "both" for both problems.
;    The passwords are supplied as command line  arguments to the program.
;
;    If an unwarranted user attempts to load the program without password
;    it will not stay resident and will issue no message to the user.
;
;    History:
;
;    03/18/92  Written by Marc-Arthur Pierre-Louis
;

.xlist
.xcref
include struc30.inc
.cref
.list


      .Model large
      .stack 100h
      .code


Oldint21        DD      0       ; address where old int 21 vector is stored
Oldint2f        DD      0       ; addrees where old int 2f vector is stored
TSRID           EQU     32H     ; ID that identifies the TSR
TSRPSP          DW      0       ; Address of the Trs's Prog. Seg. Prefix
TMPPSP          DW      0
SAVESP          DW      0
SAVESS          DW      0
cr              equ     0ah
lf              equ     0dh
eol             equ     '$'
TouchFlag       db      0
FindFlag        db      0
FALSE           equ     0
TRUE            equ     1
Entry_AX        dw      ?
Entry_BX        dw      ?
IRET_addr       dd      ?


New2FISR       PROC  FAR
;
; This ISR is used to check for a resident copy of the program or to
; unload the program from memory
;
;  Entry:
;
;        AH = TSR ID
;        AL = 1 To check for resident copy of self
;                       or
;        AL = FF to remove program from memory
;
;  Exit:
;        CY = 1 (if it is our ID and al != FF or 0)
;                  or
;        AL = FF (if al = 0 at entry)
;                  or
;        TSR is removed from memory (if al = ff at entry and TSR Loaded)
;
;  If the ID in AH does not match the TSR ID then we link to  the old
;  handler.
;
NewInt2f:
        .if     <AH ne TSRID>
          jmp   cs:OldInt2f
        .endif

        .if     <AL eq -1>
          jmp   Remove
        .endif

        .if     <ZERO al>
          mov   al,-1       ; else, tell that we're up
          clc
          iret
        .endif
        stc
        iret

New2FISR       ENDP

NewInt21   Proc   far
;
; For 5701 wath for functions 3D and 6C. If bit 2 of al or bl is set then
; we're ok. Otherwise we set bit 2 and the file is opened for read and
; write operations to allow function 5701 to succeed.
;
; For 4e check if the volume bit is set along with other bits and there is
; an '*' in the file spec. If so turn off the volume bit and do a Find first.
; if it fails, then turn on the volume bit and do another find first.
;


        .select
        .when   <AH eq 3Dh>             ;open file request?
          .if   <<cs:TouchFlag> eq TRUE> and
          .if   <BIT AL nand 3>
            mov     cs:[Entry_AX],ax
            mov     cs:[Entry_BX],bx
            or      al,2
marc_open:
            pop     word ptr cs:[Iret_addr]
            pop     word ptr cs:[Iret_addr]+2
            call    cs:[OldInt21]
            .if     nc
              jmp   cs:[IRET_addr]
            .endif
            mov     ax,cs:[Entry_Ax]
            mov     bx,cs:[Entry_Bx]
            pushf
            push    word ptr cs:[IRET_addr]+2
            push    word ptr cs:[IRET_addr]
          .endif

        .when   <AH eq 6Ch>             ;extended open request?
          .if   <<cs:TouchFlag> eq TRUE> and
          .if   <BIT BL nand 3>
            mov     cs:[Entry_AX],ax
            mov     cs:[Entry_BX],bx
            or      bl,2
            jmp     marc_open
          .endif

        .when   <AH eq 4Eh>             ;extended open request?
          .if     <<cs:[FindFlag]> eq TRUE> and
          .if     <BIT CX and 8> and    ;vol label on
          .if     <BIT CX and <NOT 8>>  ;and any other bit on
            jmp     short marc_ff
          .endif

        .endselect

        jmp   cs:[oldint21]

marc_ff:
        push    cx
        push    si
        push    ax

        mov     si,dx
        mov     cx,0ffh ; assume a string of 256 bytes

        .repeat
          lodsb
          cmp     al,'*'
          jz      StartWork
          .leave  <ZERO al>
        .loop

        pop   ax
        pop   si
        pop   cx

        jmp   cs:[oldint21]

StartWork:

        pop    ax
        pop    si
        pop    cx

        and    cx,0fff7h ; mask volume bit
        pushf
        call   oldint21
        .if    c
          or     cx,8      ; set volume bit
          mov    ax,4E00h
          pushf
          call   oldint21
        .endif

        ret   2

Newint21 endp



Unloads Proc    far
Remove:
;
;   This Procedure removes the TSR from memory. It checks to see if
;   anybody has hooked int 21 or int 2f after it was loaded. If any
;   of the vectors was hooked, the TSR can not be removed.
;

        push bx
        push cx
        push dx

         ;check if safe to unlink int 2fh

        mov  ax,352fh
        pushf
        call  oldint21
        push es
        pop  ax
        mov  dx,cs
        cmp  ax,dx
        jnz  unsafe
        mov  ax,offset newint2f
        cmp  ax,bx
        jnz  unsafe

        ;check if safe to unlink int 21

        mov  ax,3521h
        pushf
        call  oldint21
        push es
        pop  ax
        mov  dx,cs
        cmp  ax,dx
        jnz  unsafe
        mov  ax,offset NewInt21
        cmp  ax,bx
        jnz  unsafe

        mov  ax,word ptr cs:OldInt2f+2
        mov  ds,ax
        mov  dx,word ptr cs:oldint2f
        mov  ax,252fH
        pushf
        call  oldint21

        mov  ax, word ptr cs:oldint21+2
        mov  ds,ax
        mov  dx, word ptr oldint21
        mov  ax,2521h
        pushf
        call  oldint21


         ; Now set the terminating address into code in the calling prog.
         ; at offset 0ah
         ; Address is stored in CX:DX

        pop  dx
        pop  cx
        cld
        mov  di,0ah
        mov  ax,cs:TSRPSP
        mov  es,ax
        mov  ax,dx
        stosw
        mov  ax,cx
        stosw


         ; Now set Parent PSP To that of calling program at offset 16h

        pop     bx
                              ; the segment of current PSP
        mov     ax,cs:TSRPSP  ;
        mov     di,16h        ; offset where parent psp is stored
        push    ax
        pop     es
        mov     ax,bx              ; Move the Parent PSP value into ax
        stosw                      ; store new parent info there

         ; Now set The TSR  PSP as the current PSP

        mov     bx,cs:tsrpsp
        mov     ah,50h
        int     21h

        mov     ax,4c00h  ; Terminate The  TSR
        int     21h
unsafe:

        pop     ax
        pop     bx
        pop     cx
        ret

Unloads endp


printmess       proc
        ;
        ;  DX=Offset Of string
        ;
        ;
        mov     ax,0900h
        int     21h
        ret

printmess  endp


ENDOFCODE:


; The data and code that follow are only used for intallation purposes
; They will be discarded after the installion.
;

UnsafeMess    db  'Unload TSR loaded after FFix first'
              db  cr,lf,eol
endstrin      db  '$'
Argument      db  20 dup (0)
LoadMess      db  7,7,7,'FFix already active...',cr,lf,eol
Install       db  'FFix Loaded.',cr,lf
              db  'Warning:   This program should not be loaded',cr,lf
              db  'unless  you have experienced  problems with  ' ,cr,lf
              db  'certain programs  and  was  told to use it by ',cr,lf
              db  'IBM''s Technical support group. It should be',cr,lf
              db  'unloaded as soon as the problems are resolved.',cr,lf
              db  'Unload with FFix /u or close the DOS session.',cr,lf
              db  7,7,7,7,7,7,eol
Not_Install   db  7,7,7,'FFix is not active, can not uninstall it',cr,lf,eol
uninstallmess db  'FFix Unloaded',cr,lf,eol
BadFlag       db  0
HelpFlag      db  0
DelFlag       db  0
UnsafeFlg     db  0
InstFlag      db  0

GetParam   Proc  ; Get  a Parameter from the command Line
;
;   This Procedure parses the command line for an argument;
;

        xor cx,cx         ; Zero-out CX
        mov cl,ds:[080h]  ; Length of Command Line
        or  cl,cl
        jz  NoCommand
        mov si,080h
        mov di,offset es:Argument
        mov dx,0


L1:
        inc si
        mov al,ds:[si]
        cmp al,020h
        jz  L1
        cmp al,09h
        jz  L1
        cmp al,0dh
        jne L2

NoCommand:

        mov  ax,0   ;tell that no argument was found
        push cs
        pop  ds
        ret


L2:
        movsb          ; copy byte for ds:si
        dec cx         ; to es:di
        jz  done       ; are we at the end Yet?
        inc dx
        mov al,ds:[si]
        cmp al,020h    ; If a Blank then we're done
        jz  done
        cmp al,09h     ; If a Tab then we're done
        jz  done
        cmp al,0dh     ; If a Carriage Return we're done
        jz  done
        jmp l2         ; No, then get one more byte

done:

        mov  ax,1      ; Tell that we have found an argument
        push cs        ;
        pop  ds        ; Get our real Data segment
        ret


GetParam   endp


Parse_CommandL Proc

        jmp     short lab1
Delete:
        mov     DelFlag,1
        jmp     short return
lab1:
        mov     si,offset Argument
        lodsb
        cmp     al,'/';
        jz      nextbytes
        cmp     al,'-' ;
        jnz     inval1
nextbytes:
        lodsb
        cmp     al,'U'
        jz      delete
        cmp     al,'u'
        jz      delete
        cmp     al,'f'
        jnz     Touchit
        lodsb
        cmp     al,'i'
        jnz     Inval1
        lodsb
        cmp     al,'n'
        jnz     inval1
        lodsb
        cmp     al,'d'
        jnz     inval1
        mov     FindFlag,1
        jmp     short Inst

Inval1: jmp     short InvalidArg

Touchit:
        cmp     al,'d'
        jnz     both
        lodsb
        cmp     al,'a'
        jnz     invalidArg
        lodsb
        cmp     al,'t'
        jnz     invalidArg
        lodsb
        cmp     al,'e'
        jnz     invalidArg
        mov     TouchFlag,1
        jmp     short inst
both:
        cmp     al,'b'
        jnz     invalidarg
        lodsb
        cmp     al,'o'
        jnz     invalidArg
        lodsb
        cmp     al,'t'
        jnz     invalidArg
        lodsb
        cmp     al,'h'
        jnz     invalidArg
        mov     Touchflag,1
        mov     FindFlag,1

Inst:
        mov     InstFlag,1
        jmp     short return

InvalidArg:
return:
        ret

Parse_CommandL   endp

GET_PSP PROC

        push   ax
        push   bx
        mov    ah,62h       ; Get PSP
        int    21h
        mov    ax,bx        ; bx contains segment of PSP
        push   cs
        pop    es
        stosw
        pop    bx
        pop    ax
        ret

GET_PSP ENDP

main    proc    far

        push    cs
        pop     es ; make ES our data and code segment
        call    GetParam ; Get a Parameter from cmd line
        or      ax,ax
        jz      LowProfile ;no parameter from command line
                           ; EXit without Saying a word
        call    Parse_CommandL

        .if     <[HelpFlag] eq TRUE>
          ;call   Help   ; don't give any help
          jmp     Exits
        .endif

        cmp     [DelFlag],TRUE
        jz      Not_Help
        cmp     [InstFlag],TRUE
        jz      Not_Help
        mov     dx,offset badflag
        ;call    cs:printmess ; give no clues
        ;call    help
        jmp     Exits

Not_Help:

        mov     di,offset TSRPSP     ; Save The PSP
        call    cs:Get_PSP           ; in TSRPSP

        xor     ax,ax
        mov     ah,TSRID
        int     2fh       ; Check for resident copy of self
        cmp     al,0ffh   ; if DOS does not return FFh
                          ; then TSR is not loaded
        jnz     NotLoaded
        cmp     [DelFlag],1 ; Was unload flag specified ?
        jnz     TellLoaded  ; If not then say we're loaded
        mov     cx,cs       ; Otherwise unload TSR
        mov     dx, offset cs:Terminate
        mov     bx, cs:TSRPSP
        mov     ah,TSRID
        mov     al,-1     ; al = termination flag
        mov     [savess],ss
        mov     [savesp],sp
        int     2fh
                      ; If we're back here then it was not
                      ; possible to unload TSR, probably some other
                      ; TSRs have hooked our vectors.
        mov     dx,offset unsafemess
        call    printmess

        jmp     short exits  ; bail out

TellLoaded:
        mov     dx,offset LoadMess
        call    printmess
LowProfile:
        jmp     short exits


NotLoaded:
        cmp     [DelFlag],1 ; Wants to uninstall ?
        jnz     passed
        mov     dx,offset Not_install ; TSR Not installed yet
        ;all    cs:printmess ; turn off Help
        jmp     short Exits
Passed:

        ;Hook int 2fh

        mov     al,2Fh
        mov     ah,35h
        int     21h
        mov     word ptr oldint2f,bx
        mov     word ptr oldint2f+2,es
        mov     dx,offset Newint2f
        mov     ax,252fh
        int     21h

        ;Hook int 21h

        mov     ax,3521H
        int     21h
        mov     word ptr Oldint21,bx
        mov     word ptr Oldint21+2,es
        mov     dx, offset NewInt21
        mov     ax, 2521h
        pushf
        call    Oldint21

StayResident:

        push    cs
        pop     ds
        mov     dx,offset Install
        call    printmess
        mov     ax,3100h
        mov     dx,offset ENdOFCODE
        add     dx,10fh   ; for the stack
        mov     cl,4
        shr     dx,cl
        int     21h


Terminate:
        mov     sp,[savesp]
        mov     ss,[savess]
        push    cs
        pop     ds
        mov     dx,offset UninstallMess
        call    cs:printmess
Exits:
        mov     ax,4C00h
        int     21h

main    endp

        end     main
