PAGE    ,132
TITLE   DIR - Directory and path cracking
NAME    Dir
SCCSID  equ     "@(#)dir.asm    6.5 92/05/21"
;
;        IBM/Microsoft Confidential
;
;        Copyright (c) IBM Corporation 1987, 1989
;        Copyright (c) Microsoft Corporation 1987, 1989
;
;        All Rights Reserved
;
; Main Path cracking routines, low level search routines and device
;   name detection routines
;
;       FindEntry
;       Srch
;       NEXTENT
;       NextEntry
;       GETENTRY
;       GETENT
;       SetDirSrch
;       SetRootSrch
;       GetPath
;       GetPathNoSet
;       ChkDev
;       ROOTPATH
;       FINDPATH
;       StartSrch
;       MatchAttributes
;       DevName
;       Build_device_ent
;
;  History:
;               27-Nov-1995 - MLM defect 72417
;                             moved from PTDA to TCB:
;                             Attrib, DirSec, VolID, Name1, cMeta, Creating,
;                             LastEnt, EntFree, EntLast, ClusFac, NxtClusNum,
;                             DirStart,  Curr_Dir_End, WFP_Start,
;                             Sattrib, NoSetDir, DevFCB, DevPt, PathNameType,

.xlist
include dosseg.inc
.xcref
include basemaca.inc
include osmaca.inc
include fnofs.inc
include oldmaca.inc
include mi.inc
include buffer.inc
include curdir.inc
include chardd.inc
include dossymc.inc
include devhdr.inc
include dirent.inc
include dpb.inc
include error.inc
include filesys.inc
include protmode.inc
include sft.inc
include tcbdata.inc
                                            ;72417 moved find.inc after tkexport.inc
include find.inc
include vpb.inc
include dbcs.inc
include mult.inc
include proc.inc
.cref
.list

        ModName , FNO_DIR_ASM


        ReferGDT        GDT_VPB,WORD
                                                ;72417 removed PTDA entries now
                                                ;      in TCB

        ReferTask       ExtErr_Locus,BYTE
        ReferTask       CurrTCB,WORD            ; Current thread control block

        EXTRNFAR        Date16
        EXTRNFAR        GetBuffr
        EXTRNFAR        MetaMatch
        EXTRNFAR        NameTrans
        EXTRNFAR        PackName
        EXTRNFAR        PathChrCmp
        EXTRNFAR        RlseBuffr

        EXTRN           IsThisADevice:NEAR
        EXTRN           IsEOF:NEAR
        EXTRN           FirstCluster:NEAR
        EXTRN           DirRead:NEAR
        EXTRN           UnPack:NEAR
        EXTRN           FigRec:NEAR
        EXTRN           SET_BUF_AS_DIR:NEAR



        FARCODE HIGH4
HIGH4CODE SEGMENT
    ASSUME  SS:TASKAREA


Break   <FindEntry -- LOOK FOR AN ENTRY>

;***    FindEntry - Find file name in disk directory.
;
;       Handles '?' and '*' properly according to the rules in the 1.2 IFS
;       spec.
;
;       ENTRY   [ThishVPB] set
;               [SecClusPos] = 0
;               [DirSec] = Starting directory sector number
;               [ClusNum] = Next cluster of directory
;               [ClusFac] = Sectors/Cluster
;               [NAME1] = Name to look for (asciiz)
;               [cMeta] set up
;
;       EXIT    'CY' if name not found
;
;               'NC'
;               'ZR' if attributes match (always except when creating)
;               AH = Device ID (bit 7 set if not disk)
;               [ThishVPB] = handle of VPB
;               DS = TASKAREA
;               ES = TASKAREA
;               [CurBuf.Segmt]:BX = Pointer into directory buffer
;               [CurBuf.Segmt]:SI = Pointer to First Cluster field in
;                       directory entry
;               [CurBuf] has directory record with match
;               [NAME1] has name to look for (asciiz)
;               [LastEnt] is entry number of the entry
;
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, flags
;
;       PSEUDOCODE
;          StartSrch
;          if (looking for vol id)
;             SetRootSrch
;          GetEntry
;          if (error)
;             return
;    Srch: if (entry exists) {
;             if (vol id)
;                remember that
;             MetaCompare(entry name,Name1)
;    NextEnt: if (no match)
;                NextEntry
;                if (error)
;                   return
;                else
;                   go to Srch
;             else
;                go to Match
;          }
;          else
;             if (free entry)
;                go to NextEnt
;             else {
;                RlseBuffr
;                return error
;             }
;   Match: if (attributes match)
;             return
;          else
;             go to NextEnt
;
;
;

Procedure   FindEntry,NEAR
    ASSUME  DS:TASKAREA, ES:NOTHING
LocalVar    fe_namebuf,MAXPNAME         ; holds asciiz form of dir entry
LocalVar    fe_cnamebuf,WORD            ; length of dir entry name

        EnterProc
        SegAssert SS,<DS>
        push    bp
        call    StartSrch
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     AX,[bp].TCBAttrib
        UnSetBP_CurrTCB
                                        ;72417 end
        AND     AL,NOT attr_ignore      ; Ignore useless bits
        CMP     AL,attr_volume_id       ; Looking for vol ID only ?
        JNZ     fe10                    ; No
        CALL    SetRootSrch             ; Yes force search of root
fe10:   CALL    GETENTRY
        pop     bp
ifdef FSSTRICT
        JNC     fe11                    ; Search directory for matching entry
else
        JNC     fe17                    ; Search directory for matching entry
endif
        JMP     fex                     ; Return 'C' indicating 'entry not found'

ifdef FSSTRICT
fe11:   pushf
        SaveReg <si,ds,bp,ax>
        mov     si,CurrTCB
        mov     bp,TASKAREA:[si].TCBThishVPB
        VPBContext ds,bp              ; ds:bp -> VPB
        mov     ax,ds:[bp].vpb_fsd.vpdFAT_dir_sector.Offst
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        cmp     ax,[bp].TCBDirSec.Offst ; root directory?
        UnSetBP_CurrTCB
        jne     fe12                    ; no, go check for '.'
        mov     ax,ds:[bp].vpb_fsd.vpdFAT_dir_sector.Segmt
        SetBP_CurrTCB
        cmp     ax,[bp].TCBDirSec.Segmt ; root directory?
        UnSetBP_CurrTCB
                                        ;72417 end
        je      fe14                    ; yes, skip check for '.'

ifndef old
; bugbug 06-Nov-89 peterwu - corrupted FAT disk should not cause internalerror
; if a subdirectory on a FAT disk have the . and .. entries corrupted, the
; kernel should not internalerror. It would be nice to return an error code to
; the user, but currently no error code seems appropriate. So we just don't
; do this sanity check.
fe12:
else
fe12:   lds     si,TASKAREA:[si].TCBCurBuf ; ds:si -> buffer
        add     si,BUFINSIZ             ; ds:si -> first name in buffer
        cmp     word ptr [si],202eh     ; first name == ". "?
        jne     fe13                    ; no, go internal error
        add     si,(SIZE dir_entry)     ; ds:si -> second name in buffer
        cmp     word ptr [si],2e2eh     ; second name == ".."?
        je      fe14                    ; yes, we're ok

fe13:   InternalError <FindEntry - . or .. entry not found>
endif

fe14:   RestoreReg <ax,bp,ds,si>
        popf
        jmp short fe17
endif

;
; Srch entry point
;
Entry   Srch
    ASSUME  DS:TASKAREA

        EnterProc ,NoCheck              ; need buffer
        SegAssert SS,<DS>
        jmp short fe17
;
; NEXTENT entry point
; Get the next directory entry
;
; [CurBuf.Segmt]:BX = Pointer to next directory entry in CurBuf
; [CurBuf.Segmt]:DX = Pointer to first byte after end of CurBuf
; [LastEnt] = New directory entry number
;
Entry   NEXTENT

        EnterProc ,NoCheck

fe15:   SegAssert SS,<DS>
        push    bp
        mov     bp,CurrTCB
        mov     bp,TASKAREA:[bp].TCBThishVPB
        VPBContext es,bp
    ASSUME      ES:NOTHING
        CALL    NextEntry
        pop     bp
        LJC     fex                     ; No more entries. Return 'C'.

;
; [CurBuf.Segmt]:BX = Pointer to next directory entry in CurBuf
;

fe17:   PUSH    DS
        mov     si,CurrTCB
        mov     DS,ss:word ptr [si].TCBCurBuf.Segmt ; DS:BX -> Directory entry
    ASSUME      DS:NOTHING
        MOV     AH,BYTE PTR [BX].dir_name   ; AH = first char of dirent name
        OR      AH,AH                   ; End of directory?
        LJZ     fe60
        CMP     AH,DIRENT_DELETED       ; Free entry?
        LJZ     fe60
        TEST    [BX].dir_attr,attr_volume_id
        JZ      fe20                    ; This is not a Volume ID entry
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        INC     BYTE PTR [bp].TCBVolID  ; Remember that it is a Vol ID
        UnSetBP_CurrTCB
                                        ;72417 end
fe20:   TaskContext <ES>,SI
        lea     di,fe_namebuf           ; (ES:DI) -> fe_namebuf
        cmp     byte ptr [bx],'.'       ; if '.' or '..' entries, map to ".\0"
        jne     fe23
;
; here we do processing to guarantee that "??", "*", and "????????.???"
; match "." and ".." and that "?" doesn't match "..".  We do this by mapping
; each '.' to 01h, then appending a real dot to the end.   This way, each
; '.' is treated as one character, but doesn't match a real character
; accidentally.  The '.' on the end is to support the implied '.'
;
        mov     cx,2                    ; len of fe_namebuf
        mov     al,01h
        stosb                           ; map first dot
        cmp     byte ptr [bx+1],'.'     ; ".."?
        jne     fe21                    ; no, go null terminate
        stosb                           ; map second dot
        inc     cx
fe21:   mov     word ptr es:[di],002eh  ; append '.' and null terminate
        mov     fe_cnamebuf,cx          ;
        jmp short fe23_5
fe23:   MOV     SI,BX                   ; (DS:SI) -> dir entry name
        push    di
        CALLFAR PackName
        mov     cx,di                   ; (ES:CX) -> end of packed name
        pop     di                      ; (ES:DI) -> beginning of packed name
        sub     cx,di                   ; (CX) = length of name
        mov     fe_cnamebuf,cx          ;
        dec     fe_cnamebuf             ; don't count null
;
; the following processing is to support the implied '.'.  the implied dot
; means that:
;
;   "*.*" matches all files in the directory, even if they have no extension
;   "*t" matches "text"
;   "*t." matches "text"
;
; to do this, we do special processing for names that don't have extensions.
;
; if no '.' in name
;    append '.'
; if no '.' in expression
;    if no '*' in expression
;       append '.'
;    else
;       if last char in name == '.'
;          append '.'
;
; part of the expression processing is done by NameTrans.  it takes care of
;    if no '*' in expression
;       append '.'
; case.  therefore, if we get to here with no '.' in the expression, we know
; it contains a '*' and only need to test for
;    if last char in name == '.'
; we have to do that here, for each name read for disk, because it's dependent
; on the name.

        mov     al,'.'
        repne scasb                     ; look for '.'
        jz      fe23_5                  ; found '.'
        mov     word ptr es:[di-1],002eh  ; not found, append '.'
        inc     fe_cnamebuf

fe23_5: pop     ds                      ; (DS) = TASKAREA
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        lea     si,ss:[si].TCBName1     ; (DS:SI) -> expression
                                        ;72417 end
        xor     cx,cx                   ; count of chars in expression
fe24:   lodsb                           ; (AL) = char
        inc     cx                      ;
        cmp     al,'.'                  ; '.'?
        jz      fe29                    ; yes, go call MetaMatch
        or      al,al                   ; end of string?
        jnz     fe24                    ; no, go look some more.

        lea     di,fe_namebuf           ; (DS:DI) -> string
        add     di,fe_cnamebuf          ; (DS:DI) -> last char in string
        cmp     byte ptr [di-1],'.'     ; last char == '.'
        jne     fe29

        mov     word ptr [si-1],002eh   ; append '.' to end of string

fe25:   push    si
        lea     si,fe_namebuf           ; (DS:SI) -> string
                                        ;72417 moved from PTDA to TCB
        mov     di,CurrTCB
        lea     di,ss:[di].TCBName1     ; (ES:DI) -> expression
                                        ;72417 end
        push    dx
        push    bx
        mov     bx,fe_cnamebuf          ; bx = len of string
                                        ; cx = len of expression
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        cmp     [bp].TCBcMeta,-1        ; metas in name?
        UnSetBP_CurrTCB
                                        ;72417 end
        jne     fe26                    ; yes, go call MetaMatch

        cmp     bx,cx                   ; same length?
        jne     fe25a                   ; no, go return error
        repe    cmpsb                   ; do they match?
        jz      fe27                    ; yes, go continue (NC)
fe25a:  stc
        jmp short fe27                  ; no, go return CY

fe26:   CALLFAR MetaMatch
fe27:   pop     bx
        pop     dx
        pop     si                      ; (DS:SI) -> end of expression
        mov     byte ptr [si-1],00      ; remove '.' from end of string
        jnc     fe295                   ; match, go check attributes
        jmp     fe15                    ; no match, go get next file

fe28:   pop     ds                      ; (DS) = TASKAREA
fe29:   lea     si,fe_namebuf           ; (DS:SI) -> string
                                        ;72417 moved from PTDA to TCB
        mov     di,CurrTCB
        lea     di,ss:[di].TCBName1     ; (ES:DI) -> expression
                                        ;72417 end
        push    di                      ;
        xor     al,al                   ;
        mov     cx,-1                   ;
        repne   scasb                   ;
        not     cx                      ;
        dec     cx                      ; cx = expr length
        pop     di
        push    dx
        push    bx                      ;
        mov     bx,fe_cnamebuf          ; bx = string length

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        cmp     [bp].TCBcMeta,-1        ; metas in name?
        UnSetBP_CurrTCB
                                        ;72417 end
        jne     fe29a                   ; yes, go call MetaMatch

        cmp     bx,cx                   ; same length?
        jne     fe29c                   ; no, go return error
        repe    cmpsb                   ; do they match?
        jz      fe29b                   ; yes, go continue (NC)
fe29c:  stc
        jmp short fe29b                 ; no, go return CY

fe29a:  CALLFAR MetaMatch
fe29b:  pop     bx
        pop     dx
        LJC     fe15                    ; no match, get next file

;
; We have a file with a matching name.  We must now consider the attributes:
; Attrib        Action
; ------        ------
; Volume_ID     Is Volume_ID in test?
; Otherwise     If no create then Is Attrib+extra superset of test?
;               If create then Is Attrib equal to test?
;
;   BX = offset of directory entry that matched
;
fe295:  mov     si,CurrTCB
        mov     es,ss:word ptr [si].TCBCurBuf.Segmt ; ES:BX -> Directory entry
        MOV     CH,es:[bx].dir_attr        ; Attributes of file
        LEA     SI,[bx].dir_firstfile   ; point to firclus field
    ASSUME  DS:TASKAREA
        SegAssert SS,<DS>
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     AH,byte ptr [bp].TCBAttrib ; Attributes of search
        UnSetBP_CurrTCB
                                        ;72417 end
        AND     AH,NOT attr_ignore
        TEST    CH,attr_volume_id       ; Volume ID file?
        JZ      fe30                    ; Nope check other attributes
        TEST    AH,attr_volume_id       ; Can we find Volume ID?
        LJZ     fe15                    ; Nope, (not even $FCB_CREATE)
        XOR     AH,AH                   ; Set zero flag for $FCB_CREATE
        JMP     SHORT fe40              ; Found Volume ID
fe30:   CMP     AH,attr_volume_id       ; Looking only for Volume ID?
        LJZ     fe15                    ; Yes, continue search
        call    MatchAttributes
        JZ      fe40
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        TEST    BYTE PTR [bp].TCBCreating,-1    ; Pass back mismatch if creating
        UnSetBP_CurrTCB
                                        ;72417 end
        LJZ     fe15                    ; Otherwise continue searching
fe40:   pushf
        push    bp
        mov     bp,CurrTCB
        mov     bp,TASKAREA:[bp].TCBThishVPB
        VPBContext es,bp                ; es:bp -> VPB
        LES     BP,ES:[bp].vpb_fsi.vpi_pDPB
        MOV     AH,ES:[BP.dpb_drive]
        pop     bp
        popf
        jmp short fex

; We found a free entry, or the end of the directory.
; If this is a free entry, we continue the search with the next directory entry.
; If this is the end of the directory, we release the buffer containing
; the directory sector and return to the caller.
;
fe60:   POP     DS
    ASSUME  DS:TASKAREA
        SegAssert SS,<DS>
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     CX,[bp].TCBLastEnt
        CMP     CX,[bp].TCBEntFree
        UnSetBP_CurrTCB
        JAE     fe70
        SetBP_CurrTCB
        MOV     [bp].TCBEntFree,CX
        UnSetBP_CurrTCB
fe70:   CMP     AH,DIRENT_DELETED
        LJZ     fe15                   ; Not end of directory - continue search

;
; End of directory and no match found. Release directory buffer and return with
; Carry set.
;
        SetBP_CurrTCB
        MOV     [bp].TCBEntLast,CX
        UnSetBP_CurrTCB
                                       ;72417 end
        CALLFAR RlseBuffr
        STC

fex:    TaskContext <ES>,,NoAssume      ; Set ES before returning
        LeaveProc
        ret

EndProc FindEntry


Break   <NextEntry -- STEP THROUGH DIRECTORY>

;***    NextEntry - Step to next entry in directory
;
;       NextEntry steps to the next entry in the directory, if there is
;       one.
;
;       ENTRY   [CurBuf+2]:BX = Pointer to next directory entry in CurBuf
;               [CurBuf+2]:DX = Pointer to first byte after end of CurBuf
;               [LastEnt] = New directory entry number
;
;       EXIT    If Carry clear, next entry found
;                   [LastEnt] is updated for the next directory entry.
;                   [CurBuf+2]:BX -> Entry found
;               Carry set if no more entries.
;
Procedure NextEntry,NEAR
    ASSUME  DS:TASKAREA, ES:NOTHING
        SegAssert SS,<DS>

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     AX,[bp].TCBLastEnt
        CMP     AX,[bp].TCBEntLast
        UnSetBP_CurrTCB
                                        ;72417 end
        JZ      NONE                    ; No more entries in directory
        INC     AX                      ; (AX) = next directory entry index
        LEA     BX,[BX + size dir_entry]; BX -> Next directory entry
        CMP     BX,DX
        JB      HAVIT                   ; Directory entry is in this sector
;
; The next directory entry is in another sector. We release this buffer and
; get the next one for this directory.
;
        push    bp
        mov     bp,CurrTCB
        mov     bl,[bp].TCBSecClusPos
        pop     bp
        INC     BL
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        CMP     BL,BYTE PTR [bp].TCBClusFac
        UnSetBP_CurrTCB
                                        ;72417 end
        JB      SAMECLUS                ; Next sector is in same cluster
;
; The next directory sector is in another cluster. We release this buffer
; and examine [NxtClusNum] (which contains the next cluster for the directory)
; to see if it is a valid cluster for data on the disk. If it is, we call
; GETENT to get the next directory sector into the buffer cache. If not, we
; assume that there are no more directory entries to scan, and return an
; error indicating that no match was found.
;
; AX = next directory entry to get
;
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     BX,[bp].TCBNxtClusNum
        UnSetBP_CurrTCB
                                        ;72417 end
        call    IsEOF
        JAE     NONE                        ; Have reached the end of the directory
        CMP     BX,2
        JB      NONE                        ; No more directory clusters
        CALLFAR RlseBuffr
        JMP     GETENT                      ; Get next directory entry

;
; There are no more directory entries. Release the buffer and return with
; Carry set.
;
NONE:   CALLFAR RlseBuffr
        STC
        JMP     SHORT nxex

;
; We found a match. Update [LastEnt] to remember the next directory entry
; and return with Carry clear.
;
HAVIT:
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     [bp].TCBLastEnt,AX
        UnSetBP_CurrTCB
                                        ;72417 end
        CLC
nxex:   RET

;
; The next directory sector is in the same cluster as the previous one. We
; already know what sector it is, so we can go and get the sector in a
; buffer right away.
;
SAMECLUS:
        mov     di,CurrTCB
        mov     TASKAREA:[di].TCBSecClusPos,bl
                                        ;72417 moved from PTDA to TCB
        MOV     [di].TCBLastEnt,AX
                                        ;72417 end
        PUSH    DS
        LDS     DI,TASKAREA:[DI].TCBCurBuf
    ASSUME      DS:NOTHING
        Assert  ISCURBUF,<DS,DI>,"NextEntry"

        MOV     DX,word ptr [DI.buf_sector]
        MOV     CX,word ptr [DI.buf_sector]+2   ; CX:DX = this directory sector
        CALLFAR RlseBuffr
        ADD     DX,1                    ; INC does not affect CARRY
        ADC     CX,0                    ; CX:DX = next directory sector
        POP     DS
    ASSUME  DS:TASKAREA
        SegAssert SS,<DS>
        call    FirstCluster
        jc      nxex                    ; Return if error
        XOR     BX,BX                   ; BX -> First directory entry in new sector
        JMP     SetEntry
EndProc NextEntry


;***    GETENTRY, GETENT - Locate directory entry in preparation for search
;
;       ENTRY   [LastEnt] has directory entry (GETENTRY)
;               (AX) = directory entry (GETENT)
;               ES:BP point to VPB
;               [DirSec],[ClusNum],[ClusFac],[EntLast] set for DIR involved
;
;       EXIT    [CurBuf+2]:BX = Pointer to next directory entry in CurBuf
;               [CurBuf+2]:DX = Pointer to first byte after end of CurBuf
;               [LastEnt] = New directory entry number
;               [NxtClusNum],[SecClusPos] set via DirRead
;               Carry set if error (currently user FAILed to I 24)

Procedure GETENTRY,NEAR
    ASSUME  DS:TASKAREA, ES:NOTHING
        SegAssert SS,<DS>

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     AX,[bp].TCBLastEnt
        UnSetBP_CurrTCB

Entry   GETENT

        Assert  ISDPB,<ES,BP>,"GetEntry/GetEnt"
        chkvselhd es,bp
        SetBP_CurrTCB
        MOV     [bp].TCBLastEnt,AX
        UnSetBP_CurrTCB
                                        ;72417 end
;
; Convert the entry number in AX into a byte offset from the beginning of the
; directory.
;
        mov     cl,5                    ; shift left by 5 = mult by 32
        rol     ax,cl                   ; keep high order bits
        mov     dx,ax
        and     ax, NOT (32-1)          ; mask off high order bits
        and     dx, 32-1                ; mask off low order bits
;
; DX:AX contain the byte offset of the required directory entry from the
; beginning of the directory.  Convert this to a sector number.  Round the
; sector size down to a multiple of 32.
;
        MOV     BX,ES:[bp].vpb_fsi.vpi_cbSector
        AND     BL,255-31               ; Must be multiple of 32
        DIV     BX
        MOV     BX,DX                   ; Position within sector
        PUSH    BX
        call    DirRead
        POP     BX
        jc      gex                     ; Return if error
;
; BX = Position of directory entry within sector
;
; Set BX -> the next directory entry in TCBCurBuf
;     DX -> the end of TCBCurBuf
;
Entry SetEntry
        push    bp
        mov     bp,CurrTCB
        mov     DX,ss:word ptr [bp].TCBCurBuf
        pop     bp
;;      Assert  ISCURBUF,<<WORD PTR CURBUF+2>,DX>,"GetEnt"
        ADD     DX,BUFINSIZ
        ADD     BX,DX
        chkvselhd es,bp
        ADD     DX,ES:[bp].vpb_fsi.vpi_cbSector  ; Always clears carry
gex:    RET
EndProc GETENTRY


Break   <SetDirSrch SetRootSrch -- Set Search environments>

;***    SetDirSrch, SetRootSrch - Set up a directory search
;
;       ENTRY   BX cluster number of start of directory (SetDirSrch)
;               ES:BP points to VPB
;
;       EXIT    [DirStart] = BX
;               [ClusFac],[ClusNum],[SecClusPos],[DirSec] set
;               Carry set if error (currently user FAILed to I 24)
;
;       USES    AX,DX,BX

Procedure SetDirSrch,NEAR
    ASSUME  DS:TASKAREA, ES:NOTHING
        SegAssert SS,<DS>

        chkvselhd es,bp
        Assert  ISDPB,<ES,BP>,"SetDirSrch"
        OR      BX,BX
        JZ      SetRootSrch
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     [bp].TCBDirStart,BX
        UnSetBP_CurrTCB
        MOV     AL,ES:[bp].vpb_fsd.vpdFAT_cluster_mask
        INC     AL
        SetBP_CurrTCB
        MOV     BYTE PTR [bp].TCBClusFac,AL
        UnSetBP_CurrTCB
                                        ;72417 end
        SaveReg <SI>
        call    UnPack
        JNC     UNP_OK
        RestoreReg  <SI>
        RET

UNP_OK: push    bp
        mov     bp,CurrTCB
        mov     [bp].TCBClusNum,di
        MOV     DX,BX
        XOR     BL,BL
        mov     [bp].TCBSecClusPos,bl
        pop     bp
        call    FigRec
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     word ptr [bp].TCBDirSec,DX
        MOV     word ptr [bp].TCBDirSec+2,SI
        UnSetBP_CurrTCB
                                        ;72417 end
        RestoreReg  <SI>
        CLC
        RET

Entry   SetRootSrch
    ASSUME  DS:TASKAREA, ES:NOTHING
        SegAssert SS,<DS>

        Assert  ISDPB,<ES,BP>,"SetRootSrch"
        XOR     AX,AX
                                        ;72417 moved from PTDA to TCB
        push    bp
        mov     bp,CurrTCB
        MOV     [bp].TCBDirStart,AX
                                        ;72417 end
        mov     [bp].TCBSecClusPos,al
        DEC     AX
        mov     [bp].TCBClusNum,ax
        pop     bp
        chkvselhd es,bp
        MOV     AX,word ptr ES:[bp].vpb_fsd.vpdFAT_first_sector
        MOV     DX,word ptr ES:[bp].vpb_fsd.vpdFAT_dir_sector
        SUB     AX,DX
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     BYTE PTR [bp].TCBClusFac,AL
        MOV     word ptr [bp].TCBDirSec,DX
        UnSetBP_CurrTCB
        MOV     DX,word ptr ES:[bp].vpb_fsd.vpdFAT_dir_sector+2
        SetBP_CurrTCB
        MOV     word ptr [bp].TCBDirSec+2,DX
        UnSetBP_CurrTCB
                                        ;72417 end
        CLC
        RET
EndProc SetDirSrch,NoCheck


Break   <GetPath -- PARSE A WFP>

;***    GetPath - Crack the path
;
;       ENTRY   [WFP_Start] Points to WFP string (For a device, "\DEV\" must
;                       be first 5 chars; for a file or directory "d:\" must
;                       be first 3 chars.) NULL terminated.
;               [Curr_Dir_End] Points to end of Current dir part of string
;                       ( = -1 if current dir not involved, else
;                        Points to first char after last "\" of current
;                        dir part)
;               [WFP_Path_End] Points to end of path part of string
;                        Points to first char after last "\" of entire string.
;               [ThisCDS] Points to CDS being used
;               [Sattrib] Is attribute of search, determines what files can
;                       be found
;               [TCBNoSetDir] set
;               DS is TASKAREA
;               [TCBThishVPB] = VPB
;               [cMeta] set up
;
;       EXIT    Sets ExtErr_Locus = ERRLOC_DISK if disk file
;               Sets ExtErr_Locus = ERRLOC_UNK if char device
;               ID1 field of [ThisCDS] updated appropriately
;               [Attrib] = [Sattrib]
;               'C' if object not found or error
;                   SI Points to path element causing failure
;                   'Z'
;                       [DirStart],[DirSec],[ClusNum], and [ClusFac] are set
;                       up to start a search on the last directory
;                       CL is zero if there is a bad name in the path
;                       CL is non-zero if the name was simply not found
;                           [EntFree] may have free spot in directory
;                           [NAME1] is the name.
;                       CL = 81H if '*'s or '?' in NAME1, 80H otherwise
;                   'NZ'
;                       File in middle of path or bad name in path or attribute
;                       mismatch or path too long or malformed path
;               'NC' if object found
;                   If DEVICE
;                       TASKAREA:BX points to DEVFCB
;                       TASKAREA:SI points to dir_firstfile field
;                       AH = device attribute bits from device header
;                            high bit (DEVID_DEVICE) of AH also set
;                   Else
;                       ES:BP points to VPB
;                       [CurBuf] = -1 if root directory
;                       [CurBuf] contains directory record with match
;                       [CurBuf+2]:BX Points into [CurBuf] to start of entry
;                       [CurBuf+2]:SI Points into [CurBuf] to dir_firstfile field
;                            for entry
;                       [NAME1] Has name looked for
;                       'Z' if last element is a DIRECTORY
;                           [DirStart],[SecClusPos],[DirSec],[ClusNum], and
;                           [ClusFac] are set up to start a search on it.
;                           unless [TCBNoSetDir] is non zero in which case the
;                           return is like that for a file (except for 'Z' flag)
;                       'NZ' if last element is a FILE
;                           [DirSec],[ClusNum],[ClusFac],[NxtClusNum],
;                           [SecClusPos], [LastEnt], [EntLast] are set to
;                           continue search of last directory for further matches
;                           on NAME1 via the NEXTENT entry point in FindEntry (or
;                           GETENT entry in GETENTRY in which case [NxtClusNum]
;                           and [SecClusPos] need not be valid)
;
;       USES    AX, BX, CX, DX, DI, SI, ES, BP, Flags
;
Procedure   GetPath,HYBRID
    ASSUME  DS:TASKAREA, ES:NOTHING
        SegAssert SS,<DS>

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     BYTE PTR [bp].TCBCreating,00H           ; Not Creating
        UnSetBP_CurrTCB
                                        ;72417 end


;***    GetPathNoSet - Same as GetPath only Creating already set

Entry   GetPathNoSet
        MOV     [ExtErr_Locus],ERRLOC_DISK
        push    bp
        mov     bp,CurrTCB
        mov     ss:word ptr [bp].TCBCurBuf,-1   ; initial setting
        pop     bp
;
; See if TransPath had detected a device. We assume that TransPath *WILL*
; detect all valid device names. So, if it did not, then this is not the
; name of a device. If it was a device, we return to the caller.
; A dummy directory entry will have been created at DEVFCB for use by the
; sharing code.
;
        call    CheckForDevice
        jc      gppc0                       ; Not a device.
        RET

;
; We have a path to crack.
;
;
;   C && Z && (CL==00H)   bad name in path
;   C && Z && (CL==80H)   path OK but final name bad
;   C && Z && (CL==81H)   path OK but final name had metas in it.
;   C && NZ               file in middle of path or bad name in path or
;                         bad attribute mismatch or path too long or malformed
;   NC                    always means that the buffer TCBCurBuf is locked
;   NC && (AH==10000000B) if sign bit set the AH is a device ID not a file
;   NC && Z               directory found
;   NC && NZ              file found.

gppc0:
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     [bp].TCBAttrib,attr_directory+attr_system+attr_hidden
        UnSetBP_CurrTCB
                                        ;72417 end
                                        ; Attributes to search through Dirs
        mov     di,TASKAREA:CurrTCB             ; (SI) = current TCB     ;### PTR B720732
        les     di,TASKAREA:[di].TCBThisCDS     ; (DS:SI) = CDS pointer  ;### PTR B720732
        CMP     DI, 0ffffh            ;@CAR01 If a reserved name (i.e., \PIPE\) ;### B736359
        je      SETFERR               ;@CAR01 went unchecked then error out     ;### B736359
        MOV     AX,-1
        MOV     BX,ES:[DI.cd_fsd.cddFAT_id]
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     SI,[bp].TCBCurr_Dir_End
        UnSetBP_CurrTCB
                                        ;72417 end
;
; AX = -1
; BX = cluster number of current directory.  THis number is -1 if the media
;      has been uncertainly changed.
; SI = offset in TASKAREA into path to end of current directory text.  This
;      may be -1 if no current directory part has been used.
;
        CMP     SI,AX                   ; if Current directory is not part
        JZ      NO_CURR_D               ; then we must crack from root
        CMP     BX,AX                   ; is the current directory cluster valid
        JNZ     GOT_SEARCH_CLUSTER      ; Yes, go crack only what we need to
;
; We must crack the path beginning at the root.  Advance pointer to beginning
; of path and go crack from root.
;
NO_CURR_D:
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        MOV     SI,ss:[si].TCBWFP_Start
                                        ;72417 end
        LEA     SI,[SI+3]               ; Skip "d:\"

        mov     bp,CurrTCB
        mov     bp,TASKAREA:[bp].TCBThishVPB
        VPBContext es,bp

        JMP     ROOTPATH
;
; We are able to crack from the current directory part.  Go set up for search
; of specified cluster.
;
GOT_SEARCH_CLUSTER:

        mov     bp,CurrTCB
        mov     bp,TASKAREA:[bp].TCBThishVPB
        VPBContext es,bp

        call    SetDirSrch
        JC      SETFERR
        JMP     FINDPATH

SETFERR:
        XOR     CL,CL                   ; set zero
        STC
        RET

EndProc GetPath


Break   <ROOTPATH, FINDPATH -- PARSE A PATH>

;***    ROOTPATH - Search from root for path
;
;       ENTRY   Same as FINDPATH but,
;               SI Points to asciz string of path which is assumed to start at
;                   the root (no leading '\').
;
;       EXIT    Same as FINDPATH but:
;               If root directory specified, [CurBuf] and [NAME1] are NOT set,
;                   and [TCBNoSetDir] is ignored.
;
;       USES    ALL

Procedure   ROOTPATH,near,local

LocalVar    FP_TempBuf,14               ; Temporary buffer for string

    ASSUME  DS:TASKAREA, ES:NOTHING
        SegAssert SS,<DS>

        call    SetRootSrch
        CMP     BYTE PTR [SI],0
        JNZ     FINDPATH

; Root dir specified
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     AX,[bp].TCBSattrib
        MOV     [bp].TCBAttrib,AX
        UnSetBP_CurrTCB
                                        ;72417 end
        XOR     AH,AH                   ; Sets "device ID" byte, sets zero
                                        ; (dir), clears carry.
        RET

;***    FINDPATH - Parse path name
;
;       FINDPATH and ROOTPATH both parse a given path and verify that it
;       exists on the disk. They scan through the path, one component at
;       a time, verifying that the directory entries exist. The contents
;       of [Attrib] and [Sattrib] determine the nature of each component
;       i.e. directory, file, etc.
;
;       ENTRY   [Attrib] Set to get through directories
;               [Sattrib] Set to find last element
;               ES:BP points to VPB
;               SI Points to asciz string of path (no leading '\').
;               [SecClusPos] = 0
;               [DirSec] = Phys sec # of first sector of directory
;               [ClusNum] = Cluster # of next cluster
;               [ClusFac] = Sectors per cluster
;               [TCBNoSetDir] set
;               [Curr_Dir_End] Points to end of Current dir part of string
;                   ( = -1 if current dir not involved, else
;                    Points to first char after last "\" of current dir part)
;               [ThisCDS] Points to CDS being used
;               [Creating] set
;
;       EXIT    ID1 field of [ThisCDS] updated appropriately
;               [Attrib] = [Sattrib]
;               Carry set if bad path
;                  SI Points to path element causing failure
;                  Zero set
;                     [DirStart],[DirSec],[ClusNum], and [ClusFac] are set up to
;                     start a search on the last directory
;                     CL is zero if there is a bad name in the path
;                     CL is non-zero if the name was simply not found
;                        [EntFree] may have free spot in directory
;                        [NAME1] is the name.
;                        CL = 81H if '*'s or '?' in NAME1, 80H otherwise
;                  Zero reset
;                     File in middle of path or bad name in path
;                       or path too long or malformed path
;               ELSE
;                  ES:BP point to VPB
;                  [CurBuf] contains directory record with match
;                  [CurBuf+2]:BX Points into [CurBuf] to start of entry
;                  [CurBuf+2]:SI Points to fcb_FIRCLUS field for entry
;                  [NAME1] Has name looked for
;                  AH = device ID
;                     bit 7 of AH set if device SI and BX
;                     will point TASKAREA relative.  The firclus
;                     field of the device entry contains the device pointer
;                  If last element is a directory zero is set and:
;                     [DirStart],[SecClusPos],[DirSec],[ClusNum], and [ClusFac]
;                     are set up to start a search on it,
;                     unless [TCBNoSetDir] is non zero in which case the return is
;                     like that for a file (except for zero flag)
;                  If last element is a file zero is reset
;                     [DirSec],[ClusNum],[ClusFac],[NxtClusNum],[SecClusPos],
;                     [LastEnt], [EntLast] are set to continue search of last
;                     directory for further matches on NAME1 via the NEXTENT
;                     entry point in FindEntry (or GETENT entry in GETENTRY in
;                     which case [NxtClusNum] and [SecClusPos] need not be valid)
;
;       USES    AX, BX, CX, DX, DI, SI, DS, Flags
;
;       EFFECTS Switches context
;
Entry   FINDPATH,,local
    ASSUME  DS:TASKAREA, ES:NOTHING
        SegAssert SS,<DS>

        Assert  ISDPB,<ES,BP>,"FindPath"
        chkvselhd es,bp
        PUSH    SI                      ; Save start of this element
        MOV     DI,SI
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     CX,[bp].TCBDirStart     ; Get start clus of dir being searched
        CMP     [bp].TCBCurr_Dir_End,-1
        UnSetBP_CurrTCB
        JZ      NOIDS                   ; No current dir part
        SetBP_CurrTCB
        CMP     DI,[bp].TCBCurr_Dir_End
        UnSetBP_CurrTCB
                                        ;72417 end
        JNZ     NOIDS                   ; Not to current dir end yet
        mov     di,TASKAREA:CurrTCB             ; (SI) = current TCB     ;### PTR B720732
        les     di,TASKAREA:[di].TCBThisCDS     ; (DS:SI) = CDS pointer  ;### PTR B720732
;### PTR B720732        LES     DI,[ThisCDS]
        MOV     ES:[DI.cd_fsd.cddFAT_id],CX        ; Set current directory currency
;
; Parse the name pointed to by DS:SI into NAME1.  AL = 1 if there was a meta
; character in the string.
;
NOIDS:  CALLFAR NameTrans
        POP     DI                      ; (DI) = Start of this element
        JC      fperr
        MOV     CL,AL
        OR      CL,80H
        CMP     SI,DI                   ; Compare with offset returned by NameTrans
        JNZ     name_OK
fperr:  JMP     BADPATH                 ; NUL parse (two delims most likely)

;
; The name is stored at Name1 in the PTDA.
;
name_OK: push   si
        or      bl,bl
        JNZ     NOT_LAST
;
; This is the last element. For last element of the path switch to the
; correct search attributes.
;
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     BX,[bp].TCBSattrib
        MOV     [bp].TCBAttrib,BX
        UnSetBP_CurrTCB
                                        ;72417 end
;
; See if an entry exists in directory.
;
NOT_LAST:
        PUSH    DI                      ; Start of this element
        PUSH    CX                      ; CL return from NameTrans
        SaveReg <bp>
        call    FindEntry
        RestoreReg <bp>
        POP     CX
        POP     DI
        LJC     BADPATHPOP              ; If FindEntry returns with carry set,
                                        ; DS and ES are trash.
;
; We have found an entry for the file in the directory on the disk.
;
        VPBContext es,bp
        mov     DI,CurrTCB
        LDS     DI,TASKAREA:[DI].TCBCurBuf ; (DS:DI) -> curbuf
    ASSUME      DS:NOTHING
        Assert  ISCURBUF,<DS,DI>,"FindPath"

        TEST    BYTE PTR [BX+dir_attr],attr_directory
        LJZ     FILEINPATH              ; Error or end of path
;
; We have found a directory entry matching this component. It is an entry
; for a directory. We check to see if we are setting the directory.
; If not, then check for end of string. If we are setting the directory,
; then we go ahead and get the necessary sector off the disk into the
; buffer cache and mark the buffer as containing a directory sector.
;
                                        ;72417 moved from PTDA to TCB
        push    ds
        push    di
        TaskContext DS
        mov     di,CurrTCB
        CMP     BYTE PTR TASKAREA:[DI].TCBNoSetDir,0
        pop     di
        pop     ds
    ASSUME      DS:NOTHING
                                        ;72417 end
        JZ      SetDir
        MOV     DX,DI                   ; Save pointer to entry
        MOV     CX,DS
        TaskContext DS
        POP     DI                      ; DS:DI -> Start of next element
        CMP     BYTE PTR [DI-1],0
        JZ      SETRET                  ; Got it
        PUSH    DI                      ; Put start of next element back on stack
        MOV     DI,DX
        MOV     DS,CX                   ; Get back pointer to entry
    ASSUME      DS:NOTHING

;
; We are either setting the directory or else we have not yet reached the
; end of the string.
;
SetDir: MOV     DX,[SI]                 ; dir_firstfile
        SUB     BX,DI                   ; Offset into sector of start of entry
        SUB     SI,DI                   ; Offset into sector of dir_firstfile
        SaveReg <BX,AX,SI,CX>
        PUSH    word ptr [DI.buf_sector]
        PUSH    word ptr [DI.buf_sector]+2
        CALLFAR RlseBuffr
        MOV     BX,DX
        TaskContext DS
        call    SetDirSrch              ; This uses UnPack which might blow
                                        ; the entry sector buffer
        POP     CX
        POP     DX                      ; CX:DX = sector number
        JC      SKIP_GETB
        push    bp
        mov     bp,CurrTCB
        mov     [bp].TCBAllowed,ALLOWED_RETRY + ALLOWED_FAIL
        pop     bp
        XOR     AL,AL
        CALLFAR GetBuffr                ; Get the entry buffer back
SKIP_GETB:
        RestoreReg <CX,SI,AX,BX>
        JNC     SET_THE_BUF
        POP     DI                      ; Start of next element
        MOV     SI,DI                   ; Point with SI
        JMP     SHORT BADPATH

SET_THE_BUF:
        call    SET_BUF_AS_DIR
        mov     di,CurrTCB
        mov     DI,TASKAREA:word ptr [di].TCBCurBuf
        ADD     SI,DI                   ; Get the offsets back
        ADD     BX,DI
        POP     DI                      ; DS:DI -> Start of next element
;
; Make sure that if this is not the end of the path, the path next element
; begins with a path separator.
;
        MOV     AL,[DI-1]
        OR      AL,AL
        JZ      SETRET                  ; We have reached the end of string
        MOV     SI,DI                   ; DS:SI -> next element of string
        CALLFAR PathChrCmp
        JNZ     find_bad_name           ; Path separator not found. Invalid path.
        CALLFAR RlseBuffr
        JMP     FINDPATH                ; Next element

INCRET: INC     AL                      ; Reset zero
SETRET: RET
find_bad_name:
        DEC     SI                      ; Undo above INC to get failure point
BADPATH:
        XOR     CL,CL                   ; Set zero
        JMP     SHORT BADPRET

FILEINPATH:
        TaskContext DS                  ; Got to from one place with DS gone
        POP     DI                      ; DS:DI -> Start of next element
        MOV     AL,[DI-1]
        OR      AL,AL
        JZ      INCRET                  ; End of path
        MOV     SI,DI                   ; Path too long
        JMP     SHORT BADPRET
;
;   We arrive here by not finding a directory entry that matches our
;   search string, that is, FindEntry failed.  This means that DS and
;   ES are trash, so we must reset DS before we use it.
;
BADPATHPOP:
        TaskContext <ds>
        POP     SI                      ; DS:SI -> Start of next element
        MOV     AL,[SI-1]
        MOV     SI,DI                   ; DS:SI -> Start of bad element
        OR      AL,AL                   ; zero if bad element is last, non-zero if path too long
BADPRET:
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     AX,[bp].TCBSattrib
        MOV     [bp].TCBAttrib,AX       ; Make sure return correct
        UnSetBP_CurrTCB
                                        ;72417 end
        STC
        JMP     SHORT SETRET

EndProc ROOTPATH


Break   <STARTSRCH -- INITIATE DIRECTORY SEARCH>

;***    StartSrch - Set up a search for GETENTRY and NEXTENTRY
;
;       ENTRY   ThishVPB = handle to volume for search
;
;       EXIT    ES:BP points to hVPB
;               Sets up LastEnt=0, EntFree=EntLast=-1, VolID=0
;
;       USES    ES,BP,AX

Procedure   StartSrch,NEAR
    ASSUME  DS:TASKAREA, ES:NOTHING
        SegAssert SS,<DS>

        mov     bp,CurrTCB
        mov     bp,TASKAREA:[bp].TCBThishVPB
        VPBContext es,bp

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        XOR     AX,AX
        MOV     [bp].TCBLastEnt,AX
        MOV     BYTE PTR [bp].TCBVolID,AL       ; No volume ID found
        DEC     AX
        MOV     [bp].TCBEntFree,AX
        MOV     [bp].TCBEntLast,AX
        UnSetBP_CurrTCB
                                        ;72417 end
        RET
EndProc StartSrch


BREAK <MatchAttributes - the final check for attribute matching>

;***    MatchAttributes - Check found attributes against desired attr.
;
;       ENTRY   [Attrib] = attribute to search for
;               CH = found attribute
;
;       EXIT    JZ <match>
;               JNZ <nomatch>
;
;       USES    CH
;
;       ALGORITHM:
;               Can_Have attribute = NOT (Can_Have)
;               if (Must_Have specified) {      // new 2.0 functionality
;                       if (Can_Have && Found attribute) {
;                               return (NO_MATCH);
;                       }
;                       else {  // can_have satisfied; check for Must_have
;                               return ( (Found && Must_Have) == Must_Have );
;                       }
;               }
;               else {          // pre 2.0 functionality
;                       return ( Can_Have && Found_attrib && attr_all);
;               }
;
Procedure MatchAttributes,hybrid
    ASSUME  DS:NOTHING,ES:NOTHING

        PUSH    AX
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     AX,[bp].TCBSattrib            ; Must have search attribs set properly ;### B730745
        MOV     al,byte ptr [bp].TCBAttrib ; AH = Must_Have ; AL = Can_Have
        UnSetBP_CurrTCB
                                        ;72417 end

;*********BUGBUG - Nagara 3/26/90
; the following code is to keep not_normal bit alive for some time
; can be deleted if that bit is removed

        test    al,attr_not_norm        ;  do special checking for not_norm
        jnz     mat30                   ;  not_norm file requested

; ********END BUGBUG *****************

        not     al
        or      ah,ah                   ; no must_have bits ? pre os2/2.0
        jz      mat20
        and     al,ch                    ; SearchSet' && FoundSet
        jnz     mat10                   ; no match ?

; one of the can_have bits are on OR the file is a normal file; We have
; to further check for the Must_have attribute
; ch = found attribute
; ah = must_have attribute

        and     ch,ah                   ; Must_have && found
        cmp     ch,ah                   ; ZR set if matched

mat10:  POP     AX
        RET


; pre-2.0 findfirst functionality
; al = SearchSet'
; ch = foundset

mat20:  AND     AL,CH                       ; AL <- SearchSet' and FoundSet
        AND     AL,attr_all     ; AL <- SearchSet' and FoundSet and Important
        pop     ax
        ret

;*********BUGBUG - Nagara 3/26/90
; the following code is to keep not_normal bit alive for some time
; can be deleted if that bit is removed

; not_norm bit set
; if (ch) then MATCH else (i.e. if FOUND = 0) then FAIL

mat30:  or      ch,ch                   ;is the file normal ?
        jz      mat40                   ; AL already NON_ZERO (attr_not_norm)
        mov     al,ch                   ; to set ZR flag
mat40:  cmp     al,ch                   ; ZR if (ch) else NZ
        jmp     short mat10

;*********END BUGBUG **************

EndProc MatchAttributes


BREAK <Build_device_ent - Make a Directory entry>

;***    Build_device_ent - Build a directory entry for a device at DEVFCB
;
;       Build_device_ent makes up a dummy directory entry for a device
;       at DEVFCB. This is done as some calls use this information to
;       use the same code to return information for a file or a device.
;       (e.g. Dos_Find_First).
;
;       ENTRY   [NAME1] has name
;               [DevPt] points to device header (supplied by DevName)
;
;       EXIT    BX points to DEVFCB
;               SI points to dir_firstfile field
;               AL = 0
;               dir_firstfile = DevPt
;
;       USES    AX, BX, CX, DX, DI, SI, Flags
;
Procedure Build_device_ent,near,local
    ASSUME  DS:TASKAREA, ES:TASKAREA
        SegAssert SS,<DS,ES>

        MOV     AX,"  "
                                        ;72417 moved from PTDA to TCB
        mov     di,CurrTCB
        lea     DI,ss:[di].TCBDevFCB+8 ; Point to extent field
                                        ;72417 end
;
; Fill dir_ext
;
        STOSW
        STOSB                           ; Blank out extent field
        MOV     AL,attr_device
;
; Fill Dir_attr
;
        STOSB                           ; Set attribute field
        XOR     AX,AX
        MOV     CX,10
;
; Fill dir_pad
;
        REP     STOSW                   ; Fill rest with zeros
        CALLFAR Date16
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        lea     DI,[bp].TCBDevFCB+dir_time
        XCHG    AX,DX
;
; Fill dir_time
;
        STOSW
        XCHG    AX,DX
;
; Fill dir_date
;
        STOSW
        MOV     SI,DI                   ; SI points to dir_firstfile field
        MOV     AX,[bp].TCBDevPt.Offst
;
; Fill dir_firstfile
;
        STOSW                           ; dir_firstfile points to device
        MOV     AX,[bp].TCBDevPt.Segmt
        UnSetBP_CurrTCB
                                        ;72417 end
;
; Fill dir_size_l
;
        STOSW
        RET
EndProc Build_device_ent


;***    CheckForDevice - Check to see if name represents a device
;
;       ENTRY   [WFP_Start] Points to WFP string NULL terminated.
;               [PathNameType] indicates type of path
;
;       EXIT    'C' clear if name is of a device
;                   TASKAREA:BX points to DEVFCB
;                   TASKAREA:SI points to dir_firstfile field (from Build_device_ent)
;                   AH = device attribute bits from device header
;                        high bit of AH also set (DEVID_DEVICE)
;               'C' set if not a device
;
;       NOTES   If PathNameType indicates that the path is of a device, then
;               we know that TransPath has placed a \DEV\ prefix in front of
;               the device name (there is no D:).
;
;       USES    AX,BX,CX,DI,SI,ES,Flags
;
;       EFFECTS Changes Name1
;
;   Change history:
;   ---------------
;   75228 Nizinski 10/29/93  Changed check of PNType_LOCDEV from a 'cmp'
;                            to a 'test' instruction, since multiple bits
;                            can be on in the PathNameType byte.
;                            Also changed subsequent 'jnz' to 'jz'.
;
Procedure   CheckForDevice,NEAR,LOCAL
        ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

;
; Check to see if TransPath had detected a local device earlier. If TransPath
; did not detect a local device, then we assume that this is not a device.
;
; NOTE If the name had been for a pseudo-device, we would not come here
;      (we would go to an FSD).
;
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_LOCDEV                 ; 75228
        UnSetBP_CurrTCB
                                        ;72417 end
        STC
        JZ      ckd99           ; Not a local device. Exit with Carry.; 75228
        SaveReg <DS>
        TaskContext <DS,ES>,SI  ; This assumes the string is always in TASKAREA
;
; TransPath had detected a device. We make an entry at DEVFCB for it by first
; copying the name into Name1, and then calling Build_device_ent.
;
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        lea     di,[bp].TCBName1 ; ES:DI -> Name1
        mov     si,[bp].TCBWFP_Start ; DS:SI -> beginning of the name
        UnSetBP_CurrTCB
                                        ;72417 end
        add     si,5                        ; Advance pointer past \DEV\
;
; Copy the name over into Name1. The end of the name is indicated with a NULL.
; This is true since TransPath did this when it detected that the name was for
; a device.
; If the name is longer than 8 characters long, then we asssume that it is not
; for a device. This is because we only support 8-byte character device names
; in the FAT file system.
;
; ES:DI -> Destination (Name1)
; DS:SI -> Device name
;
        mov     cx,DEVNAMELEN+1
ckd10:  lodsb
        stosb
        or      al,al
        jz      ckd20                   ; We have reached the end of the name
        loop    ckd10

ckd15:  STC                             ; Name is not that of a device
        jmp     short ckd90

ckd20:  call    Build_device_ent
;
; In the particular case of a char device set LOCUS to Unknown. This makes
; certain idiotic problems reported go away.
;
                                        ;72417 moved from PTDA to TCB
        mov     [ExtErr_Locus],ERRLOC_UNK
        SetBP_CurrTCB
        mov     ax,[bp].TCBSattrib
        mov     [bp].TCBAttrib,ax
;
; Get device attribute bits from the device header at DevPt into AH.
; This is used by some callers of CheckForDevice.
;
; SI -> dir_firstfile field in DEVFCB (from Build_device_ent)
;
        lea     bx,[bp].TCBName1     ;get name1 into bx

        lds     di,[bp].TCBDevPt
        UnSetBP_CurrTCB
                                        ;72417 end
    ASSUME DS:NOTHING
        mov     ah,BYTE PTR [DI].SDevAtt
        or      ah,DEVID_DEVICE+DEVID_DEVICE_EOF
        and     ah,NOT DEVID_DEVICE_RAW ; Clears Carry
        inc     al                      ; reset zero
ckd90:  RestoreReg <DS>
ckd99:  RET
EndProc CheckForDevice


BREAK <fValidFATDisk - check to ensure disk is a FAT disk>

;***    fValidFATDisk - Check VPB to ensure that disk is a valid FAT disk.
;
;       ENTRY   [TCBThishVPB] contains VPB for volume
;
;       EXIT    NZ  Disk is in the FAT format
;               Z   Disk is not in the FAT format
;                     AX = ERROR_NOT_DOS_DISK
;
;       USES    Flags, AX
;
;       NOTE    The algorithm is simple. We just set AX to the error
;               code always, and test the number of FATs for non-zero.
;               If it is zero, then the disk is not valid for FAT, and
;               AX is set up correctly.
;
;               The method used for detecting a valid FAT drive is not
;               100% reliable. But, it serves the purpose for IFS partitions.
;               We need to ensure that device, pipe, etc names also work
;               so we assume that a NULLVPB indicates that this is not for
;               an operation on a drive.
;
Procedure fValidFATDisk,hybrid
    ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA

        SaveReg <es,bp>
        mov     bp,[CurrTCB]
        mov     bp,[bp].TCBThishVPB
        or      bp,bp                   ; Is there a VPB?
        ERRNZ   NULLVPB
        jz      fvfd20                  ; No. Must be a device.
        VPBContext ES,BP                ; ES:BP -> VPB
        test    es:[bp].vpb_fsd.vpdFAT_FAT_count,-1
        mov     ax,ERROR_NOT_DOS_DISK
fvfd10: RestoreReg <bp,es>
        ret

fvfd20: or      sp,sp                   ; Set NZ to indicate it is valid.
        jmp     short fvfd10

EndProc fValidFATDisk

HIGH4CODE    ENDS
        END
