;       SCCSID = @(#)ldrmisc.asm        6.2 91/10/22

        PAGE    ,132
TITLE   LDRMISC - Loader Misc. routines
NAME    LDRMISC

SCCSID  equ     "@(#)ldrmisc.asm        6.2 91/10/22"

;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
; Modifications
; -------------
; 130813  Change Team  08/14/95 Add a counter adjustment code to ldrUCaseString
;                               to avoid converting unexpected data to upper
;                               case.  The original code was running over the
;                               memory area if the data pointed by pchstring
;                               contained DBCS characters.
;
; 149028  Change Team  05/17/96 Fixed overflow of string from previous defect.
; 161596  Development  08/01/96 Modified lielist to support versions other than
;                               2.0 and to support major/minor versions.

.XLIST
        include fnomem.inc
        include dosseg.inc
        include basemaca.inc
        include osmaca.inc
        include osconst.inc
        include mi.inc
        include error.inc
        include protmode.inc
        include seldesc.inc
        include dossymc.inc
        include sigdef.inc
        include except.inc
        include pgexport.inc
        include vmexport.inc
        include selxport.inc
        include vmowner.inc
        include tkexport.inc
        include infoseg.inc
        include curdir.inc
        include dirent.inc
        include find.inc
        include screen.inc
        include sem.inc
        include tkptda.inc
        include ldrvars.inc
        include ldrxport.inc
        include dynxport.inc
        include virtsym.inc
        include sft.inc
        include dbcs.inc
.LIST

        ModName  , FNO_LDRMISC_ASM
        CpuMode 386

        ReferFlat       RsrcHead,WORD
        ReferFlat       _pPTDACur,dword

        EXTRN           Bmp_SegSize:ABS

        EXTRNFAR        FindMultiChar
        EXTRNFAR        ScanPathChar
        EXTRNFAR        SegAlloc
        EXTRNFAR        Bmp_Init
        EXTRNFAR        StrLen

        EXTRN16         UCase
        EXTRN16         TestDBCSLOW

ifdef   PMDEBUG
        EXTRN16         Debug_BreakDM

        EXTRN32         _printf
endif
        EXTRN32         _SELLdrDesc
        EXTRN32         _ldrASMpMTEFromHandle

CONSTANTS       SEGMENT

; These are used by the loader to validate input string to
; DosLoadModule and DosGetModHandle

AllInvalidChars         DB      '\/:"<>'
InvalidWildChars        DB      '*?'

AllInvalidCnt           EQU     ($ - AllInvalidChars)
InvalidWildCnt          EQU     ($ - InvalidWildChars)

CONSTANTS       ENDS



ifdef LDRSTRICT
ifdef PMDEBUG
_DATA   segment

                public  _ldrErr
_ldrErr         dd      0
_ldrstopmsg     db      "ldrStop",c_CR,c_LF,0

_DATA   ends
endif
endif


        FARCODE HIGH3
HIGH3CODE        SEGMENT
    assume DS:NOTHING, ES:NOTHING, SS:TASKAREA

;***LP  is_path - scans a string for path characters, returns ZR if found.
;
;       Called by "DosLoadModule" ,"DosGetModHandle" and LDRIsPath to
;       determine whether the caller is providing a pathname or a module name.
;       This procedure scans the input string (by calling ScanPathChar) for
;       any characters ('/' or '\') that would suggest the caller is
;       providing a pathname and not a module name.
;
;       Entry:
;               (DS:SI) = pointer to ASCIIZ string to scan.
;       Return:
;               if found ZR flag set
;               else     ZR flag reset
;
;       Note:
;         At the moment a string is not considered a path unless it contains
;         a ':', '\' or '/' character. Whether or not it is a valid path is
;         determined by TransPath.
;
;       Uses:
;         Flags.
;
Procedure is_path,<near, far16>

;       Save the regs used by the ScanPathChar.

        push    ax
        push    si

        push    si
        mov     al,byte ptr ds:[si]     ; get first character
        TestDBCS isp1                   ; Is this a DBC?
        jnz     short isp1              ; No, then jump to next test
        inc     si                      ; DBC so skip trailing byte

isp1:   cmp     byte ptr ds:[si+1],":"  ; drive character present
        pop     si                      ; restore beginning string ptr.
        jz      short ispx              ; yes, then we've got a path

;       Search for a "path" character in the input string.

        CALLFAR ScanPathChar            ; see if any path chars

ispx:   pop     si
        pop     ax
        ret

EndProc is_path


;***    CheckModName - Make sure a module name contains no path or extension
;
;       ENTRY   (DS:SI) null terminated ASCII string
;               (CX)    length of string (not counting null)
;               (AX)    CHECK_ALL or CHECK_WILDCHARS
;
;       EXIT    if no error: Carry clear
;               if error: Carry set and
;                       (AX) = ERROR_INVALID_NAME
;
;       USES    AX, BX, DX
;               (DS:SI)  and the string it points to are preserved
;               (CX) is also preserved

    assume DS:NOTHING, ES:NOTHING, SS:TASKAREA

Procedure CheckModName, <hybrid, far16>

        SaveReg <es,bx,cx,si,di>
        jcxz    short cmn5              ; NULL string, report an error

;       First scan the string for any characters with ascii < MIN_CHAR_VALUE

        mov     bl,MIN_CHAR_VALUE
        mov     di,si
        push    cx

cmn1:   cmp     byte ptr [di],bl        ; Is value < MIN_CHAR_VALUE
        jc      short cmn2              ; Yes, exit and report error
        inc     di                      ; Bump pointer
        loop    cmn1                    ; Loop till end

cmn2:   pop     cx
        jc      short cmn5

;       Determine which set of invalid chars we need to use

        mov     dx,OFFSET DOSGROUP:AllInvalidChars
        mov     bx,AllInvalidCnt
        cmp     ax,CHECK_ALL
        je      short cmn3

        mov     dx,OFFSET DOSGROUP:InvalidWildChars
        mov     bx,InvalidWildCnt

cmn3:   DosContext      es

;       ES:DX   address of illegal character set
;       DS:SI   address of asciiz string to scan
;       CX      length of asciiz string
;       BX      length of illegal char array

        CALLFAR FindMultiChar           ; Returns NC if char found
        cmc
cmn5:   mov     ax,ERROR_INVALID_NAME

cmne:   RestoreReg <di,si,cx,bx,es>
cmnx:   ret

EndProc CheckModName

HIGH3CODE        ENDS



        FARCODE INIT
INITCODE segment



;***    Check_U_ModName - Check unsized module name, make sure a module name
;       contains no path or extension.
;
;       This procedure front ends the above procedure CheckModName for caller
;       in SYSINIT which doesn't know the module name length and has the name
;       pointed to by DS:DI.
;
;       This procedure called by SYSINIT only so can be discarded later,
;        so is in segment InitCode
;
;       ENTRY   (DS:DI) -> null terminated ASCII string
;
;       EXIT    if no error: Carry clear
;               if error: Carry set and
;                       (AX) = ERROR_INVALID_NAME
;
;       USES    AX
;               (DS:DI)  and the string it points to are preserved

    assume DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure Check_U_ModName,near

; First calculate the name size

        SaveReg <es,bx,cx,dx,si> ;save caller's registers

        push    ds              ;copy selector of name to es
        pop     es              ; for scasb
        mov     cx,0ffffh       ;set up cx as counter
        xor     al,al           ;looking for terminating zero
        push    di              ;save offset of name
        repne   scasb           ;look for terminating zero
        pop     si              ;restore name offset to si for call
        mov     ax,0fffeh       ;set up to calculate name length (- the 0)
        sub     ax,cx           ;calculate name length
        mov     cx,ax           ;length to cx for CheckModName

; Now we know name size, call procedure to check name.

        mov     ax,CHECK_ALL
        CALLFAR CheckModName    ;check name now that parameters are correct

        xchg    si,di           ;restore offset of name to di

        RestoreReg <si,dx,cx,bx,es>

; Return with carry and AX as set by CheckModName

        ret

EndProc Check_U_ModName



;***    RsrcInit - initialize the BMP segment to store resource information
;
;       RsrcInit allocates a GDT segment for storing system wide
;       information about resource segments. The segment is managed by
;       BMP.  The selector will be stored in _DATA:RsrcHead
;
;       ENTRY   NONE
;
;       EXIT    RsrcHead containing selector for newly allocated segment
;
;       USES    AX, CX
;
Procedure RsrcInit,near
        ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

        SaveReg <ds,es,si,bx,dx,di>

        xor     ax,ax
        mov     bx,Bmp_SegSize
        mov     cx,(D_DATA0 SHL 8) + SA_GDT
        .ERRNZ  HF_MOVABLE
        xor     si,si
        mov     di,RSRCBMPOWNER
        CALLFAR SegAlloc

        FlatContext ds
        mov     word ptr [RsrcHead+2],bx

;       Initialize the pointer to this in Sysinit's PTDA

;????   mov     word ptr ss:ptda_rsrclist+2,bx
        mov     ecx,_pPTDACur
        movzx   ebx,bx
        ror     ebx,16
        mov     [ecx].ptda_rsrclist,ebx
        ror     ebx,16

;       offset has been statically initialized to 0.
;
; Initialize the segment to be a BMP managed segment.
;       ax = segment handle
;       bx = segment selector
;       cx = block size
;       dx = grow delta (in blocks)

        mov     cx,SIZE ldrrsrc_s
        mov     dx,2
        CALLFAR Bmp_Init
        RestoreReg <di,dx,bx,si,es,ds>
        ret
EndProc RsrcInit

INITCODE ends


        FARCODE HIGH32
SWAP32_14      segment

;***LP  LdrGetModIndx - Get pointer to module and its length
;
;       Scan fully qualified module name in pchname and extract pointer to
;       module (without the drive and path) and the length of the string
;       (without) the extension).
;
;       ENTRY   pchname pointer to module name
;               plen    pointer to where len is returned
;
;       EXIT    EAX = pointer to filename
;               len copied to plen

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _LdrGetModIndx,near
ArgVar   pchname,DWORD
ArgVar   plen,DWORD
LocalVar pname,DWORD
LocalVar pext,DWORD
        EnterProc
        SSToDS  eax,pext
        PushP   eax
        SSToDS  eax,pname
        PushP   eax
        PushP   pchname
        CCALL32 _ldrGetFileName,,,PushP
        mov     edx,plen
        mov     [edx],eax
        mov     eax,pname
        LeaveProc
        RETC
EndProc _LdrGetModIndx

SWAP32_14      ENDS

SWAP32_16 segment
;***LP  ldrStrCpyB - Same as strcpy but copies reverse
;
;       ENTRY
;               pDst    Destination string
;               pSrc    Source string
;               Cnt     Count
;
;       EXIT
;               None
;
        ASSUME DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _ldrStrCpyB,near
ArgVar  pDst,DWORD
ArgVar  pSrc,DWORD
ArgVar  Cnt, DWORD
        EnterProc
        SaveReg <esi,edi>
        mov     edi,pDst
        mov     esi,pSrc
        mov     ecx,Cnt
        add     esi,ecx
        dec     esi
        add     edi,ecx
        dec     edi
        std
        rep     movsb
        cld
        RestoreReg <edi,esi>
        LeaveProc
        RETC
EndProc _ldrStrCpyB

SWAP32_16 ENDS

SWAP32_17      segment

;***LP  _ldrNextLibPath - Get next path to attempt for LIBPATH search.
;
;       _ldrNextLibPath fetches the next path name to attempt, in the
;       series of file opens which occur when traversing the LIBPATH.
;
;       _ldrNextLibPath uses *pichLibPath to index the next location
;       in the path string to access.  *pichLibPath should be 0 for the
;       first call to _ldrNextLibPath, and should not be modified again
;       for the duration of the search.
;
;       pchModName and cchModName describe the module name that is
;       being searched for.  This name must not include a drive letter,
;       path, or extension.
;
;       The path is selected from the LIBPATH variable, and the .DLL
;       extension is appended automaticly.
;
;       The selected path is returned in pchBuf.  The size of this
;       buffer must be passed in via cchBuf, for overflow protection.
;
;       _ldrNextLibPath (pichLibPath, pchBuf, cchBuf, pchModName, cchModName);
;
;       ENTRY   pichLibPath     : Address of LibPath element index
;               pchBuf          : (0:32) Pointer to path return buffer
;               cchBuf          : size of path return buffer
;               pchModName      : (0:32) Pointer to module name
;               cchModName      : Size of module name (USHORT)
;
;       EXIT
;               Return Value == NO_ERROR
;                       *pichLibPath incremented to next path element
;                       *pchBuf points to ASCIIZ pathname to try
;                       *pchModName not modified
;
;               Return Value == ERROR_FILE_NOT_FOUND
;                       if *pichLibPath == -1 :
;                               No more paths exist
;                       *pchBuf undefined
;                       *pchModName not modified
;
;       USES            EAX, ECX, EDX, EFlags
;
;       BUGBUG : Handling of blanks seems really bogus!  You could say :
;
;               LIBPATH=c:\;c : \ o s 2 \ d l l ;
;
;               And all the blanks would be ignored... !
;
; 75220: No longer put's -1 into pichLibPath if it is returning
;        a valid path.  Instead, pichLibPath is set to pointing
;        to the NULL char at the end, and next pass it will be
;        set to -1 and the rc will be non-zero.  This is to
;        allow the loop on -1 in ldropenpath while allowing
;        EndLibpath processing even if the system libpath is
;        not semicolon terminated

        ASSUME  DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING
Procedure       _ldrNextLibPath,near
ArgVar  pichLibPath,DWORD
ArgVar  pchBuf,DWORD
ArgVar  cchBuf,DWORD
ArgVar  pchModName,DWORD
ArgVar  cchModName,WORD

        EnterProc

        SaveReg <ebx,esi,edi>

        mov     edx,cchBuf
        or      edx,edx
        jz      nlp8a                           ; null buffer - error

        mov     esi,pichLibPath
        mov     esi,[esi]

        mov     edi,pchBuf                      ; (EDI) -> Start of buffer.

        cmp     esi,-1                          ; End of LibPath last time ?
        je      nlp8b                           ; yes - ERROR_FILE_NOT_FOUND

        xor     bl,bl                           ; clear quote indicator

;       (BL) is set to one to indicate that we are processing a quoted
;       string.  This is for file names where that may contain embedded ";"
;       as part of the string.  So to have a LIBPATH with a ";" contained
;       in it the user must quote the LIBPATH. (ie. LIBPATH=a:\;"c:\A;B";c:\)

;       (ECX) goes nonzero only if the last copied character was a
;       DBCS character, during this loop.  This is use for determining
;       whether or not to add a slash, at the end of this loop.

;       (EDX) contains the number of bytes available in the destination
;       buffer.  If (EDX) goes to Zero, we stop the data transfer, but
;       we still make sure to move to the next LIBPATH element anyway.

nlp0:   xor     ecx,ecx                         ; default (ECX) to Zero
nlp1:   lodsb                                   ; get a character from LibPath

        movzx   eax,al
        test    BYTE PTR [eax].CharType,fDBCS

        jz      short nlp2                      ; yes - handle it.

        or      al,al                           ; end of LIBPATH string?
        jz      short nlp3                      ; yes - exit

        cmp     al," "                          ; Blank ?
        jne     short noblnk                    ; if not continue

                                                ; check if inside quoted string
        or      bl,bl                           ; if it is consider blank a char
        jnz     short nlp1b                     ; else

        jmp     short nlp0                      ; yes - just ignore it.

noblnk: cmp     al,'"'                          ; end/begininng of quote string
        jne     short nlp1a
        xor     bl,1                            ; toggle quote indicator
        jmp     short nlp1                      ; don't copy quote charactor

nlp1a:  cmp     al,";"                          ; Path Delimiter?
        jne     short nlp1b
        or      bl,bl                           ; are we in a quote string
        jz      short nlp4                      ; no, go try this string

nlp1b:  or      edx,edx                         ; buffer length Zero?
        jz      short nlp0                      ; yes - no data transfers
        dec     edx                             ; transferring one byte

        stosb                                   ; valid char - save it.
        jmp     short nlp0

;       We just detected the DBCS character, so we copy two bytes.

nlp2:   or      edx,edx                         ; buffer length Zero?
        jz      short nlp1                      ; yes - no data transfers

        dec     edx                             ; transferring one byte.
        or      edx,edx                         ; buffer length was One?
        jz      short nlp1                      ; yes - no data transfers

        dec     edx                             ; transferring two bytes
        stosb
        movsb
        inc     ecx                             ; Set ECX nonzero
        jmp     short nlp1

;       Here, we just detected the LibPath terminating null character.

nlp3:   cmp     edi,pchBuf                      ; did we copy anything?
        je      short nlp8a                     ; no - ERROR_FILE_NOT_FOUND
        dec     esi                             ; move the libpath index
                                                ;  back to the NULL character
                                                ;  so it will be detected
                                                ;  next time.
        jmp     short nlp5                      ; yes - got a path string OK

;       Here, we detected the path delimiter.  We discard this
;       character if we haven't copied anything into the buffer yet.

nlp4:   cmp     edi,pchBuf                      ; did we copy anything?
        je      short nlp0                      ; no - discard delimiter

;       Here, we have a valid path.  We may need to add a slash
;       character if it's not there already.

nlp5:   mov     eax,pichLibPath
        mov     [eax],esi                       ; Store new libpath index

        mov     al,"\"                          ; (AL) = a slash

        or      ecx,ecx                         ; If we just copied a DBCS,
        jnz     short nlp6                      ; we need a slash.

        cmp     BYTE PTR [edi-1],al             ; Have a slash already?
        je      short nlp7                      ; yes.

        cmp     BYTE PTR [edi-1],"/"            ; Have a slash already?
        je      short nlp7                      ; yes.

nlp6:   or      edx,edx                         ; data transfer valid?
        jz      short nlp8b                     ; no - return error.
        dec     edx                             ; transferring one byte.
        stosb                                   ; Add a slash.

;       So, now we have the path copied in, complete with a slash.
;       We now concatenate the module name, the ".DLL", and the '\0'.

nlp7:   movzx   ecx,cchModName
        mov     esi,pchModName
        jecxz   short nlp8b                     ; ModName length too short (0)

        sub     edx,ecx                         ; check buffer length
        jb      short nlp8b                     ; Buffer too small

        rep     movsb                           ; Copy the ModName
        sub     edx,5                           ; check ".DLL/0" length
        jb      short nlp8b                     ; Buffer too small

        mov     eax,"LLD."
        stosd                                   ; Add ".DLL"
        xor     eax,eax                         ; NO_ERROR (also a NULL in AL)
        stosb                                   ; Null Terminate path string.
        jmp     short nlp9                      ; NO_ERROR

;       We come to nlp8a ONLY if we have run out of room for the data in
;       the buffer.

;       nlp8b is used when the end of the LibPath segment is detected.

nlp8a:  mov     esi,-1                          ; End of string !
        mov     edi,pichLibPath
        mov     DWORD PTR [edi],-1              ; Set libpath index to 'Done'
nlp8b:  mov     eax,ERROR_FILE_NOT_FOUND

nlp9:   RestoreReg <edi,esi,ebx>
        LeaveProc
        RETC

EndProc _ldrNextLibPath

SWAP32_17      ENDS
SWAP32_14      segment



;***LP  ldrGetFileName - Get filename portion of the path
;
;       Given a fully qualified path, extract the filename (without the path
;       and the extension). Return pointer to the file name, its length and
;       a pointer to the "extension". This fully supports long names i.e.
;       the name can have a '.' in it.
;
;       ENTRY:
;           pchName     address of ASCIIZ path name
;           ppchName    where pointer to the filename portion is returned
;           ppchExt     where pointer to extension is returned
;
;       EXIT
;           EAX         length of filename without the extension
;
;       C Callable

        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _ldrGetFileName,near
ArgVar  pchName,DWORD
ArgVar  ppchName,DWORD
ArgVar  ppchExt,DWORD
        EnterProc
        SaveReg <esi>
        mov     esi,pchName
        xor     ecx,ecx
lgfn0:  lodsb                           ; Get next character
        or      al,al                   ; End of the ASCII string ?
        jz      short lgfn3             ; Yes, done.

; BUGBUG philipl - this is probably not correct.  Find a way to
; access the TestDBCS macro from HIGH32CODE
;       TestDBCS                        ; Is AL a DBCS char ?
        TestDBCSLeadByte                ; Is AL a DBCS char ?
        jnz     short lgfn1             ; No, continue scanning
        inc     esi                     ; Yes, skip next char
        jmp     short lgfn0             ; Continue scanning

;       Is it a path character ?

lgfn1:  cmp     al,'\'
        je      short lgfn2
        cmp     al,'/'
        je      short lgfn2

;       Is it a '.' char ? If it is we save its position

        cmp     al,'.'
        jne     short lgfn0
        mov     ecx,esi                 ; ECX=position of char after last '.'
        jmp     short lgfn0

;       Save position of last path character

lgfn2:  mov     edx,esi                 ; EDX=address of char after last '/'
        jmp     short lgfn0

lgfn3:  mov     eax,esi                 ; EAX points to char after NULL
        mov     esi,edx                 ; ESI points to start of filename
        cmp     ecx,edx                 ; Is position of '.' after start of name
        jb      short lgfn4             ; No, don't adjust
        mov     eax,ecx                 ; Adjust for end of name at '.'

lgfn4:  mov     ecx,ppchExt
        mov     [ecx],eax               ; return address of char after the dot
        sub     eax,edx                 ; Calculate name length
        dec     eax                     ; EAX=required length
        mov     edx,ppchName            ; Return pointer to name
        mov     [edx],esi

        RestoreReg <esi>
        LeaveProc
        RETC
EndProc _ldrGetFileName

SWAP32_14      ENDS
LDRSWAP_TEXT    segment



;***LP  ldrIsPath - Determine if the supplied string is a full path
;
;       We call is_path to do the work. See is_path for details.
;
;       ENTRY
;               pachname        Pointer to string
;
;       EXIT
;               EAX     0       is not a path
;                       !0      is a path
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _ldrIsPath, near
ArgVar   pachname, DWORD
LocalVar sel,DWORD

        EnterProc
        SaveReg <esi,edi,ds>

        mov     edi,pachname                    ; FInd length of module name
        mov     ecx,-1
        xor     eax,eax
        mov     sel,eax                         ; Allocate a new selector
        repnz   scasb
        not     ecx                             ; including the terminating NULL
        SSToDS  eax,sel
        PushP   eax                             ; psel
        PushP   ecx                             ; cb
        PushP   pachname                        ; laddr
        CCALL32 _SELLdrDesc,,,PushP
IFDEF   LDRSTRICT
        or      eax,eax
        jz      short ldris1                    ; failure - internal error.
        Panic   "ldrIsPath: SELLdrDesc"
ldris1:
ENDIF
        mov     ds,sel
        ASSUME  DS:NOTHING
        xor     esi,esi
        CALL16  is_path
        setz    al
        movzx   eax,al
        RestoreReg <ds,edi,esi>
        LeaveProc
        RETC
EndProc _ldrIsPath

LDRSWAP_TEXT    ENDS
SWAP32_13       segment


;***LP  _ldrMatchStringsN - Check if two strings match for a given length N
;
;       _ldrMatchStringsN does a case sensitive string match with DBCS checks.
;
;       Neither pch1 nor pch2 is zero terminated.
;
;       ENTRY   pch1                    - string 1
;               pch2                    - string 2
;               cch                     - length of string 2
;
;       EXIT
;               Return Value == TRUE    (NonZero)
;                       pch1 == pch2
;
;               Return Value == FALSE   (Zero)
;                       pch1 != pch2
;
;       USES            EAX, ECX, EFlags
;

Procedure _ldrMatchStringsN,near

        ASSUME  DS:FLAT,ES:FLAT,SS:NOTHING

        ArgVar  pch1,DWORD
        ArgVar  pch2,DWORD
        ArgVar  cch2,DWORD

        EnterProc

        SaveReg  <esi,edi>

        mov     edi,pch1                        ; (ES:EDI) -> pch1
        mov     esi,pch2                        ; (DS:ESI) -> pch2
        mov     ecx,cch2                        ; (ECX) = length of pch2

        repe    cmpsb

        sete    al
        movzx   eax,al

        RestoreReg <edi,esi>

        LeaveProc
        RETC

EndProc _ldrMatchStringsN

SWAP32_13       ENDS
LDRSWAP_TEXT    segment


ifdef LDRSTRICT

;***LP  _ldrStop - Stop in debugger
;
;       If the global _ldrErr is TRUE and the mte pointer is null or not
;       doscalls, this routine transfers control to the debugger.
;
;       ENTRY   id                      - identifier of caller (ignored)
;               pmte                    - pointer to mte or NULL
;
;       EXIT    void
;
;       USES    EFlags
;

Procedure       _ldrStop,cdecl,,,esp
        assume  ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:TASKAREA
    ArgVar ls_id,dword
    ArgVar ls_pmte,dword

ifdef PMDEBUG
        cmp     _ldrErr,0                       ; Are we enabled?
        je      short ls8                       ;  no, just return
        cmp     [ls_pmte],0                     ; null mte pointer?
        je      short ls4                       ;  yes, skip doscalls check

        xchg    eax,[ls_pmte]                   ; (eax)=target mte pointer
        mov     eax,[eax].mte_flags1            ; (eax)=target mte flags
        and     eax,DOSMOD+LIBRARYMOD
        cmp     eax,DOSMOD+LIBRARYMOD           ; is this the DOSCALLS module?
        mov     eax,[ls_pmte]                   ; [restore eax]
        je      short ls8                       ;  yes, don't stop

ls4:    CCALL32 _printf <offset FLAT:_ldrstopmsg>,,cdecl

        push    [esp]                           ; convert near ret to far ret
        mov     [esp+4],cs

        JMP16   Debug_BreakDM                   ; break in debugger
endif

ls8:    RETC
EndProc         _ldrStop

endif


LDRSWAP_TEXT    ENDS


        FARCODE HIGH32
HIGH32CODE      SEGMENT


;***LP  ldrAscToOrd - convert an ASCII string to an ordinal
;
;       ENTRY   pchName         - pointer to string
;               cchName         - length of the string
;
;       EXIT    (EAX) = integer value of string mod 64K
;                       0 if null string or bad digit

Procedure _ldrAscToOrd,near
        ASSUME DS:FLAT, ES:FLAT, SS:NOTHING

        ArgVar  pchName,DWORD
        ArgVar  cchName,DWORD

        EnterProc
        push    esi             ; Save esi

;       if null string, we should return 0

        xor     eax,eax
        mov     ecx,cchName
        jecxz   short ato3
        mov     esi,pchName
        xor     edx,edx         ; Zero our accumulator

;       convert the ASCII string in to a number

ato1:   lodsb                   ; (EAX) = next digit
        sub     al,'0'          ; numeric value = (character - '0')
        jc      short ato2      ;  error if not a digit
        cmp     al,9            ; Too big?
        ja      short ato2      ;  yes, error

;       multiply what we have so far by 10

        lea     edx,[edx+edx*4] ; (EDX) = orignal (EDX)*5
        add     edx,edx         ; (EDX) = original (EDX)*10

;       add next digit

        add     edx,eax         ; (EDX) = original (EDX)*10 + digit
        loop    short ato1

ato2:   cmp     ecx,1           ; Digits remaining?
        sbb     ecx,ecx         ; (ECX) = 0 if digits left, -1 otherwise
        and     edx,ecx         ; (EDX) = 0 if bad digit found
        movzx   eax,dx          ; (EAX) = computed value mod 64K
ato3:   pop     esi
        LeaveProc
        RETC

EndProc _ldrAscToOrd



;***LP  _ldrUCaseString - Upper case string
;
;       ENTRY   pchstring               - pointer to string to upper case
;               cb                      - count of bytes in string
;
;       EXIT                            - String upper cased
;
Procedure       _ldrUCaseString,near
ArgVar  pchstring,DWORD
ArgVar  cb,DWORD
        EnterProc
        SaveReg <edi,esi>

        mov     esi,pchstring
        mov     edi,pchstring
        mov     ecx,cb
        jcxz    ucs4

ucs1:   lodsb                           ; - (AL) - character to upper case
        movzx   eax,al
        test    BYTE PTR [eax].CharType,fDBCS

        je      short ucs2              ;  yes, don't UCase it

        CALL16  UCase
        stosb                           ; - (AL) - Upper cased character

ucs3:   loop    short ucs1

ucs4:   RestoreReg <esi,edi>
        LeaveProc
        RETC

ucs2:   stosb                           ; copy DBCS lead byte
        dec     ecx                     ; adjust counter @IBM JS08252A Defect 149028
        jcxz    ucs4                    ; avoid running over ( fale-safe ) @IBM JS08252A Defect 149028
        movsb                           ; copy second byte
        loop    short ucs1
        jmp     short ucs4

EndProc _ldrUCaseString

;***LP  _ldrFindChar - Find a single byte character, taking into account
;                      DBCS and ignoring characters in "" unless the
;                      character to find is a ".
;                      If the character is not found, AX = -1
;
;
;       ENTRY   pchstring               - pointer to string to find the
;                                         character in.
;               ch                      - character to locate
;
;       EXIT                            - AX = index of character
;                                         or -1 if not found
;
Procedure       _ldrFindChar,near
ArgVar  pchstring,DWORD
ArgVar  cb,DWORD
        EnterProc
        SaveReg <edi,esi,ebx,ecx>

        xor     edi,edi
        xor     eax,eax
        mov     esi,pchstring
        mov     ebx,cb

fcs1:   lodsb                           ; (AL) - character to compare
        movzx   eax,al
        test    BYTE PTR [eax].CharType,fDBCS

        je      short fcs2              ; yes, go to the next character

        cmp     ax,bx
        je      short fcs4              ; characters match, return
        inc     edi                     ; increment index of found char
        cmp     bx,'"'                  ; is it a double quote?
        je      fcquote                 ; go to find other quote
        cmp     ax,0                    ; end of string?

fcs3:   jnz     short fcs1
        mov     edi,-1                  ; character not found, set result
                                        ; to -1
fcs4:   mov     eax,edi
        RestoreReg <ecx,ebx,esi,edi>
        LeaveProc
        RETC

fcs2:   lodsb                           ; get rid of second byte
        inc     edi
        jmp     short fcs1              ; go check next char


fcs2b:  lodsb                           ; yes, go to the next character
        inc     edi
        jmp     short fcquote

fcquote:lodsb                           ; (AL) - character to compare
        movzx   eax,al
        test    BYTE PTR [eax].CharType,fDBCS
        je      fcs2b
        inc     di                      ; increment index of found char
        cmp     bx,'"'                  ; is it a double quote?
        jne     fcquote                 ; go to find other quote

EndProc _ldrFindChar

HIGH32CODE      ENDS



        FARCODE HIGH32

SWAP32_9       segment



;***LP  ldrHashQATName
;
;       Calculate a hash value to represent the filename. Sum each
;       character in the string rotated left by its byte position
;       from the beginning of the string.
;
;       ENTRY   pchname pointer to module name
;
;       EXIT    EAX = hash number

        ASSUME  DS:FLAT, ES:NOTHING, SS:NOTHING
Procedure _ldrHashQATName,near
ArgVar   pName,DWORD
        EnterProc
        push    esi                   ; Save the registers we will use.
        push    ebx
        push    ecx

        mov     esi,pName             ; Use esi to index thru the string.
        xor     ebx,ebx               ; Use ebx to maintain running total.
        xor     ecx,ecx               ; Use cl for shift count.

next_hash_char:
        movzx   eax,byte ptr ds:[esi] ; Get next character in filename
        or      al,al                 ; Exit loop if it is the end of
        jz      short end_hash        ;   the string.
        rol     eax,cl                ; Work out the add value for this char.
        add     ebx,eax               ; Add this char to the running total.
        add     cx,4                  ; Next rotate value.
        and     cx,03Fh
        inc     esi                   ; Pointer to next char.

        jmp     short next_hash_char  ; And again for the next char.

end_hash:                             ; All done. Set return value to the
        mov     eax,ebx               ;   hash value.
        pop     ecx                   ; Restore the registers we trashed.
        pop     ebx
        pop     esi

        LeaveProc
        RETC
EndProc _ldrHashQATName

SWAP32_9       ENDS
SWAP32_23      segment

;***LP  ldrVersionLie
;
;       Called by w_get_version to determine whether or not to lie about
;       the version. Use the MTE20LIELIST bit in mte_flags2 to determine
;       the answer.
;
;       Note at PDD or IFS init time, the module PDD or IFS handle is in
;       the SysInit PTDA.
;
;       ENTRY   NOTHING
;
;       EXIT    Carry set   - w_get_version should return the version number,
;                             EAX register trashed.
;               Carry clear - this routine has supplied the version number
;                             in AX.
;
;       USES    ES

Procedure ldrVersionLie,far32
    assume ds:FLAT, es:nothing, fs:nothing, gs:nothing, ss:nothing

        push    ds
        FlatContext <ds>,eax

        mov     eax,_pPTDACur             ; get the module handle from the PTDA
        mov     ax,[eax].ptda_module

        or      ax,ax                     ; if module handle zero, do not lie
        jnz     short lVL1

        pop     ds
        stc
        RETC

lVL1:   CCALL32 _ldrASMpMTEFromHandle,<eax>  ; get the module flags
	push 	ebx                      ;161596
        mov     bx,[eax].mte_flags2      ;161596

        and     bx,MTE20LIELIST ; if lie bit on, go to lie code
        jnz     short lVL20

	pop	ebx
	pop	ds              ; 161596
        stc                     ; tell caller that we did not supply the version
        RETC

lVL20:  ;mov     ax,01400h               ; set version to 2.0 161596

	mov     ebx,[eax].mte_swapmte    ;get swappable mte to get version info
	mov     ah,[ebx].smte_major_os   ;161596
	mov     al,[ebx].smte_minor_os   ;161596
	pop	ebx                      ;161596
	pop 	ds                       ;161596
        clc                              ;tell caller we supplied the version
        RETC

EndProc ldrVersionLie

SWAP32_23  ENDS

        END
