        TITLE DIRCALL - Directory manipulation internal calls
        NAME  DIRCALL


IF 1
__TONYI__ = 1		; Space/Speed hacks....
__TONYI__1= 1		; XOR twiddle in MakeNode
__TONYI__2= 1		; LAHF/SAHF twiddle in DOS_CHDIR
__TONYI__3= 1		; Tail merging, minor space tweaks
ENDIF

;
;       IBM Corporation
;       Copyright (C) IBM Corporation 1993
;       All Rights Reserved.
;
;**     Low level directory manipulation routines for making removing and
;         verifying local or NET directories
;
;       DOS_MKDIR
;       DOS_CHDIR
;       DOS_RMDIR
;
;       Modification history:
;
;               Created: ARR 30 March 1983

        .xlist
        .xcref
        include version.inc
        include dosseg.inc
        INCLUDE DOSSYM.INC
        INCLUDE DEVSYM.INC
        INCLUDE FASTOPEN.INC
        INCLUDE FASTXXXX.INC
        include sf.inc
        include dpb.inc
        include mult.inc
        include curdir.inc
        .cref
        .list

Installed = TRUE

        i_need  THISSFT,DWORD
        i_need  THISCDS,DWORD
        i_need  NoSetDir,BYTE
        i_need  CURBUF, DWORD
        i_need  DIRSTART,WORD
        i_need  THISDPB,DWORD
        i_need  NAME1,BYTE
        i_need  LASTENT,WORD
        i_need  SATTRIB,BYTE
        i_need  ATTRIB,BYTE
        i_need  ALLOWED,BYTE
        i_need  FAILERR,BYTE
        i_need  RenBuf,BYTE
        i_need  FastOpenFlg,BYTE                  ; DOS 3.3
        i_need  FastOpenTable,BYTE                ; DOS 3.3
        i_need  WFP_START,WORD                    ; DOS 3.3
        i_need  HIGH_SECTOR,WORD                  ; F.C. >32mb

DOSCODE Segment
        ASSUME  SS:DOSDATA,CS:DOSCODE


BREAK <DOS_MkDir - Make a directory entry>
;---------------------------------------------------------------------------
;
; Procedure Name : DOS_MkDir
;
; Inputs:
;       [WFP_START] Points to WFP string ("d:/" must be first 3 chars, NUL
;               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)
;       [THISCDS] Points to CDS being used
;               (Low word = -1 if NUL CDS (Net direct request))
; Function:
;       Make a new directory
; Returns:
;       Carry Clear
;               No error
;       Carry Set
;           AX is error code
;               error_path_not_found
;                       Bad path (not in curr dir part if present)
;               error_bad_curr_dir
;                       Bad path in current directory part of path
;               error_access_denied
;                       Already exists, device name
; DS preserved, Others destroyed
;---------------------------------------------------------------------------

;hkn; called from path.asm. DS already set up.

        ASSUME  ES:NOTHING
procedure   DOS_MKDIR,NEAR
        DOSAssume   <DS>,"DOS_MkDir"

        Invoke  TestNet
        JNC     local_mkdir
IF NOT Installed
        transfer NET_MKDIR
ELSE
        MOV     AX,(multNET SHL 8) OR 3
        INT     2FH
        return
ENDIF

NODEACCERRJ:
        MOV     AX,error_access_denied
BadRet:
        STC
IFDEF __TONYI__3
LeaveCrit_RET:
ENDIF
        LeaveCrit   critDisk
        return

PATHNFJ:
        LeaveCrit   critDisk
        transfer SET_MKND_ERR           ; Map the MakeNode error and return

LOCAL_MKDIR:
        EnterCrit   critDisk
;
; MakeNode requires an SFT to fiddle with.  We Use a temp spot (RENBUF)
;
        MOV     WORD PTR [THISSFT+2],SS

;hkn; DOSDATA
        MOV     WORD PTR [THISSFT],OFFSET DOSDATA:RenBuf
;
;  NOTE: Need WORD PTR because MASM takes type of
;   TempSFT (byte) instead of type of sf_mft (word).
;
        MOV     WORD PTR RenBuf.sf_mft,0    ; make sure SHARER won't complain.
        MOV     AL,attr_directory
        invoke  MAKENODE

        JC      PATHNFJ
        CMP     AX,3
        JZ      NODEACCERRJ     ; Can't make a device into a directory
        LES     BP,[THISDPB]    ; Makenode zaps this
        LDS     DI,[CURBUF]
ASSUME  DS:NOTHING
        SUB     SI,DI
        PUSH    SI              ; Pointer to dir_first
IFDEF __TONYI__
;-----------------------------------------------------------------
; The next couple of instructions are going to whack DS:AX, so
; we can use them here to speed things up and shrink the code.
; Saves a byte...
;-----------------------------------------------------------------
	lds	ax, dword ptr [DI.buf_sector]
	push	ds
	push	ax
ELSE
        PUSH    WORD PTR [DI.buf_sector+2]          ;F.C. >32mb

        PUSH    WORD PTR [DI.buf_sector] ; Sector of new node
ENDIF
;hkn; SS is DOSDATA
        context DS
IFDEF __TONYI__1
;---------------------------------------------------
; Save 3 bytes, 12% faster too....
;---------------------------------------------------
	xor	ax,ax		;
	xchg	[DIRSTART],ax	; DIRSTART == 0
	push	ax		; Push original DIRSTART
ELSE
        PUSH    [DIRSTART]      ; Parent for .. entry
        XOR     AX,AX
        MOV     [DIRSTART],AX   ; Null directory
ENDIF
        invoke  NEWDIR
        JC      NODEEXISTSPOPDEL    ; No room
        invoke  GETENT          ; First entry
        JC      NODEEXISTSPOPDEL    ; Screw up
        LES     DI,[CURBUF]

        TEST    ES:[DI.buf_flags],buf_dirty  ;LB. if already dirty              ;AN000;
        JNZ     yesdirty                  ;LB.    don't increment dirty count   ;AN000;
        invoke  INC_DIRTY_COUNT           ;LB.                                  ;AN000;
        OR      ES:[DI.buf_flags],buf_dirty
yesdirty:
        ADD     DI,BUFINSIZ     ; Point at buffer
        MOV     AX,202EH        ; ". "
        MOV     DX,[DIRSTART]   ; Point at itself
        invoke  SETDOTENT
        MOV     AX,2E2EH        ; ".."
        POP     DX              ; Parent
        invoke  SETDOTENT
        LES     BP,[THISDPB]
        MOV     [ALLOWED],allowed_FAIL + allowed_RETRY
        POP     DX              ; Entry sector
        POP     [HIGH_SECTOR]   ;F.C. >32mb

        XOR     AL,AL           ; Pre read
        invoke  GETBUFFR
        JC      NODEEXISTSP
        MOV     DX,[DIRSTART]
        LDS     DI,[CURBUF]
ASSUME  DS:NOTHING
        OR      [DI.buf_flags],buf_isDIR
        POP     SI              ; dir_first pointer
        ADD     SI,DI
        MOV     [SI],DX
        XOR     DX,DX
        MOV     [SI+2],DX       ; Zero size
        MOV     [SI+4],DX
DIRUP:
        TEST    [DI.buf_flags],buf_dirty  ;LB. if already dirty                 ;AN000;
        JNZ     yesdirty2                 ;LB.    don't increment dirty count   ;AN000;
        invoke  INC_DIRTY_COUNT           ;LB.                                  ;AN000;
        OR      [DI.buf_flags],buf_dirty        ; Dirty buffer
yesdirty2:

;hkn; SS is DOSDATA
        context DS
        MOV     AL,ES:[BP.dpb_drive]
        invoke  FLUSHBUF
        MOV     AX,error_access_denied
IFDEF __TONYI__3
;-----------------------------------------------------
; Save a bit.........
;-----------------------------------------------------
LeaveCrit_RET_a:
	jmp	LeaveCrit_RET
ELSE
        LeaveCrit   critDisk
        return
ENDIF

NODEEXISTSPOPDEL:
        POP     DX              ; Parent
        POP     DX              ; Entry sector
        POP     [HIGH_SECTOR]   ; F.C. >32mb

        LES     BP,[THISDPB]
        MOV     [ALLOWED],allowed_FAIL + allowed_RETRY
        XOR     AL,AL           ; Pre read
        invoke  GETBUFFR
        JC      NODEEXISTSP
        LDS     DI,[CURBUF]
ASSUME  DS:NOTHING
        OR      [DI.buf_flags],buf_isDIR
        POP     SI              ; dir_first pointer
        ADD     SI,DI
        SUB     SI,dir_first    ;Point back to start of dir entry
        MOV     BYTE PTR [SI],0E5H    ; Free the entry
        CALL    DIRUP           ; Error doesn't matter since erroring anyway
NODEEXISTS:
        JMP     NODEACCERRJ

NODEEXISTSP:
        POP     SI              ; Clean stack
        JMP     SHORT NODEEXISTS                                      ;AC000;

EndProc DOS_MKDIR

BREAK <DOS_ChDir -- Verify a directory>
;----------------------------------------------------------------------------
;
; Procedure Name : DOS_ChDir
;
; Inputs:
;       [WFP_START] Points to WFP string ("d:/" must be first 3 chars, NUL
;               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)
;       [THISCDS] Points to CDS being used May not be NUL
; Function:
;       Validate the path for potential new current directory
; Returns:
;       NOTE:
;           [SATTRIB] is modified by this call
;       Carry Clear
;           CX is cluster number of the DIR, LOCAL CDS ONLY
;               Caller must NOT set ID fields on a NET CDS.
;       Carry Set
;           AX is error code
;               error_path_not_found
;                       Bad path
;               error_access_denied
;                       device or file name
; DS preserved, Others destroyed
;----------------------------------------------------------------------------

;hkn; called from path.asm and dir2.asm. DS already set up.

        ASSUME  ES:NOTHING
procedure   DOS_CHDIR,NEAR
        DOSAssume   <DS>,"DOS_Chdir"

        Invoke  TestNet
        JNC     LOCAL_CHDIR
IF NOT Installed
        transfer NET_CHDIR
ELSE
        MOV     AX,(multNET SHL 8) OR 5
        INT     2FH
        return
ENDIF

LOCAL_CHDIR:
        EnterCrit   critDisk
        TEST    ES:[DI.curdir_flags],curdir_splice ;PTM.
        JZ      nojoin                             ;PTM.
        MOV     ES:[DI.curdir_ID],0FFFFH           ;PTM.
nojoin:
        MOV     [NoSetDir],FALSE
        MOV     [SATTRIB],attr_directory+attr_system+attr_hidden
                                ; Dir calls can find these
; DOS 3.3  6/24/86 FastOpen

        OR      [FastOpenFlg],FastOpen_Set         ; set fastopen flag
        invoke  GetPath
IFDEF __TONYI__2
;--------------------------------------------------------------------
; We need to preserve ZF and CF across this next AND.  Since we're
; whacking AX next, we can do this *MUCH* faster with LAHF/SAHF
; which are both single byte instructions, but don't touch memory.
; This is ~4X faster than pushf/popf!!!!
;--------------------------------------------------------------------
	lahf				; Save ZF & CF
        and     [FastOpenFlg],Fast_yes  ; clear it all         ;AC000;
	sahf				; Restore ZF & CF
ELSE
        PUSHF                                                                   ;AN000;
        AND     [FastOpenFlg],Fast_yes             ; clear it all               ;AC000;
        POPF                                                                    ;AN000;
ENDIF
; DOS 3.3  6/24/86 FastOpen
        MOV     AX,error_path_not_found
        JC      ChDirDone
        JNZ     NOTDIRPATH      ; Path not a DIR
        MOV     CX,[DIRSTART]   ; Get cluster number
        CLC
ChDirDone:
IFDEF __TONYI__3
;-----------------------------------------------------
; Save a bit.........
;-----------------------------------------------------
	jmp	LeaveCrit_RET_a
ELSE
        LeaveCrit   critDisk
        return
ENDIF
EndProc DOS_CHDIR

BREAK <DOS_RmDir -- Remove a directory>
;----------------------------------------------------------------------------
;
; Procedure Name : DOS_RmDir
;
; Inputs:
;       [WFP_START] Points to WFP string ("d:/" must be first 3 chars, NUL
;               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)
;       [THISCDS] Points to CDS being used
;               (Low word = -1 if NUL CDS (Net direct request))
; Function:
;       Remove a directory
;       NOTE: Attempt to remove current directory must be detected by caller
; Returns:
;       NOTE:
;           [SATTRIB] is modified by this call
;       Carry Clear
;               No error
;       Carry Set
;           AX is error code
;               error_path_not_found
;                       Bad path (not in curr dir part if present)
;               error_bad_curr_dir
;                       Bad path in current directory part of path
;               error_access_denied
;                       device or file name, root directory
;                       Bad directory ('.' '..' messed up)
; DS preserved, Others destroyed
;----------------------------------------------------------------------------

;hkn; called from path.asm. DS already set up.

        ASSUME  ES:NOTHING
procedure   DOS_RMDIR,NEAR
        DOSAssume   <DS>,"DOS_RmDir"

        Invoke  TestNet
        JNC     Local_RmDIR
IF NOT Installed
        transfer NET_RMDIR
ELSE
        MOV     AX,(multNET SHL 8) OR 1
        INT     2FH
        return
ENDIF

LOCAL_RMDIR:
        EnterCrit   critDisk
        MOV     [NoSetDir],0
        MOV     [SATTRIB],attr_directory+attr_system+attr_hidden
                                ; Dir calls can find these
        invoke  GetPath
        JC      NOPATH          ; Path not found
        JNZ     NOTDIRPATH      ; Path not a DIR
        MOV     DI,[DIRSTART]
        OR      DI,DI           ; Root ?
        JNZ     rmdir_get_buf   ; No
        JMP     SHORT NOTDIRPATH

NOPATH:
        MOV     AX,error_path_not_found
        JMP     BadRet

NOTDIRPATHPOP:
        POP     AX                        ;F.C. >32mb
        POP     AX
NOTDIRPATHPOP2:
        POP     AX
NOTDIRPATH:

IFDEF __TONYI__2
;---------------------------------------------
; Save a byte...
;---------------------------------------------
	jmp	short NODEEXISTS  ; (jumps to NodeAccErrJ)
ELSE
        JMP     NodeAccErrJ
ENDIF

rmdir_get_buf:
        LDS     DI,[CURBUF]
ASSUME  DS:NOTHING
        SUB     BX,DI           ; Compute true offset
        PUSH    BX              ; Save entry pointer
IFDEF __TONYI__
;------------------------------------------------------------
; We're about to whack ES:DI in the next couple of instructions.
; We'll use'em here to tighten things up a bit.
; Saves a byte...faster too.  Memory pushes are expensive.
;------------------------------------------------------------
	les	di, dword ptr [DI.buf_sector]
	push	es
	push	di
ELSE
        PUSH    WORD PTR [DI.buf_sector+2] ;F.C. >32mb
        PUSH    WORD PTR [DI.buf_sector] ; Save sector number
ENDIF
;hkn; SS is DOSDATA
        context DS
        context ES

;hkn; NAME1 is in DOSDATA
        MOV     DI,OFFSET DOSDATA:NAME1
IFDEF __TONYI__3
;--------------------------------------------------------------------
; Save a byte...faster too.
; STARTSRCH bonks AX instantly........don't worry.
;--------------------------------------------------------------------
        mov	ax, 63	; '?'	; AL=? AH=0
        mov	cx, 10		; Do 10 '?'s
        rep	stosb		;
        stosw			; Do 11th '?' *and* NULL terminate.
ELSE
        MOV     AL,'?'
        MOV     CX,11
        REP     STOSB
        XOR     AL,AL
        STOSB                   ; Nul terminate it
ENDIF
        invoke  STARTSRCH       ; Set search
        invoke  GETENTRY        ; Get start of directory
        JC      NOTDIRPATHPOP   ; Screw up
        MOV     DS,WORD PTR [CURBUF+2]
ASSUME  DS:NOTHING
        MOV     SI,BX
        LODSW
        CMP     AX,(' ' SHL 8) OR '.'   ; First entry '.'?
        JNZ     NOTDIRPATHPOP           ; Nope
        ADD     SI,(SIZE dir_entry) - 2 ; Next entry
        LODSW
        CMP     AX,('.' SHL 8) OR '.'   ; Second entry '..'?
        JNZ     NOTDIRPATHPOP           ; Nope

;hkn; SS is DOSDATA
        context DS
        MOV     [LASTENT],2             ; Skip . and ..
        invoke  GETENTRY                ; Get next entry
        JC      NOTDIRPATHPOP           ; Screw up
        MOV     [ATTRIB],attr_directory+attr_hidden+attr_system
        invoke  SRCH                    ; Do a search
        JNC     NOTDIRPATHPOP           ; Found another entry!
        CMP     [FAILERR],0
        JNZ     NOTDIRPATHPOP           ; Failure of search due to I 24 FAIL
        LES     BP,[THISDPB]
        MOV     BX,[DIRSTART]
        invoke  RELEASE                 ; Release data in sub dir
        JC      NOTDIRPATHPOP           ; Screw up
        POP     DX                      ; Sector # of entry
        POP     [HIGH_SECTOR]           ; F.C. >32mb

        MOV     [ALLOWED],allowed_FAIL + allowed_RETRY
        XOR     AL,AL                   ; Pre read
        invoke  GETBUFFR                ; Get sector back
        JC      NOTDIRPATHPOP2          ; Screw up
        LDS     DI,[CURBUF]
ASSUME  DS:NOTHING
        OR      [DI.buf_flags],buf_isDIR
        POP     BX                      ; Pointer to start of entry
        ADD     BX,DI                   ; Corrected
        MOV     BYTE PTR [BX],0E5H      ; Free the entry

;DOS 3.3 FastOpen  6/16/86  F.C.
        PUSH    DS

;hkn; SS is DOSDATA
        context DS
        invoke  FastOpen_Delete         ; call fastopen to delete an entry
        POP     DS
;DOS 3.3 FastOpen  6/16/86  F.C.

        JMP     DIRUP                   ; In MKDIR, dirty buffer and flush

EndProc DOS_RMDIR

DOSCODE ENDS
    END

