mirror of
https://github.com/SEPPDROID/DR-DOS-OpenDOS.git
synced 2025-10-22 07:54:28 +00:00
2529 lines
66 KiB
Plaintext
2529 lines
66 KiB
Plaintext
; File : $UTILS.FDO$
|
|
;
|
|
; Description :
|
|
;
|
|
; Original Author : DIGITAL RESEARCH
|
|
;
|
|
; Last Edited By : $CALDERA$
|
|
;
|
|
;-----------------------------------------------------------------------;
|
|
; Copyright Work of Caldera, Inc. All Rights Reserved.
|
|
;
|
|
; THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL,
|
|
; PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC.
|
|
; ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES
|
|
; WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF
|
|
; THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO
|
|
; HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE
|
|
; AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE
|
|
; AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED,
|
|
; COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
|
|
; CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,
|
|
; TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF
|
|
; CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
|
|
; AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND
|
|
; CIVIL LIABILITY.
|
|
;-----------------------------------------------------------------------;
|
|
;
|
|
; *** Current Edit History ***
|
|
; *** End of Current Edit History ***
|
|
;
|
|
; $Log$
|
|
; UTILS.FDO 1.39 94/11/30 13:39:18
|
|
; added share_delay function
|
|
; UTILS.FDO 1.37 94/07/13 15:42:01
|
|
; Change to rename/delete of file open in compatibility modes
|
|
; UTILS.FDO 1.36 94/06/28 11:10:10
|
|
; Limit ddsc allocation to 1st 255
|
|
; UTILS.FDO 1.34 94/04/25 19:33:04
|
|
; Reject blank names (ie. all spaces) when parsing path
|
|
; We used to die in rebuild_ldt_curdir (GATEWAY problem)
|
|
; UTILS.FDO 1.33 93/12/16 13:57:06
|
|
; Fix path_prep bug when dir in path doesn't exist
|
|
; UTILS.FDO 1.32 93/12/09 23:56:10
|
|
; Move non-inherited bit to correct place in file handle
|
|
; UTILS.FDO 1.31 93/12/08 03:30:03
|
|
; Add extra check to offer_join: consider JOIN B: \FRED, SUBST L: \FRED\FRED
|
|
; A CD L:\ would see FRED at the root of B: and change into it, so we
|
|
; would end up at \FRED, so we only check if ROOTLEN=2
|
|
; UTILS.FDO 1.27 93/11/19 17:45:14
|
|
; Fix for SERVER print queue viewing problem
|
|
; UTILS.FDO 1.26 93/11/08 16:30:12
|
|
; Get dat/time on device handle returns current date/time
|
|
; UTILS.FDO 1.25 93/09/14 20:03:42
|
|
; Trust LFLG_PHYSICAL
|
|
; UTILS.FDO 1.23 93/09/03 20:26:09
|
|
; Add "no critical errors" support (int 21/6C)
|
|
; UTILS.FDO 1.22 93/07/26 18:11:00
|
|
; re-arrange DHNDL_DCNTHI for the benefit of Geoworks
|
|
; UTILS.FDO 1.21 93/07/20 22:43:48
|
|
; Even fewer checks on int 25/26
|
|
; ENDLOG
|
|
; General utility include module for FDOS.A86
|
|
|
|
select_pb2:
|
|
;----------
|
|
call get_pb2_drive ; get drive from parameter block
|
|
; jmp select_unique ; select drive, make HDS unique
|
|
|
|
select_unique:
|
|
;-------------
|
|
; entry: AL = drive to select (0-15)
|
|
|
|
mov byte ptr path_drive,al ; save logical drive
|
|
jmp select_logical_drv ; select the drive
|
|
|
|
|
|
logical2physical:
|
|
;----------------
|
|
; On Entry:
|
|
; AL = drive to select
|
|
; On Exit:
|
|
; AL = appropriate physical drive to select
|
|
;
|
|
; This routine is called by low level routines (func_ddio, func_getdpb)
|
|
; and bypasses the checks for networked/joined drives together with the
|
|
; normal media change checks. It does however handle SUBST'd drives.
|
|
;
|
|
call get_ldt_raw ; ES:BX -> LDT for our drive
|
|
jc logical2physical10 ; if we don't have one must be physical
|
|
test es:LDT_FLAGS[bx],LFLG_JOINED
|
|
jnz logical2physical10 ; joined drive - treat as physical
|
|
test es:LDT_FLAGS[bx],LFLG_SUBST
|
|
jz logical2physical10 ; as long as we aren't SUBST'd it OK
|
|
mov al,es:LDT_NAME[bx] ; get the drive from the ascii name
|
|
and al,1fh ; as the drive may require rebuilding
|
|
dec ax ; make it zero based
|
|
logical2physical10:
|
|
ret
|
|
|
|
|
|
Public dbcs_lead
|
|
|
|
dbcs_lead:
|
|
;---------
|
|
; Return true if given byte is the first of a double byte character.
|
|
; Entry
|
|
; al = byte to be tested
|
|
; Exit
|
|
; Z Flag = 1 - byte is a DBCS lead
|
|
; 0 - byte is not a DBCS lead
|
|
; Lost
|
|
; no registers changed
|
|
|
|
if KANJI
|
|
push si
|
|
push bx
|
|
push ax
|
|
|
|
; First get a pointer to the double byte lead table in the COUNTRY info.
|
|
mov si, offset DBCS_tbl+2 ; ds:si -> double byte table
|
|
|
|
; Examine each entry in the table to see if it defines a range that includes
|
|
; the given character.
|
|
mov bl, al ; bl = byte to be tested
|
|
dbcs_loop:
|
|
lods ss:ax ; al/ah = start/end of range
|
|
test ax, ax ; end of table?
|
|
jz dbcs_no ; yes - exit (not in table)
|
|
cmp al, bl ; start <= bl?
|
|
ja dbcs_loop ; no - try next range
|
|
cmp ah, bl ; bl <= end?
|
|
jb dbcs_loop ; no - try next range
|
|
|
|
cmp al, al ; return with Z flag set
|
|
jmp dbcs_exit
|
|
|
|
dbcs_no:
|
|
cmp al, 1 ; return with Z flag reset
|
|
|
|
dbcs_exit:
|
|
pop ax
|
|
pop bx
|
|
pop si
|
|
ret
|
|
else
|
|
test al, al ; force non-0 condition
|
|
ret
|
|
endif
|
|
|
|
|
|
kanji_eos:
|
|
;---------
|
|
; entry: ES:DI -> string to find the end of
|
|
; exit: ES:DI -> character before NUL byte
|
|
|
|
mov dx,di ; in case string is empty
|
|
kanji_eos1:
|
|
mov al,es:[di] ; get next character
|
|
test al,al ; is it the final NUL byte
|
|
jz kanji_eos9 ; return if NUL found
|
|
mov dx,di
|
|
inc di ; move to next character
|
|
if KANJI
|
|
call dbcs_lead ; is this first half of 16-bit char?
|
|
jne kanji_eos1 ; skip if normal character
|
|
inc di ; else skip 2nd half (should really check)
|
|
endif
|
|
jmps kanji_eos1 ; loop back for next character
|
|
|
|
kanji_eos9:
|
|
mov di,dx ; ES:DI -> last character
|
|
ret ; end of string found
|
|
|
|
|
|
path_chop:
|
|
;---------
|
|
; entry: ES:DI -> path = "d:\level1\level2\"
|
|
; exit: path = "d:\level1\"
|
|
|
|
mov al,es:[di] ; get next character
|
|
test al,al ; end of string?
|
|
jz path_chop2 ; yes, string scanned
|
|
inc di ; next character
|
|
if KANJI
|
|
call dbcs_lead ; lead-in of 16-bit character?
|
|
jne path_chop1 ; no, normal 8-bit
|
|
inc di ; skip hi-byte (should really check)
|
|
jmps path_chop ; try again
|
|
path_chop1:
|
|
endif
|
|
call check_slash ; is this a path character
|
|
jne path_chop ; loop back if not path character
|
|
mov dx,cx ; last but one '/' = last '/'
|
|
mov cx,di ; last '/' = current '/'
|
|
jmps path_chop ; repeat
|
|
path_chop2:
|
|
mov di,dx ; ES:DI -> last but one slash + 1
|
|
sub ax,ax ; get a NUL byte
|
|
stosb ; chop off the last level
|
|
ret
|
|
|
|
Public rebuild_ldt_root
|
|
|
|
rebuild_ldt_root:
|
|
;----------------
|
|
; On Entry:
|
|
; ES:BX -> LDT_ to rebuild
|
|
; fdos_hds = physical root for this drive
|
|
; On Exit:
|
|
; ES:BX preserved
|
|
; LDT_ROOT rebuilt from ASCII LDT_NAME
|
|
;
|
|
push ds
|
|
push es ! pop ds ; DS:BX -> LDT_
|
|
push ss ! pop es
|
|
mov di,offset temp_ldt ; ES:DI -> temp LDT_
|
|
lea si,LDT_NAME+3[bx] ; point to start of pathname
|
|
mov cx,LDT_ROOTLEN[bx] ; CX = end of root portion
|
|
xor ax,ax ; assume we want root block
|
|
sub cx,3 ; skip the 'D:\'
|
|
jbe rebuild_ldt_root10 ; nothing to do unless SUBST'd
|
|
rep movsb ; copy the root portion of the name
|
|
call select_dir ; select this directory
|
|
jnc rebuild_ldt_root10
|
|
xor ax,ax
|
|
mov LDT_ROOTLEN[bx],2 ; force ourselves into the root
|
|
mov LDT_NAME+3[bx],al ; as the media has changed
|
|
rebuild_ldt_root10:
|
|
push ds ! pop es ; ES:BX -> LDT_
|
|
pop ds
|
|
mov ax,fdos_hds_blk
|
|
mov es:LDT_ROOT[bx],ax ; update our root block
|
|
if JOIN
|
|
mov al,fdos_hds_drv ; and the physical drive
|
|
mov es:LDT_DRV[bx],al
|
|
endif
|
|
ret
|
|
|
|
|
|
rebuild_ldt_curdir:
|
|
;------------------
|
|
; On Entry:
|
|
; ES:BX -> LDT_ to rebuild
|
|
; fdos_hds = logical root of this drive
|
|
; On Exit:
|
|
; ES:BX preserved
|
|
; LDT_DRV and LDT_BLK rebuilt from ASCII LDT_NAME
|
|
;
|
|
push ds
|
|
push es ! pop ds ; DS:BX -> LDT_
|
|
push ss ! pop es
|
|
mov di,offset temp_ldt ; ES:DI -> temp LDT_
|
|
mov si,LDT_ROOTLEN[bx] ; SI = end of root portion
|
|
lea si,LDT_NAME[bx+si] ; point to subdir entry
|
|
lodsb ; get 1st char
|
|
call check_slash ; is it a leading '\' ?
|
|
je rebuild_ldt_curdir10 ; yes, discard it
|
|
dec si ; else leave it alone
|
|
rebuild_ldt_curdir10:
|
|
test al,al ; anything to do?
|
|
jz rebuild_ldt_curdir40 ; no, we are already there
|
|
rebuild_ldt_curdir20:
|
|
lodsb ! stosb ; copy the string
|
|
test al,al ; until we hit the terminating NUL
|
|
jnz rebuild_ldt_curdir20
|
|
dec di
|
|
call select_dir ; select this directory
|
|
jnc rebuild_ldt_curdir40
|
|
mov si,LDT_ROOTLEN[bx] ; SI = end of root portion
|
|
cmp si,3 ; is root real root or a subdir ?
|
|
ja rebuild_ldt_curdir30
|
|
mov si,3 ; real root, leave '\' alone
|
|
rebuild_ldt_curdir30:
|
|
mov LDT_NAME[bx+si],0 ; move ASCII to root
|
|
rebuild_ldt_curdir40:
|
|
push ds ! pop es ; ES:BX -> LDT_
|
|
pop ds
|
|
mov ax,fdos_hds_blk
|
|
mov es:LDT_BLK[bx],ax ; update our curdir block
|
|
if JOIN
|
|
mov al,fdos_hds_drv ; and the physical drive
|
|
mov es:LDT_DRV[bx],al
|
|
endif
|
|
ret
|
|
|
|
select_dir:
|
|
;----------
|
|
; On Entry:
|
|
; DS:BX -> LDT
|
|
; ES:DI -> end of ASCII path
|
|
; temp_ldt contains dir to select
|
|
; On Exit:
|
|
; DS:BX preserved
|
|
; CY set on error, root of original drive reselected
|
|
;
|
|
mov ax,'.\' ; append a "\." in case it's the root
|
|
stosw ; (and so a NULL path)
|
|
xor ax,ax
|
|
stosb
|
|
push ds ! push bx ; make sure LDT survives
|
|
push ss ! pop ds ; DS back to SYSDAT
|
|
mov si,offset temp_ldt ; ES:SI -> directory to select
|
|
call path_prep_next ; try to move into it
|
|
if JOIN
|
|
call offer_join ; are we opening a JOIN'd drive ?
|
|
jnc select_dir20
|
|
endif
|
|
call finddfcbf ; find the directory entry
|
|
jz select_dir10 ; stop if we can't
|
|
test DATTS[bx],DA_DIR ; check if directory
|
|
jz select_dir10 ; fail if this is a file
|
|
push ds ! pop es
|
|
lea di,DNAME[bx] ; ES:DI -> ASCII name to open
|
|
mov cx,8+3
|
|
mov al,' '
|
|
repe scasb ; is it all spaces ?
|
|
je select_dir10 ; if so reject it
|
|
call open_dir
|
|
jnc select_dir20
|
|
select_dir10:
|
|
mov ax,fdos_hds_root ; move to the virtual root
|
|
mov fdos_hds_blk,ax
|
|
stc ; return error
|
|
select_dir20:
|
|
pop bx ! pop ds
|
|
ret
|
|
|
|
|
|
eject
|
|
; Run down the path and parse final name
|
|
; exit: ds:dx -> info_fcb parsed at path end
|
|
; cf = 1, and al = code on any error
|
|
|
|
path_prep:
|
|
les di,dword ptr fdos_pb+2 ; es:di -> path name
|
|
|
|
path_prep_ptr:
|
|
call path_prep_check ; try to prepare path
|
|
jnc path_prep_good ; skip if success
|
|
jmp fdos_error ; return error to application
|
|
|
|
path_prep_drive_error:
|
|
; stc ; return CY set
|
|
mov ax,ED_DRIVE ; with correct error code
|
|
path_prep_good:
|
|
ret
|
|
|
|
path_prep_check:
|
|
call get_path_drive ; from asciiz or default
|
|
jc path_prep_drive_error ; continue if drive A: - Z:
|
|
path_prep_cont:
|
|
push di ; DX = drive code (0-15)
|
|
push es ; save string address
|
|
push ds ! pop es ; ES = SYSDAT
|
|
call select_unique ; select the drive and
|
|
pop es
|
|
pop si ; es:si -> past drive
|
|
|
|
lods es:al ; get first character
|
|
call check_slash ; if '\' or '/' then start at root
|
|
jne path_prep_curdir ; else select current directory
|
|
push es
|
|
call path_prep_root ; fake a '.' entry for the root
|
|
pop es
|
|
lods es:al ; get next char
|
|
dec si ; forget we looked
|
|
test al,al ; if just a '\' stop now
|
|
jz path_prep_done
|
|
call check_slash
|
|
jne path_prep_next ; otherwise start processing from root
|
|
mov ax,ED_ACCESS ; get correct error code
|
|
stc ; and return if \\
|
|
ret
|
|
|
|
path_prep_curdir:
|
|
; We need to select the current directory as a start for our operations
|
|
dec si ; forget about char we looked at
|
|
push es ! push si ; and save position in name
|
|
mov al,logical_drv ; get the current logical drive
|
|
call get_ldt ; and hence the LDT structures
|
|
jc path_prep_curdir30 ; no LDT, leave at physical root
|
|
if JOIN
|
|
mov al,fdos_hds_drv ; are we on a known drive ?
|
|
sub al,es:LDT_DRV[bx] ; (we may be on joined drive)
|
|
jne path_prep_curdir10 ; if not better rebuild
|
|
cbw ! dec ax ; AX = FFFF
|
|
else
|
|
mov ax,0FFFFh
|
|
endif
|
|
cmp ax,es:LDT_BLK[bx] ; do we need to do a rebuild
|
|
jne path_prep_curdir20 ; or can we trust the media ?
|
|
path_prep_curdir10:
|
|
call rebuild_ldt_curdir ; better reselect current dir
|
|
path_prep_curdir20:
|
|
mov ax,es:LDT_BLK[bx] ; move to current directory block
|
|
mov fdos_hds_blk,ax
|
|
path_prep_curdir30:
|
|
pop si ! pop es ; ES:SI -> name again
|
|
|
|
path_prep_next:
|
|
;--------------
|
|
; Called by disk change code to rebuild an HDS_ for a drive after
|
|
; media change detected.
|
|
; On Entry:
|
|
; ES:SI -> pathname to rebuild
|
|
; fdos_hds = HDS_ to rebuild
|
|
; On Exit:
|
|
; CY set if problem (AX=error code)
|
|
; ES=DS
|
|
;
|
|
cmp es:byte ptr [si],0 ; can't have trailing '/' or '\'
|
|
je path_prep_error ; return this as an error
|
|
|
|
mov ax,path_drive
|
|
inc ax ; al = drive (one based)
|
|
call parse_path ; set up the info_fcb
|
|
jc path_prep_error ; skip on any parse error
|
|
|
|
test al,al ; AL = delimiter
|
|
jz path_prep_done ; are we at the end ?
|
|
|
|
cmp word ptr info_fcb+1,' .'
|
|
je path_prep_next ; CHDIR (".") => stay where we are
|
|
|
|
call check_no_wild ; no wilds cards in path's
|
|
je path_prep_error ; skip if wild cards found
|
|
|
|
if JOIN
|
|
push es
|
|
push si
|
|
call offer_join ; are we opening a JOIN'd drive ?
|
|
pop si
|
|
pop es
|
|
jnc path_prep_next ; if so move on to next stage
|
|
endif
|
|
push es
|
|
push si ; save string address
|
|
push ds ! pop es ; ES = local segment
|
|
call finddfcbf ; locate the directory entry
|
|
pop si
|
|
pop es ; restore string address
|
|
jz path_prep_error ; no, missing directory in path
|
|
|
|
test DATTS[bx],DA_DIR ; check if directory
|
|
jz path_prep_error ; fail if this is a file
|
|
|
|
push es ! push si ; save string address
|
|
push ds ! pop es ; ES = local segment
|
|
if PASSWORD
|
|
call check_pwd_any ; check if PW req'd & supplied
|
|
endif
|
|
call open_dir ; go down one level
|
|
pop si ! pop es ; restore string address
|
|
jnc path_prep_next ; if open is good, repeat
|
|
path_prep_error:
|
|
mov ax,ED_PATH ; return code in case of error
|
|
stc ; indicate error to caller
|
|
ret
|
|
|
|
|
|
path_prep_done:
|
|
cmp info_fcb+1,'.' ; is it '.' or '..' ?
|
|
jne path_prep_exit ; if so get its full name
|
|
if JOIN
|
|
call offer_join ; are we opening a JOIN'd drive ?
|
|
jnc path_prep_root ; if so move into the dir
|
|
endif
|
|
call finddfcbf ; find the directory entry
|
|
jz path_prep_exit ; stop if we can't
|
|
call open_dir ; move into the directory
|
|
jc path_prep_error ; stop if we can't
|
|
mov cx,DBLOCK1[bx] ; are we destined for the root ?
|
|
jcxz path_prep_root ; yes, stop - we won't find anything
|
|
push fdos_hds_root
|
|
mov fdos_hds_root,0 ; don't stop at virtual root
|
|
call find_parent ; find the parental entry
|
|
pop fdos_hds_root
|
|
jz path_prep_error ; (shouldn't happen)
|
|
mov ax,DPWD[bx] ; get password hash code from entry
|
|
mov local_password,ax ; ensure we can get back down
|
|
lea si,DNAME[bx] ; point to it's name
|
|
mov cx,11
|
|
push ds ! pop es
|
|
mov di,offset info_fcb+1
|
|
rep movsb ; copy parental name to info_fcb
|
|
path_prep_exit:
|
|
push ds ! pop es ; restore ES to local segment
|
|
clc
|
|
ret
|
|
|
|
|
|
path_prep_root:
|
|
push ds ! pop es ; ES = local segment
|
|
mov al,info_fcb ; preserve drive setting
|
|
call clear_info_fcb
|
|
mov info_fcb+1,'.' ; fake a '.' directory
|
|
ret
|
|
|
|
|
|
clear_info_fcb:
|
|
;--------------
|
|
; Sets up a clean info_fcb for later use
|
|
; On Entry:
|
|
; AL = drive
|
|
; On Exit:
|
|
; All regs preserved
|
|
;
|
|
push di ! push cx ! push ax
|
|
mov di,offset info_fcb
|
|
stosb ; set the drive code
|
|
|
|
mov cx,11
|
|
mov al,' '
|
|
rep stosb ; fill name with blanks
|
|
if PASSWORD
|
|
mov cx,8
|
|
mov di,offset password_buffer
|
|
rep stosb ; blank password buffer
|
|
mov es:local_password,cx ; zero out local password
|
|
endif
|
|
pop ax ! pop cx ! pop di
|
|
clc
|
|
ret
|
|
|
|
|
|
eject
|
|
; Get drive from path name, or if none, use default drive
|
|
; On Entry:
|
|
; es:di -> path name
|
|
; On Exit:
|
|
; AL = path_drive = sepcified or default drive
|
|
; es:di -> past drive name
|
|
; cf = 1 if illegal drive name
|
|
|
|
get_path_drive:
|
|
cmp es:byte ptr [di],0 ; check if string is empty
|
|
je get_path_error ; which isn't O.K. at all
|
|
cmp es:byte ptr 1[di],':' ; if the second char is ':',
|
|
jz get_path_explicit ; then drive is in pathname
|
|
call current_dsk2al
|
|
jmps get_path_ok ; common code from here
|
|
|
|
get_path_explicit:
|
|
mov al,es:[di] ; grab the drive designator
|
|
call toupper ; make sure it is upper case
|
|
sub al,'A' ; correct offset. if too
|
|
jb get_path_error ; small, return error
|
|
cmp al,ss:last_drv
|
|
jae get_path_error
|
|
inc di
|
|
inc di ; it's ok, bump the pointer
|
|
get_path_ok:
|
|
xor ah,ah ; zero ah and clc
|
|
mov path_drive,ax ; save for other functions
|
|
ret
|
|
|
|
get_path_error:
|
|
stc ; flag the error
|
|
ret
|
|
|
|
eject
|
|
asciiz_dev_offer:
|
|
;----------------
|
|
; On Entry:
|
|
; PB+2 -> pathname
|
|
; On Exit:
|
|
; Only come back if not a device
|
|
;
|
|
; See if the filename is that of a simple device
|
|
; eg. 'CON', 'A:CON', 'CON.EXT'
|
|
; We should also accept '\DEV' format
|
|
; eg. '\DEV\CON', "A:\DEV\CON'
|
|
; More complicated forms should be ignored - they will be handled
|
|
; after the pathname is parsed.
|
|
; eg. 'A:\CON', '\CON', 'SUBDIR\CON.EXT'
|
|
;
|
|
push ds
|
|
mov si,2[bp] ; SI -> parameter block
|
|
lds si,ds:2[si] ; DS:SI -> file specification
|
|
cmp ds:byte ptr 0[si],0 ; NUL names are stupid, but
|
|
je asciiz_dev_offer30 ; you do get them....
|
|
cmp ds:byte ptr 1[si],':' ; is a drive specified
|
|
jne asciiz_dev_offer10
|
|
inc si ! inc si ; skip that, but no more
|
|
asciiz_dev_offer10:
|
|
push ss ! pop es ; ES:DI -> scratch FCB in build
|
|
mov di,offset name_buf ; name in pcmode data buffer
|
|
mov al,' '
|
|
mov cx,8+3
|
|
rep stosb ; start by clearing name
|
|
|
|
mov al,[si] ; beware of '\DEV\name' format..
|
|
call check_slash ; if not slash carry on
|
|
jne asciiz_dev_offer15
|
|
lodsb ; possible, lets look at rest
|
|
lodsb
|
|
call toupper
|
|
cmp al,'D' ; is '\D'possible ?
|
|
jne asciiz_dev_offer30
|
|
lodsb
|
|
call toupper
|
|
cmp al,'E' ; is '\DE' on ?
|
|
jne asciiz_dev_offer30
|
|
lodsb
|
|
call toupper
|
|
cmp al,'V' ; is '\DEV' on ?
|
|
jne asciiz_dev_offer30
|
|
lodsb ; finally how about trailing '\'
|
|
call check_slash
|
|
jne asciiz_dev_offer30
|
|
mov al,[si] ; check for delimiter
|
|
asciiz_dev_offer15:
|
|
call check_delim ; if first char = delimiter
|
|
jz asciiz_dev_offer30 ; then it can't be a device
|
|
|
|
mov di,offset name_buf ; build name in scratch FCB
|
|
mov cx,8 ; length of name field
|
|
call parse_one ; parse just the name
|
|
cmp al,'.'
|
|
jnz asciiz_dev_offer20 ; do we have to parse an extention ?
|
|
mov di,offset name_buf+8 ; di -> fcb ext field
|
|
mov cx,3 ; length of ext field
|
|
call parse_one ; parse just extension
|
|
asciiz_dev_offer20:
|
|
test al,al ; if not a NUL by now forget it
|
|
jnz asciiz_dev_offer30
|
|
push ss ! pop ds
|
|
mov si,offset name_buf ; DS:SI -> name
|
|
call check_device_common ; try to find the name
|
|
jnc asciiz_dev_accept ; if we can handle it here
|
|
asciiz_dev_offer30:
|
|
pop ds ; DS back to normal
|
|
push ss ! pop es ; ditto with ES
|
|
ret ; not a device - proceed
|
|
|
|
asciiz_dev_accept:
|
|
;----------------
|
|
; We have found a match in the device at ES:BX
|
|
;
|
|
mov ss:word ptr current_device,bx
|
|
mov ss:word ptr current_device+WORD,es
|
|
pop ds ; DS = SYSDAT again
|
|
pop ax ; discard return address
|
|
call local_disk ; we need the MX
|
|
les bx,ss:current_device ; ES:BX -> device header
|
|
cmp fdos_pb,FD_EXPAND
|
|
je asciiz_dev_accept20
|
|
cmp fdos_pb,4Eh ; is it FD_FFIRST ?
|
|
je asciiz_dev_accept10
|
|
jmp open_dev ; open the device locally
|
|
asciiz_dev_accept10:
|
|
jmp first_dev ; 'find' the device locally
|
|
asciiz_dev_accept20:
|
|
jmp expand_dev ; 'expand' the device locally
|
|
|
|
eject
|
|
chk_no_dev: ; check file is not a character device
|
|
;----------
|
|
; On Entry:
|
|
; info_fcb contains parsed filename
|
|
; On Exit:
|
|
; Don't return if it's a character device
|
|
;
|
|
call check_device ; is this a device ?
|
|
jnc chk_not_dev10
|
|
push ds ! pop es ; ES points to data again
|
|
ret
|
|
chk_not_dev10:
|
|
jmp fdos_ED_ACCESS ; blow caller away
|
|
|
|
check_device:
|
|
;------------
|
|
; On Entry:
|
|
; info_fcb contains parsed filename
|
|
; On Exit:
|
|
; CY set if not found
|
|
; else
|
|
; CY clear if found
|
|
; ES:BX -> device header
|
|
;
|
|
mov si,offset info_fcb+1 ; DS:SI -> name to check
|
|
; jmp check_device_common
|
|
|
|
check_device_common:
|
|
;-------------------
|
|
; On Entry:
|
|
; DS:SI -> 8 byte buffer to check
|
|
; On Exit:
|
|
; CY set if not found
|
|
; else
|
|
; CY clear if found
|
|
; ES:BX -> device header
|
|
;
|
|
push ss ! pop es ; Get the PCMODE Data Segment
|
|
mov bx,offset dev_root ; hence the Device List
|
|
mov ax,si ; keep copy of start in AX
|
|
check_device10:
|
|
test es:DH_ATTRIB[bx],DA_CHARDEV
|
|
jz check_device20 ; skip unless it's a character device
|
|
lea di,DH_NAME[bx] ; ES:DI -> device name
|
|
mov cx,8/2 ; compare file name w/o extension
|
|
repe cmpsw ; compare until CX == 0 or mismatch
|
|
je check_device30
|
|
check_device20:
|
|
mov si,ax ; restore starting address
|
|
les bx,es:DH_NEXT[bx] ; get next device driver
|
|
cmp bx,0FFFFh ; end of the chain ?
|
|
jne check_device10
|
|
stc ; indicate character device not found
|
|
check_device30:
|
|
ret
|
|
|
|
eject
|
|
|
|
no_dir_vol:
|
|
;----------
|
|
test DATTS[bx],DA_DIR+DA_VOLUME
|
|
jnz dir_vol_err ; return error if label or directory
|
|
ret ; else it's O.K.
|
|
dir_vol_err:
|
|
jmp fdos_ED_ACCESS ; return "access denied"
|
|
|
|
eject
|
|
|
|
|
|
get_pb2_drive:
|
|
;-------------
|
|
mov al,byte ptr fdos_pb+2 ; get requested drive code
|
|
dec al ; Get the default drive if
|
|
jns get_pb2_drv1 ; the requested drive is 00
|
|
call current_dsk2al ; AL = default drive
|
|
get_pb2_drv1:
|
|
ret
|
|
|
|
eject
|
|
|
|
mkdir_init:
|
|
;----------
|
|
push ax ; Init 1st block of the directory
|
|
call zeroblk ; zero the block
|
|
pop ax ! push ax ; get the block number
|
|
xor bx,bx ; seek to beginning of cluster
|
|
call fill_dirbuf ; DI -> directory entry
|
|
pop dx ! push dx ; get our own block #
|
|
mov ax,' .' ; this is the "." directory
|
|
call init_dot ; set name, attrib, time, date, block1
|
|
call flush_dirbuf ; copy '.' entry to sector buffer
|
|
pop ax ; get the block number
|
|
mov bx,1 ; do 2nd entry
|
|
call fill_dirbuf ; DI -> directory entry
|
|
call hdsblk
|
|
xchg ax,dx ; DX = parent directory
|
|
mov ax,'..' ; this is the ".." directory
|
|
; call init_dot ; fall into INIT_DOT
|
|
; ret
|
|
|
|
init_dot:
|
|
mov dirp,di ; save directory entry for SETPCD
|
|
push di
|
|
mov DBLOCK1[di],dx ; our own block #
|
|
stosw ; store "." or ".."
|
|
mov al,' '
|
|
mov cx,11-2
|
|
rep stosb ; pad the name with spaces
|
|
mov al,DA_DIR
|
|
stosb ; attribute = directory
|
|
call GetTOD ; get time/date of creation
|
|
pop bx
|
|
jmp stamp_date_and_time ; set date DX and time AX in dir BX
|
|
|
|
eject
|
|
|
|
; Utility functions for RMDIR and UNLINK
|
|
|
|
rmdir_ok: ; make sure directory not in use
|
|
;--------
|
|
;
|
|
|
|
mov bx,dirp ; get the directory entry
|
|
mov ax,DBLOCK1[bx] ; block number of directory
|
|
xor bx,bx ; start at beginning
|
|
rmdir_ok1:
|
|
push ax ! push bx ; save block, offset
|
|
call fill_dirbuf ; locate directory entry
|
|
pop bx ! pop ax ; restore offset, block
|
|
cmp DNAME[di],0 ; is it virgin entry?
|
|
je rmdir_ok4 ; yes, no entries above here
|
|
cmp DNAME[di],0E5h ; is it deleted entry?
|
|
je rmdir_ok3 ; yes, no problems yet...
|
|
cmp DNAME[di],'.' ; is it "." or ".."?
|
|
je rmdir_ok3
|
|
if DELWATCH
|
|
; We have found a dir entry - better check if it is a pending delete
|
|
; and that delwatch is installed. Then we can ignore it.
|
|
test DATTS[di],DA_VOLUME ; is the volume label bit set
|
|
jz rmdir_not_empty ; no, can't be pending delete
|
|
xor dx,dx ; (also sets DH = DELW_RDMASK)
|
|
; cmp dx,DBLOCK1[di] ; is it really a pending delete ?
|
|
; jz rmdir_not_empty ; yes, fall thru to delwatch check
|
|
xchg ax,dx ; AH = DELW_RDMASK, DX = dir cluster
|
|
mov si,di ; -> directory buffer (for DELWATCH)
|
|
callf ss:fdos_stub ; is the delwatch TSR installed
|
|
xchg ax,dx ; AX = dir cluster
|
|
jnc rmdir_ok3 ; delwatch will handle pending deletes
|
|
rmdir_not_empty:
|
|
endif
|
|
mov ax,ED_ACCESS ; return "access denied" if not empty
|
|
|
|
rmdir_inuse: ; directory not empty or in use:
|
|
jmp fdos_error ; return the error
|
|
|
|
rmdir_ok3: ; else this entry O.K.
|
|
inc bx ; check next directory entry
|
|
cmp bx,dirperclu ; cluster completed?
|
|
jb rmdir_ok1 ; loop back if more to come
|
|
call getnblk ; get next block in directory
|
|
sub bx,bx ; start at beginning of block
|
|
cmp ax,lastcl ; end of cluster chain?
|
|
jb rmdir_ok1 ; loop back if not done yet
|
|
rmdir_ok4: ; directory is empty
|
|
mov al,adrive
|
|
call hshdscrd ; discard the hash values
|
|
clc ; "go ahead with RMDIR..."
|
|
ret ; return, ready for the evil deed...
|
|
|
|
|
|
chkcds: ; check if any process uses directory to delete
|
|
;------
|
|
; On Entry:
|
|
; dirp -> directory entry of DIR to check
|
|
; On Exit:
|
|
; CY clear if DIR is in use
|
|
;
|
|
mov bx,dirp
|
|
mov cx,DBLOCK1[bx] ; block number of directory to delete
|
|
mov dl,physical_drv ; get the drive the subdir is in
|
|
mov al,-1 ; start with drive A:
|
|
chkcds10:
|
|
inc ax ; next drive
|
|
call get_ldt_raw ; ES:BX -> LDT for drive
|
|
jc chkcds20 ; bail if if bad drive
|
|
test es:byte ptr LDT_FLAGS+1[bx],(LFLG_NETWRKD+LFLG_PHYSICAL)/100h
|
|
js chkcds10 ; it can't be a network drive
|
|
jz chkcds10 ; it must be a physical drive
|
|
cmp dl,es:LDT_DRV[bx] ; does the drive match?
|
|
jne chkcds10 ; no, don't bother then
|
|
cmp es:LDT_BLK[bx],0ffffh ; is it valid
|
|
jne chkcds19
|
|
push ax
|
|
push cx
|
|
push dx
|
|
call select_logical_drv ; select with media change check
|
|
call rebuild_ldt_curdir ; rebuild LDT_
|
|
pop dx
|
|
pop cx
|
|
pop ax
|
|
chkcds19:
|
|
if 0
|
|
; This didn't make the beta, so leave until the next release
|
|
; We really need to make sure we relog all SUBST's drives before
|
|
; we can be sure this is valid and fail the rmdir
|
|
cmp cx,es:LDT_ROOT[bx] ; is this our root block ?
|
|
je chkcds20 ; (ie. a SUBST'd drive)
|
|
endif
|
|
cmp cx,es:LDT_BLK[bx] ; does the block match
|
|
jne chkcds10 ; no, try next drive
|
|
chkcds20:
|
|
ret
|
|
|
|
|
|
eject
|
|
;
|
|
; Go down one level in directory
|
|
; On Entry:
|
|
; DIRP -> directory to open
|
|
; PATH_DRIVE = drive to use
|
|
; On Exit:
|
|
; AX = fdos_hds_blk (the current directory block)
|
|
; CY clear on success
|
|
; CY set on error
|
|
;
|
|
|
|
open_dir:
|
|
mov bx,dirp
|
|
test DATTS[bx],DA_DIR ; check if directory
|
|
stc
|
|
jz open_dir20 ; fail if this is a file
|
|
cmp word ptr info_fcb+1,'..'
|
|
jne open_dir10 ; watch out if going up a level
|
|
mov ax,fdos_hds_blk ; get current block
|
|
cmp ax,fdos_hds_root ; check if at logical root already
|
|
jne open_dir10 ; and if not carry on
|
|
cmp ax,DBLOCK1[bx] ; if we are already at the virtual root
|
|
stc ; and want to stay there that's OK
|
|
jne open_dir20 ; otherwise return an error
|
|
open_dir10:
|
|
mov al,physical_drv ; remember the drive
|
|
mov fdos_hds_drv,al
|
|
mov ax,DBLOCK1[bx] ; remember this directory block
|
|
mov fdos_hds_blk,ax
|
|
clc ; success
|
|
open_dir20:
|
|
ret
|
|
|
|
|
|
Public hdsblk
|
|
|
|
;======
|
|
hdsblk: ;/* check if we are in subdirectory */
|
|
;======
|
|
;
|
|
; exit: AX = directory block number
|
|
; ZF = set if at root
|
|
; regs: others preserved
|
|
|
|
mov ax,fdos_hds_blk ; get current directory block
|
|
test ax,ax ; set ZF
|
|
ret
|
|
|
|
|
|
|
|
parent2save_area:
|
|
;-----------------
|
|
; On Entry:
|
|
; AX = cluster number of parent to find
|
|
; On Exit:
|
|
; save_area contains parental name (DX = length of name)
|
|
;
|
|
call find_parent ; locate parent directory
|
|
jz path_error ; stop in case we're in a mess
|
|
lea si,DNAME[bx] ; get parent directory name
|
|
mov di,offset save_area ; ES:DI -> scratch area
|
|
; jmp unparse ; make it ASCIIZ file name
|
|
|
|
; build ASCIIZ string from directory entry
|
|
; entry: BX -> directory buffer
|
|
; ES:DI -> output buffer
|
|
; exit: ES:DI -> next byte in buffer
|
|
|
|
unparse:
|
|
;-------
|
|
push di ; save base of name
|
|
mov cx,8 ; remainder of up to 7 characters
|
|
lea si,DNAME[bx] ; SI -> directory name
|
|
call unparse_field ; copy name, strip trailing blanks
|
|
mov al,'.'
|
|
stosb ; add the dot for start of extension
|
|
push di ; remember where extention starts
|
|
mov cx,3 ; copy 3-char extension
|
|
lea si,DNAME+8[bx] ; SI -> directory extention
|
|
call unparse_field ; copy extension, strip trailing blanks
|
|
pop ax ; recover start of extension
|
|
cmp ax,di ; did we generate extension?
|
|
jne unparse1 ; skip if we did
|
|
dec di ; else eat the '.'
|
|
unparse1:
|
|
xor ax,ax
|
|
stosb ; NUL-terminate the name
|
|
pop bx ; ES:BX -> base of name
|
|
cmp es:byte ptr [bx],05h
|
|
jne unparse2 ; if not mapped E5 (deleted entry/Kanji)
|
|
mov es:byte ptr [bx],0E5h ; else map back to E5 for Kanji support
|
|
unparse2:
|
|
ret
|
|
|
|
unparse_field:
|
|
;-------------
|
|
; entry: DS:SI -> disk buffer
|
|
; ES:DI -> ASCIIZ name to build
|
|
; CX = field length
|
|
; On Exit:
|
|
; ES:DI -> end of name
|
|
; BX preserved
|
|
|
|
push si ; save start of field
|
|
add si,cx ; SI -> end of field
|
|
inc cx ; one extra for LOOPE dec
|
|
unprsf10:
|
|
dec si ; lets look at the previous char
|
|
cmp ds:byte ptr [si],' ' ; trailing space ?
|
|
loope unprsf10
|
|
pop si ; SI = start of field
|
|
rep movsb
|
|
ret
|
|
|
|
|
|
path_error:
|
|
jmp fdos_ED_PATH ; return "invalid path" error
|
|
|
|
mkspace_parent:
|
|
;--------------
|
|
; save_area contains the parental name, DX bytes long. We wish to insert it
|
|
; into an ASCIIZ string so make DX bytes of space at ES:DI.
|
|
; On Entry:
|
|
; ES:DI -> ASCIIZ, DX = byte count
|
|
; On Exit:
|
|
; DS:SI -> parents name, CX = length of parent (DX on entry)
|
|
;
|
|
mov al,0 ; find end of name
|
|
mov cx,128 ; max. path length
|
|
repne scasb ; scan for end of path
|
|
neg cx
|
|
add cx,128 ; CX = string length including NUL
|
|
mov ax,cx
|
|
add ax,dx
|
|
cmp ax,64
|
|
ja path_error
|
|
dec di ; ES:DI -> '\0'
|
|
mov si,di ; SI -> source of copy
|
|
add di,dx ; point to beyond insertion
|
|
push ds
|
|
push es ! pop ds ; move string backwards to make space
|
|
std ! rep movsb ! cld ; for directory name
|
|
pop ds
|
|
mov cx,dx ; CX = length of new directory name
|
|
mov si,offset save_area ; SI -> unparsed name
|
|
ret
|
|
|
|
; find parent directory starting with cluster AX
|
|
; entry: AX = cluster of parent to find
|
|
; exit: ZF = 1 if not found (shouldn't happen)
|
|
; -or-
|
|
; ZF = 0 if found, BX=DIRP -> dir entry
|
|
|
|
find_parent:
|
|
mov blk,ax ; save the block number
|
|
push ds ! pop es
|
|
mov di,offset info_fcb+1
|
|
mov ax,'..' ! stosw ; file name is '..'
|
|
mov al,' ' ; pad with spaces
|
|
mov cx,9 ! rep stosb
|
|
call finddfcbf ; find pointer to parent
|
|
jz fndpar2 ; shouldn't happen...
|
|
call open_dir ; go up one level
|
|
jc fndpar3 ; screwed up by security...
|
|
call setenddir ; search from beginning
|
|
fndpar1:
|
|
sub cx,cx
|
|
call getdir ; find next directory entry
|
|
jz fndpar2 ; end of directory
|
|
mov al,DNAME[bx] ; check if deleted file
|
|
cmp al,0E5h
|
|
je fndpar1 ; skip empty slots
|
|
cmp al,0
|
|
je fndpar2 ; end of directory
|
|
test DATTS[bx],DA_DIR ; try to find directory
|
|
jz fndpar1 ; skip plain files
|
|
mov ax,DBLOCK1[bx] ; get starting cluster
|
|
cmp ax,blk
|
|
jne fndpar1
|
|
fndpar3:
|
|
or ax,0FFFFh ; force non-zero condition
|
|
fndpar2:
|
|
ret ; ZF = 0 if found
|
|
|
|
eject
|
|
path_prep_chk:
|
|
;-------------
|
|
; Run down the path and parse final name
|
|
; exit: ds:dx -> info_fcb parsed at path end
|
|
|
|
call path_prep ; prepare the path
|
|
call chk_no_dev ; devices not allowed
|
|
chk_no_dot_or_wild:
|
|
;------------------
|
|
call chk_no_dot ; no subdirs entries either
|
|
; jmp chk_no_wild ; wild cards not allowed
|
|
|
|
eject
|
|
chk_no_wild: ; make sure path doesn't contain wild cards
|
|
;----------- ; (or is all spaces)
|
|
call check_no_wild ; error if any wildcards
|
|
jne check_no_wild_ret ; or if all spaces
|
|
jmp fdos_ED_FILE ; return "invalid filename"
|
|
|
|
check_no_wild: ; make sure path doesn't contain wild cards
|
|
;------------- ; (or is all spaces) ZF set on problem
|
|
push es
|
|
push ds ! pop es ; ES -> SYSDAT
|
|
mov di,offset info_fcb+1
|
|
mov cx,11
|
|
mov al,'?' ; scan for wild cards
|
|
repne scasb
|
|
je check_no_wild_exit ; skip if wild cards found
|
|
mov di,offset info_fcb+1
|
|
mov cx,11
|
|
mov al,' ' ; scan for all spaces
|
|
repe scasb ; ZF set if a problem
|
|
check_no_wild_exit:
|
|
pop es
|
|
check_no_wild_ret:
|
|
ret
|
|
|
|
|
|
chk_for_root:
|
|
;------------
|
|
; On Entry:
|
|
; info_fcb -> name of failed search
|
|
; fdos_hds -> dir we searched in
|
|
; On Exit:
|
|
; ZF set if a search for root (or '.' in root)
|
|
;
|
|
cmp fdos_hds_blk,0 ; are we in the root ?
|
|
jne chk_for_root10 ; no, no further checks required
|
|
push ds ! pop es
|
|
mov di,offset info_fcb+1
|
|
mov al,'.' ; check for root
|
|
scasb ; is it a '.' entry ?
|
|
jne chk_for_root10
|
|
mov cx,8+3-1
|
|
mov al,' '
|
|
repe scasb ; is it all spaces ?
|
|
chk_for_root10:
|
|
ret
|
|
|
|
|
|
; Parse a pathname into an info_fcb
|
|
; entry: es:si -> asciiz string
|
|
; AX = drive code
|
|
; exit: es:si -> next asciiz name in path
|
|
; dx -> fcb
|
|
; CY clear, AL = 0 if end of string
|
|
; CY set, AX = error code
|
|
|
|
parse_path:
|
|
;----------
|
|
push ds ! push es
|
|
pop ds ! pop es
|
|
|
|
call clear_info_fcb ; initialise to blanks and drive AL
|
|
|
|
mov dx,offset info_fcb ; use a scratch fcb
|
|
mov di,dx ; dx saves initial di
|
|
inc di
|
|
|
|
mov ax,[si] ; check first two chars
|
|
cmp al,'.' ; special case: if name = '.'
|
|
jne parse_path20 ; then we parse it differently
|
|
movsb ; copy the '.'
|
|
cmp ah,'.' ; special case: if name = '..'
|
|
jne parse_path10 ; then we parse it differently
|
|
movsb ; copy '..'
|
|
parse_path10:
|
|
lodsb ; get next char
|
|
cmp al,' ' ; skip all spaces
|
|
je parse_path10
|
|
jmps parse_path30 ; now exit as normal
|
|
|
|
parse_path20:
|
|
call check_delim ; if first char = delimeter
|
|
je parse_path30 ; then only allow '\'
|
|
|
|
; filename begins with a legal char, parse it normally
|
|
|
|
mov di,dx
|
|
inc di ; di -> fcb name field
|
|
mov cx,8 ; length of name field
|
|
call parse_one ; parse just the name
|
|
mov di,dx ; DI -> FCB
|
|
cmp es:byte ptr 1[di],0E5h ; is first character E5?
|
|
jne parse_path30 ; skip if not
|
|
mov es:byte ptr 1[di],05h ; else make it internal synonym
|
|
parse_path30:
|
|
cmp al,'.'
|
|
jne parse_path40 ; skip if no extension
|
|
add di,9 ; di -> fcb ext field
|
|
mov cx,3 ; length of ext field
|
|
call parse_one ; parse just extension
|
|
parse_path40:
|
|
if PASSWORD
|
|
cmp al,';' ; check if password specified
|
|
jne parse_path50 ; skip if no password
|
|
mov di,offset password_buffer
|
|
mov cx,8 ; length of password field
|
|
call parse_one ; parse just password
|
|
push ax
|
|
push ds ! push si
|
|
push ss ! pop ds ; DS:SI -> ASCII password
|
|
mov si,offset password_buffer
|
|
call hash_pwd ; AX = encrypted password
|
|
mov local_password,ax ; remember it in case we need it
|
|
pop si ! pop ds
|
|
pop ax
|
|
endif
|
|
parse_path50:
|
|
test al,al ; a NUL is OK
|
|
jz parse_path90
|
|
call check_slash ; if terminator != '\' or '/',
|
|
stc ; assume an error
|
|
jne parse_path80 ; report it if so
|
|
parse_path60:
|
|
lodsb ; get next character
|
|
call check_delim ; we expect a normal character
|
|
jne parse_path80 ; here - exit if we've got one
|
|
call check_slash ; swallow '\'s at this point and leave
|
|
je parse_path60 ; other delimiters for next time
|
|
cmp al,'.' ; trailing '\.' ?
|
|
jne parse_path75
|
|
mov cx,si ; remember position of '.'
|
|
parse_path70:
|
|
lodsb ; now discard trailing spaces
|
|
cmp al,' '
|
|
je parse_path70 ; keep going until we lose all spaces
|
|
test al,al ; stop at a NUL
|
|
jz parse_path50
|
|
call check_slash ; if it's a '\' try again
|
|
je parse_path50
|
|
mov si,cx ; retract to the '.'
|
|
parse_path75:
|
|
mov al,'\' ; return '\' as the delimiter
|
|
clc ; and exit with no problems
|
|
parse_path80:
|
|
dec si ; retract a byte (CY unaffected)
|
|
parse_path90:
|
|
push ds ! push es
|
|
pop ds ! pop es
|
|
ret
|
|
|
|
|
|
|
|
Public parse_one
|
|
|
|
; Parse a single name or extension
|
|
; On Entry:
|
|
; DS:SI -> asciiz name
|
|
; ES:DI -> start of fcb field
|
|
; CX = field size
|
|
; On Exit:
|
|
; AL = last char parsed
|
|
;
|
|
; nb. make no assumptions about DS and ES
|
|
;
|
|
|
|
parse_one:
|
|
lodsb ; grab asciiz char
|
|
cmp al,'*' ; if char = *, then fill
|
|
jz parse_one_wild ; rest of field with '?'
|
|
call check_delim ; if char is not delimiter,
|
|
jnz parse_one_char ; then move it to fcb
|
|
ret ; if delimiter, return
|
|
|
|
parse_one_wild:
|
|
mov al,'?'
|
|
rep stosb ; after filling
|
|
jmps parse_one_ignore ; skip until a delimiter
|
|
|
|
parse_one_char:
|
|
if KANJI
|
|
call dbcs_lead ; is it 1st byte of Kanji pair?
|
|
jnz parse_one_skip ; skip if straight 8-bit
|
|
inc si ; assume both chars discarded
|
|
dec cx ; we will copy 2 bytes
|
|
jcxz parse_one_ignore ; ignore both if only room for one
|
|
stosb ; thats the first byte
|
|
dec si ; point at 2nd again
|
|
lodsb ; get the 2nd byte
|
|
parse_one_skip:
|
|
endif
|
|
stosb ; send char to fcb
|
|
loop parse_one ; get another character from ASCIIZ string
|
|
parse_one_ignore:
|
|
lodsb
|
|
call check_delim ; ignore up to next delimiter
|
|
jnz parse_one_ignore
|
|
ret
|
|
|
|
;
|
|
;
|
|
; Check for a path name delimiter
|
|
; entry: AL = ASCIIZ char
|
|
; exit: all registers preserved
|
|
; ZF = 1 if char is a delimeter
|
|
; ZF = 0 if char is legal in file names
|
|
|
|
Public check_delim
|
|
|
|
check_delim:
|
|
;-----------
|
|
cmp al,' ' ; if any printable char,
|
|
jae check_delim_char ; then skip
|
|
cmp al,al ; set zf
|
|
ret
|
|
|
|
check_delim_char:
|
|
if KANJI
|
|
call dbcs_lead ; if it's 1st of kanji pair
|
|
jne check_delim10 ; DON'T upper case it
|
|
test al,al ; clear zf
|
|
ret ; (should really check the 2nd byte)
|
|
check_delim10:
|
|
endif
|
|
call toupper ; make it upper case
|
|
push es ! push di ! push cx
|
|
push cs ! pop es
|
|
lea di,delim_string ; es:di -> delimeters
|
|
mov cx,length delim_string
|
|
cld
|
|
repnz scasb ; match al against the list
|
|
pop cx ! pop di ! pop es
|
|
clc ; never return cf set
|
|
ret ; with zf set by scasb
|
|
|
|
delim_string db ':.;,=+\<>|/"[]' ; DOS delimeters
|
|
|
|
|
|
; Check AX for '\\'
|
|
|
|
Public check_dslash
|
|
|
|
check_dslash:
|
|
xchg al,ah
|
|
call check_slash
|
|
xchg al,ah
|
|
jne check_slash_done
|
|
; jmp check_slash
|
|
|
|
; Check delimeter character for '\' or '/'
|
|
; entry: al = char
|
|
; exit: zf = 1 if either slash
|
|
|
|
check_slash:
|
|
cmp al,'\' ; if first char is a backslash
|
|
jz check_slash_done ; or a frontslash, then
|
|
cmp al,'/' ; return with zf set
|
|
check_slash_done:
|
|
clc ; never return cf set
|
|
ret
|
|
|
|
; Convert character to upper case
|
|
; WARNING - may be called with DS <> SYSDAT
|
|
|
|
toupper:
|
|
;-------
|
|
test al,al
|
|
js toupper_intl
|
|
cmp al,'a'
|
|
jb isupper
|
|
cmp al,'z'
|
|
ja isupper
|
|
sub al,'a'-'A'
|
|
isupper:
|
|
ret
|
|
|
|
toupper_intl:
|
|
callf ss:intl_xlat ; call international upper case vector
|
|
ret
|
|
|
|
eject
|
|
kill_file: ; release clusters for file/dir and delete entry
|
|
;---------
|
|
mov bx,dirp ; get pointer to directory entry
|
|
if DELWATCH
|
|
call hdsblk ; AX = directory root cluster
|
|
xchg ax,dx ; DX = dir cluster
|
|
mov cx,dcnt ; CX = directory index for entry
|
|
mov ah,DELW_DELETE ; we are about to delete this dir
|
|
mov al,physical_drv ; directory entry so give delwatch
|
|
callf ss:fdos_stub ; a chance to make it pending delete
|
|
jnc kill_file10 ; delwatch took it - just update dir
|
|
endif
|
|
mov al,0E5h ; deleted file mark
|
|
xchg al,DNAME[bx] ; delete the directory entry
|
|
mov DUNDEL[bx],al ; save 1st letter for UNDEL command
|
|
mov ax,DBLOCK1[bx] ; get starting block #
|
|
call delfat ; release all clusters
|
|
kill_file10:
|
|
jmp flush_dirbuf ; update the directory
|
|
; done it! (DIR/FAT still dirty)
|
|
eject
|
|
|
|
mustbe_nolbl:
|
|
;------------
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; Only returns if no label exists
|
|
; forces us to root of drive
|
|
;
|
|
push ds ! pop es ; ES = DS for string ops
|
|
mov si,offset info_fcb+1
|
|
mov di,offset save_area ; SI->search name, DI->save area
|
|
mov cx,11
|
|
push di ; save save_area
|
|
push si ; save info_fcb+1
|
|
push cx ; save length
|
|
rep movsb ; copy search name into save area
|
|
pop cx ; CX = length (11)
|
|
pop di ; DI = info_fcb+1
|
|
push di
|
|
push cx
|
|
mov al,'?' ; now fill info_fcb with wildcards
|
|
rep stosb
|
|
call find_labelf ; look for a volume label
|
|
pop cx ; CX = length (11)
|
|
pop di ; DI = info_fcb+1
|
|
pop si ; SI = save_area
|
|
push ds ! pop es ; ES = DS for string ops
|
|
rep movsb ; restore info_fcb
|
|
jnz mustbe_nolbl10 ; if we found a label bail out
|
|
ret
|
|
mustbe_nolbl10:
|
|
jmp fdos_ED_ACCESS ; return access denied
|
|
|
|
find_labelf: ; find label only
|
|
;----------- ; forces us to root
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; ZF clear if volume label found
|
|
; dirp/dcnt tell where label is
|
|
;
|
|
call setenddir ; start from beginning
|
|
; jmp find_label
|
|
|
|
find_label: ; find label only
|
|
;---------- ; forces us to root
|
|
; On Entry:
|
|
; dcnt -> location to search from
|
|
; On Exit:
|
|
; ZF clear if volume label found
|
|
; dirp/dcnt tell where label is
|
|
;
|
|
mov chdblk,0 ; don't assume sequential access
|
|
mov fdos_hds_blk,0 ; look for labels in the root
|
|
mov finddfcb_mask,000ffh ; return VOL labels, not pending dels
|
|
find_label30:
|
|
call finddfcb ; find matching file name
|
|
jz find_label40 ; skip if not found
|
|
test DATTS[bx],DA_VOLUME
|
|
jz find_label30 ; try again if not a volume label
|
|
find_label40:
|
|
mov finddfcb_mask,DA_VOLUME*256
|
|
ret ; back to no VOL labels or pending dels
|
|
|
|
if UNDELETE
|
|
find_pending_delete: ; find pending delete only
|
|
;-------------------
|
|
; On Entry:
|
|
; dcnt -> location to search from
|
|
; On Exit:
|
|
; ZF clear if pending delete entry found
|
|
; dirp/dcnt tell where entry is
|
|
;
|
|
mov finddfcb_mask,0h ; return pending delete entries
|
|
find_pending_delete10:
|
|
mov al,05h ; replace 1st char with 05h
|
|
xchg al,info_fcb+1 ; saving char we really want
|
|
push ax
|
|
call finddfcb ; find matching file name
|
|
pop ax
|
|
mov info_fcb+1,al ; restore original 1st letter
|
|
jz find_pending_delete30 ; skip if not found
|
|
test DATTS[bx],DA_VOLUME ; Is it a pending delete entry
|
|
jz find_pending_delete10 ; No, try again if not a volume label
|
|
cmp word ptr DBLOCK1[bx],0 ; Is this a pending delete entry
|
|
jz find_pending_delete10 ; No, try again if not correct
|
|
cmp al,'?' ; wildcard is OK
|
|
je find_pending_delete20
|
|
cmp al,DUNDEL[bx] ; does saved char match what we want?
|
|
jne find_pending_delete10
|
|
find_pending_delete20:
|
|
and DATTS[bx],not DA_VOLUME ; mask out volume bit
|
|
mov al,DUNDEL[bx] ; move deleted character to normal
|
|
mov DNAME[bx],al ; position for return
|
|
or al,al ; clear the zero flag (assumes al!=0)
|
|
find_pending_delete30:
|
|
mov finddfcb_mask,DA_VOLUME*256
|
|
ret ; back to no VOL labels or pending dels
|
|
|
|
endif
|
|
|
|
eject
|
|
find_xfn: ; find spare external file number
|
|
;--------
|
|
; exit: DI = new handle
|
|
|
|
|
|
push es ; save ES while we play
|
|
xor di,di ; return handle 0 if don't have PSP
|
|
call get_xftptr
|
|
jc fndxfn2
|
|
mov bx,di ; save the offset
|
|
mov al,0FFh ; look for unused slot
|
|
repne scasb ; loop while CX != 0 and *ES:DI != 0FFh
|
|
jne fndxfn3 ; ZF = 1 if match, else none found
|
|
dec di ; DI = matching offset
|
|
sub di,bx ; DI = handle index
|
|
fndxfn2:
|
|
pop es
|
|
clc ; indicate no error
|
|
ret
|
|
|
|
fndxfn3:
|
|
pop es
|
|
stc ; indicate an error
|
|
ret
|
|
|
|
Public alloc_dhndl
|
|
|
|
alloc_dhndl:
|
|
;-----------
|
|
; provisionally allocate a spare DHNDL_, do not return without one
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; AX = IFN of handle
|
|
; ES:BX -> DHNDL_ structure
|
|
; (All other regs preserved)
|
|
call find_dhndl ; try to find a spare DHNDL_
|
|
jc fdos_ED_HANDLE ; bail out if we can't
|
|
ret
|
|
|
|
mustbe_free_handle:
|
|
;------------------
|
|
; return an error to user if either an XFN or an IFN is unavailable
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; None
|
|
;
|
|
call alloc_dhndl ; make sure we can allocate a DHNDL
|
|
mov es:DHNDL_COUNT[bx],0 ; free it in case open/creat fails
|
|
; (we are protected by local_disk)
|
|
; jmp alloc_xfn ; make sure an XFN is also free
|
|
|
|
Public alloc_xfn
|
|
|
|
alloc_xfn: ; find spare external file number
|
|
;--------
|
|
; exit: DI = new handle
|
|
|
|
call find_xfn ; try to find spare slot in XFT
|
|
jc fdos_ED_HANDLE ; "out of handles" if no luck
|
|
mov fdos_ret,di ; else save the resulting handle
|
|
ret
|
|
|
|
|
|
fdos_ED_HANDLE:
|
|
mov ax,ED_HANDLE ; out of user file #s, all 20 in use
|
|
jmp fdos_error
|
|
|
|
|
|
; Allocate & initialize file handle:
|
|
; entry: AX = open mode
|
|
; DIRP -> directory entry
|
|
; exit: ES:BX = CURRENT_DHNDL = file handle
|
|
; AX = fdos_ret = IFN
|
|
; -or-
|
|
; System call fails with ED_HANDLE
|
|
|
|
open_handle:
|
|
;-----------
|
|
mov bx,dirp ; else get directory entry
|
|
test DATTS[bx],DA_RO ; check if file is r/o - if so
|
|
jz creat_handle ; make handle r/o too
|
|
and ax,not DHM_RWMSK
|
|
creat_handle:
|
|
;------------
|
|
; entry point for create file - when you create a read-only file
|
|
; you still get a handle you can write with !
|
|
push ax ; save open mode
|
|
xchg ax,si ; SI = open mode
|
|
mov di,S_OM_COMPAT ; check if open/sharing modes are compatible
|
|
call check_with_share ; does SHARE approve ?
|
|
call alloc_dhndl ; allocate a DHNDL_ structure
|
|
mov fdos_ret,ax ; remember IFN in case it's FCB
|
|
pop dx ! push dx ; DX = open mode
|
|
push es ! push bx ; save DHNDL_ pointer
|
|
test dh,DHM_FCB/100h ; FCB call?
|
|
jne creat_handle10 ; skip XFN allocation if FCB
|
|
push ax ; save IFN
|
|
call alloc_xfn ; allocate spare XFN
|
|
pop ax ; recover IFN
|
|
mov bx,di ; BX = XFN
|
|
call get_xftptr ; ES:DI -> user file table
|
|
jc creat_handle10 ; skip if we don't have one
|
|
mov es:[di+bx],al ; set IFN in PSP
|
|
creat_handle10:
|
|
pop bx ! pop es
|
|
lea di,DHNDL_COUNT[bx] ; point at open count
|
|
mov ax,1
|
|
stosw ; open by one
|
|
pop ax ; recover open mode
|
|
mov cx,DHAT_TIMEOK+DHAT_CLEAN
|
|
test al,DHM_LOCAL ; is it private ?
|
|
jz creat_handle20
|
|
or ch,DHAT_LOCAL/256 ; rememmber it's local
|
|
and al,not DHM_LOCAL ; clear inherit bit
|
|
creat_handle20:
|
|
; lea di,DHNDL_MODE[bx] ; update the mode
|
|
stosw
|
|
mov si,dirp
|
|
mov al,DATTS[si] ; get file attribute byte
|
|
lea di,DHNDL_DATRB[bx] ; now copy file attribute
|
|
stosb ; to DHNDL_
|
|
xchg ax,cx ; AX = attributes
|
|
or al,physical_drv ; get physical drive
|
|
; lea di,DHNDL_WATTR[bx] ; make as clean disk file
|
|
stosw
|
|
; lea di,DHNDL_DEVOFF[bx] ; ES:DI -> dd entry in DHNDL_
|
|
mov ax,ss:word ptr current_ddsc
|
|
stosw ; point to DDSC_
|
|
mov ax,ss:word ptr current_ddsc+WORD
|
|
stosw
|
|
mov ax,DBLOCK1[si] ; get starting cluster of file
|
|
; lea di,DHNDL_BLK1[bx]
|
|
stosw
|
|
lea si,DTIME[si]
|
|
; lea di,DHNDL_TIME[bx]
|
|
movsw ; copy the time
|
|
; lea di,DHNDL_DATE[bx]
|
|
movsw ; and the date
|
|
lodsw ; skip 1st cluster (already done)
|
|
; lea di,DHNDL_SIZE[bx]
|
|
movsw ! movsw ; copy the file size
|
|
; lea di,DHNDL_POS[bx]
|
|
xor ax,ax ; zero current position
|
|
stosw ! stosw
|
|
; lea di,DHNDL_IDX[bx]
|
|
stosw ; zero block index
|
|
if DOS5
|
|
call hdsblk ; get directory block
|
|
; lea di,DHNDL_DBLK[bx]
|
|
stosw
|
|
xor ax,ax
|
|
stosw
|
|
else
|
|
; lea di,DHNDL_BLK[bx]
|
|
stosw ; and current block
|
|
call hdsblk ; get directory block
|
|
; lea di,DHNDL_DBLK[bx]
|
|
stosw
|
|
endif
|
|
mov ax,dcnt ; set DCNT of file
|
|
; lea di,DHNDL_DCNTLO[bx]
|
|
stosb ; store low byte of DCNT
|
|
mov es:DHNDL_DCNTHI[bx],ah ; and hi byte
|
|
; lea di,DHNDL_NAME[bx] ; copy name from dir entry
|
|
mov si,dirp
|
|
mov cx,11
|
|
rep movsb
|
|
xor ax,ax
|
|
stosw ; zero DWORD
|
|
stosw
|
|
lea di,DHNDL_SHARE[bx] ; zero sharing record
|
|
stosw
|
|
if DOS5
|
|
stosw ! stosw ! stosw ; zero DHNDL_BLK + IFS
|
|
endif
|
|
callf ss:share_stub+S_OPEN ; we have opened this handle
|
|
; ask SHARE to register it
|
|
jc create_handle30
|
|
mov ax,fdos_ret ; AX = handle to return
|
|
ret
|
|
create_handle30: ; free the handle again
|
|
push ax
|
|
mov ax,fdos_ret
|
|
call release_handle2
|
|
pop ax
|
|
jmp fdos_error
|
|
|
|
|
|
|
|
Public verify_handle
|
|
|
|
verify_handle:
|
|
;-------------
|
|
; On Exit:
|
|
; ES:BX = DHNDL_
|
|
; CY set if bad file handle (nb. device handle is bad)
|
|
;
|
|
call check_handle ; make sure we can access it
|
|
jc vfy_hnd9 ; return if character device
|
|
|
|
select_handle: ; select directory of current handle
|
|
;-------------
|
|
; On Entry:
|
|
; ES:BX -> DHNDL_
|
|
; On Exit:
|
|
; ES:BX preserved
|
|
;
|
|
mov al,es:DHNDL_ATTR[bx] ; get physical drive
|
|
and al,DHAT_DRVMSK ; from attrib field
|
|
push es ! push bx
|
|
call select_physical_drv ; select the drive
|
|
pop bx ! pop es
|
|
mov ax,es:DHNDL_DBLK[bx]
|
|
mov fdos_hds_blk,ax ; copy HDS_BLK
|
|
clc ; handle is OK file
|
|
vfy_hnd9:
|
|
ret ; good handle
|
|
|
|
|
|
; Checks if parameter is a legal file handle:
|
|
; Entry: CURRENT_DHNDL = handle to verify
|
|
; Exit: ES:BX = DHNDL_ if O.K.
|
|
; CY clear if local disk file
|
|
; CY set if device/network handle
|
|
; Note: doesn't return on error
|
|
|
|
check_handle:
|
|
;------------
|
|
les bx,current_dhndl
|
|
test es:DHNDL_WATTR[bx],DHAT_REMOTE+DHAT_DEV
|
|
stc ; assume device/network file
|
|
jnz chkhnd10 ; return with CY = 0 if disk file
|
|
callf ss:share_stub+S_UPDATE ; make sure DHNDL_ info valid
|
|
jc chkhnd20 ; if not close it down
|
|
chkhnd10:
|
|
ret
|
|
|
|
chkhnd20:
|
|
call get_xftptr ; ES:BX -> XFT
|
|
jc fdos_ED_H_MATCH ; skip if not handle
|
|
mov bx,fdos_pb+2 ; get XFN so we can poke
|
|
mov es:byte ptr [di+bx],0ffh
|
|
; PSP handle to closed
|
|
; jmp fdos_ED_H_MATCH ; return "invalid handle"
|
|
|
|
fdos_ED_H_MATCH:
|
|
mov ax,ED_H_MATCH ; "invalid handle"
|
|
jmp fdos_error ; return an error
|
|
|
|
public vfy_dhndl_ptr
|
|
|
|
vfy_dhndl_ptr:
|
|
;=============
|
|
; Verifies file handles at FDOS_xxxx calling level
|
|
; before MXdisk is locked.
|
|
; On Entry:
|
|
; SS:BP -> func #, parm off, parm seg
|
|
; stack holds two ret addresses + above values
|
|
; On Exit:
|
|
; ES:BX -> DHNDL_
|
|
|
|
mov si,2[bp] ; SI -> parameter block
|
|
mov ax,2[si] ; get file handle from parameter block
|
|
test ss:byte ptr remote_call+1,DHM_FCB/100h; if we are doing an FCB operation
|
|
jnz vfy_dhndl10 ; deal only with IFN, forget PSP
|
|
mov es,ss:current_psp
|
|
cmp ax,PSP_XFNMAX ; CX = # entries in table
|
|
jae vfy_dhndl_err
|
|
les di,PSP_XFTPTR ; ES:DI -> user file table
|
|
mov bx,ax ; get user file number (0-19)
|
|
mov al,es:[bx+di] ; get IFN for this handle
|
|
vfy_dhndl10:
|
|
cmp al,0ffh ; invalid handle?
|
|
je vfy_dhndl_err ; 00h-FEh only
|
|
les bx,ss:file_ptr ; get the address of the first entry
|
|
vfy_dhndl20:
|
|
cmp ax,es:DCNTRL_COUNT[bx] ; handle in this block?
|
|
jae vfy_dhndl50 ; skip to try next block if not
|
|
mov ah,DHNDL_LEN
|
|
mul ah
|
|
add ax,DCNTRL_LEN ; skip the header
|
|
add bx,ax ; add to start of structure
|
|
mov cx,es:DHNDL_COUNT[bx] ; fail if the handle is not in use
|
|
jcxz vfy_dhndl_err
|
|
inc cx ; FFFF = pre-allocated is also failed
|
|
jz vfy_dhndl_err
|
|
cmp ss:WindowsHandleCheck,26h
|
|
; have we been patched
|
|
jne vfy_dhndl30 ; yes, don't check owner
|
|
mov ax,ss:machine_id ; get current process
|
|
cmp ax,es:DHNDL_UID[bx] ; are we the owning process
|
|
jne vfy_dhndl_err ; no, return an error
|
|
vfy_dhndl30:
|
|
mov ss:word ptr current_dhndl,bx
|
|
mov ss:word ptr current_dhndl+WORD,es
|
|
test es:DHNDL_MODE[bx],DHM_NOCRIT
|
|
jnz vfy_dhndl40 ; are critical errors allowed ?
|
|
ret
|
|
|
|
vfy_dhndl40:
|
|
or valid_flg,NO_CRIT_ERRORS
|
|
ret ; remember no critical errors possible
|
|
|
|
vfy_dhndl50:
|
|
sub ax,es:DCNTRL_COUNT[bx] ; update the internal file number
|
|
les bx,es:DCNTRL_DSADD[bx] ; get the next entry and check
|
|
cmp bx,0FFFFh ; for the end of the list
|
|
jnz vfy_dhndl20
|
|
vfy_dhndl_err:
|
|
add sp,WORD ; pop return addr - return to caller
|
|
mov bx,ED_H_MATCH ; with "invalid handle" error
|
|
ret
|
|
|
|
|
|
|
|
|
|
; On Entry:
|
|
; FDOS_PB+2 = external file handle to release
|
|
; On Exit:
|
|
; ES:BX -> DHNDL_
|
|
|
|
release_handle:
|
|
;--------------
|
|
mov ax,fdos_pb+2 ; get user file number (0-19)
|
|
release_handle2:
|
|
;---------------
|
|
call get_xftptr ; ES:DI -> XFN table
|
|
jc release_ifn ; IFN = XFN if no PSP
|
|
cmp ax,cx ; more than in handle table?
|
|
jae rlshnd_err ; return error if too large
|
|
xchg ax,bx ; BX = external file number
|
|
mov al,0FFh ; get old IFN, release XFN
|
|
xchg al,es:[bx+di] ; get IFN for this handle
|
|
cmp al,0FFh
|
|
je rlshnd_err
|
|
release_ifn:
|
|
call ifn2dhndl ; ES:BX -> DHNDL_
|
|
jnc check_no_dir_ret ; return if no error
|
|
|
|
rlshnd_err: ; else bad handle
|
|
jmp fdos_ED_H_MATCH ; return the error
|
|
|
|
|
|
eject
|
|
check_no_dir: ; check if entry is directory
|
|
;--------
|
|
; entry: BX -> directory entry
|
|
|
|
test DATTS[bx],DA_DIR ; test if directory
|
|
jnz chk_ro_err ; skip if a directory
|
|
check_no_dir_ret:
|
|
ret ; else return to caller
|
|
|
|
check_ro: ; check if file write protected
|
|
;--------
|
|
; entry: BX -> directory entry
|
|
|
|
test DATTS[bx],DA_RO ; test if file r/o
|
|
jnz chk_ro_err ; skip if file r/o
|
|
ret ; else return to caller
|
|
chk_ro_err:
|
|
jmp fdos_ED_ACCESS ; return "access denied" if r/o
|
|
|
|
|
|
|
|
discard_files: ; discard all handles on adrive
|
|
;-------------
|
|
mov al,adrive
|
|
callf ss:share_stub+S_DISCARD ; tell share to forget about files
|
|
ret
|
|
|
|
|
|
|
|
; Close file if it's us in compatibility modes
|
|
|
|
close_if_same_psp:
|
|
;-----------------
|
|
; Note: We first check if the file is open by anyone other
|
|
; than the same UID and PSP, or is open in shared mode.
|
|
; In either case we deny the operation.
|
|
; Otherwise we fall through and close the file.
|
|
; We could do it in one with a new share, but this way means
|
|
; less changes.
|
|
;
|
|
mov di,S_DENY_IF_OPEN ; check if file already open
|
|
call check_with_share ; and stop if it is
|
|
; jmp close_if_open
|
|
|
|
; Make sure our file is not open
|
|
|
|
close_if_open:
|
|
;-------------
|
|
; entry: HDSADR->BLK = block # of directory
|
|
; HDSADR->DRV = drive number
|
|
; DCNT = directory position
|
|
; Note: If the file is open by any other process,
|
|
; error ED_SHAREFAIL is returned from the system call
|
|
; If open by us (same UID, any PSP) in compatibility mode,
|
|
; close it and allow us to proceed.
|
|
|
|
mov di,S_CLOSE_IF_OPEN ; check if file already open
|
|
; jmps check_with_share
|
|
|
|
check_with_share:
|
|
;----------------
|
|
; On Entry:
|
|
; DI = S_ share query
|
|
; On Exit:
|
|
; Come back if share says it's OK.
|
|
;
|
|
call hdsblk ; get directory block in AX
|
|
xchg ax,dx ; directory block
|
|
mov cx,dcnt ; get the directory count in CX
|
|
mov al,physical_drv ; get physical drive in AL
|
|
callf ss:share_stub[di] ; ask SHARE if it knows of anyone open
|
|
jc check_with_share10
|
|
ret ; must be OK.
|
|
|
|
check_with_share10:
|
|
jmp fdos_error ; bail out with an error
|
|
|
|
|
|
eject
|
|
|
|
eject
|
|
;
|
|
; Return a pointer to the DOS Handle corresponding to the internal
|
|
; handle number passed in AX
|
|
;
|
|
; On Entry:
|
|
; AL = IFN
|
|
;
|
|
; On Exit:
|
|
; ES:BX -> DOS handle (All other Regs preserved)
|
|
; CY set if no corresponding valid DHNDL_
|
|
;
|
|
Public ifn2dhndl
|
|
ifn2dhndl:
|
|
push ax
|
|
xor ah,ah ; make IFN a word
|
|
les bx,ss:file_ptr ; get the address of the first entry
|
|
|
|
ifn2dh10:
|
|
cmp ax,es:DCNTRL_COUNT[bx] ; handle in this block?
|
|
jae ifn2dh20 ; skip if not
|
|
mov ah,DHNDL_LEN ; calculate offset of the DOS Handle
|
|
mul ah
|
|
add bx,ax ; add structure offset (should be 0)
|
|
add bx,DCNTRL_LEN ; and then skip the header
|
|
pop ax
|
|
clc
|
|
ret ; ES:BX -> valid DHDNL_
|
|
|
|
ifn2dh20:
|
|
sub ax,es:DCNTRL_COUNT[bx] ; update the internal file number
|
|
les bx,es:DCNTRL_DSADD[bx] ; get the next entry and check
|
|
cmp bx,0FFFFh ; for the end of the list
|
|
jnz ifn2dh10
|
|
ifn2dh15:
|
|
pop ax
|
|
stc
|
|
ret ; invalid file handle number
|
|
|
|
|
|
|
|
;
|
|
; Allocate a DHNDL_ structure
|
|
;
|
|
; On Entry:
|
|
; None
|
|
;
|
|
; On Exit:
|
|
; CY clear if handle allocated
|
|
; AX = IFN of handle
|
|
; ES:BX -> DOS handle
|
|
; (All other Regs preserved)
|
|
;
|
|
Public find_dhndl
|
|
find_dhndl:
|
|
push cx ! push dx ! push di ! push si
|
|
mov ax,ss:machine_id ; get current process
|
|
mov dx,ss:owning_psp ; DX = owining PSP
|
|
xor si,si ; SI = IFN
|
|
les di,ss:file_ptr ; get the address of the first entry
|
|
find_dh10:
|
|
mov cx,es:DCNTRL_COUNT[di] ; get # handles in this block
|
|
jcxz find_dh40 ; skip if none
|
|
lea bx,DCNTRL_LEN[di] ; ES:BX -> 1st DHNDL_
|
|
find_dh20:
|
|
push cx
|
|
cli ; be alone while looking at handles
|
|
mov cx,es:DHNDL_COUNT[bx]
|
|
jcxz find_dh50 ; if handle free grab it
|
|
inc cx ; FFFF = allocated but unused
|
|
jnz find_dh30
|
|
cmp ax,es:DHNDL_UID[bx] ; was it allocated to us
|
|
jne find_dh30
|
|
cmp dx,es:DHNDL_PSP[bx] ; if so use it again
|
|
je find_dh60
|
|
find_dh30:
|
|
sti ; finished with this handle
|
|
pop cx
|
|
inc si ; onto next IFN
|
|
cmp si,0FFh ; only handles 00-FE are valid
|
|
jae find_dh45 ; so bail if out of range
|
|
add bx,DHNDL_LEN ; onto next handle in the block
|
|
loop find_dh20
|
|
find_dh40:
|
|
les di,es:DCNTRL_DSADD[di] ; get the next entry and check
|
|
cmp di,0FFFFh ; it's valid
|
|
jne find_dh10
|
|
find_dh45:
|
|
stc ; no more handles,
|
|
jmps find_dh70 ; exit in failure..
|
|
|
|
find_dh50:
|
|
mov es:DHNDL_COUNT[bx],0FFFFh
|
|
mov es:DHNDL_UID[bx],ax ; allocate it to us
|
|
mov es:DHNDL_PSP[bx],dx
|
|
find_dh60:
|
|
sti ; safe again
|
|
pop cx ; discard handle count
|
|
xchg ax,si ; AX = IFN
|
|
mov ss:word ptr current_dhndl,bx
|
|
mov ss:word ptr current_dhndl+WORD,es
|
|
mov ss:current_ifn,ax
|
|
clc ; we have found and allocated a handle
|
|
find_dh70:
|
|
pop di ! pop si ! pop dx ! pop cx
|
|
ret
|
|
|
|
|
|
Public get_xftptr
|
|
|
|
get_xftptr:
|
|
;----------
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; ES:DI -> PSP_XFTPTR for current_psp
|
|
; CX = # entries in it
|
|
; CY set if not PSP operation (eg. FCB's)
|
|
; (all other regs preserved)
|
|
;
|
|
|
|
test ss:byte ptr remote_call+1,DHM_FCB/100h; if we are doing an FCB operation
|
|
jnz get_xftptr_err ; deal only with IFN, forget PSP
|
|
mov es,ss:current_psp
|
|
mov cx,PSP_XFNMAX ; CX = # entries in table
|
|
les di,PSP_XFTPTR ; ES:DI -> user file table
|
|
ret
|
|
|
|
get_xftptr_err:
|
|
stc ; forget about XFN's
|
|
ret
|
|
|
|
|
|
Public current_dsk2al
|
|
|
|
current_dsk2al:
|
|
;--------------
|
|
; AL = current default drive
|
|
mov al,ss:current_dsk
|
|
ret
|
|
|
|
Public lds_si_dmaptr
|
|
|
|
lds_si_dmaptr:
|
|
;-------------
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; DS:SI -> current DMA address
|
|
; (All other regs preserved)
|
|
lds si,ss:dword ptr dma_offset
|
|
ret
|
|
|
|
|
|
public les_di_dmaptr
|
|
|
|
les_di_dmaptr:
|
|
;-------------
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; ES:DI -> current DMA address
|
|
; (All other regs preserved)
|
|
les di,ss:dword ptr dma_offset
|
|
ret
|
|
|
|
Public copy_asciiz
|
|
|
|
copy_asciiz:
|
|
;----------
|
|
; Copy an ASCIIZ string from DS:SI to ES:DI
|
|
lodsb ! stosb
|
|
test al,al
|
|
jnz copy_asciiz
|
|
ret
|
|
|
|
if JOIN
|
|
|
|
check_join:
|
|
;----------
|
|
; On Entry:
|
|
; fdos_hds -> HDS we wish to check
|
|
; On Exit:
|
|
; AH = drive (zero based) from fdos_hds_drv
|
|
; AL = drive (zero based) of the JOIN root
|
|
; if JOINed drive
|
|
; ZF clear
|
|
; else
|
|
; ZF set
|
|
; (All other regs presrved)
|
|
;
|
|
push es ! push bx
|
|
mov al,fdos_hds_drv ; get drive from HDS_
|
|
mov ah,al ; save HDS_DRV in AH
|
|
cmp join_drv,0 ; need we do anything ?
|
|
je check_join30 ; not if we haven't JOIN'd
|
|
cmp fdos_hds_root,0 ; is virtual root the physical one ?
|
|
jne check_join10 ; if not we can't be JOIN'd
|
|
call get_ldt ; ES:BX -> LDT for this drive
|
|
jc check_join10 ; bad LDT - we can't be joined
|
|
test es:LDT_FLAGS[bx],LFLG_JOINED
|
|
jz check_join30
|
|
mov al,es:LDT_NAME[bx] ; get drive letter
|
|
sub al,'A' ; make drive letter zero based
|
|
mov bl,byte ptr path_drive ; get the logical drive we are on
|
|
push ax
|
|
mov al,bl ; AL = logical drive we are using
|
|
call get_ldt ; ES:BX -> LDT for this drive
|
|
pop ax
|
|
jc check_join10 ; no valid LDT..
|
|
cmp es:LDT_NAME+2[bx],'\' ; are we at the root ?
|
|
jne check_join10 ; no, it can't be match
|
|
mov bl,es:LDT_NAME[bx] ; get the root drive for this drive
|
|
sub bl,'A' ; make drive letter zero based
|
|
cmp al,bl ; are we on the JOIN'd drive ?
|
|
mov bl,0ffh ; assume we are
|
|
je check_join20 ; were we ?
|
|
check_join10:
|
|
mov al,ah ; restore HDS_DRV
|
|
xor bl,bl ; return with ZF clear
|
|
check_join20:
|
|
test bl,bl ; set ZF appropriately
|
|
check_join30:
|
|
pop bx ! pop es
|
|
ret
|
|
|
|
offer_join: ; are we opening a JOIN'd drive ?
|
|
;----------
|
|
; If we are at the root of a JOIN'd drive moving up ("..") fiddle HDS
|
|
; onto parental JOIN drive root.
|
|
; If we are at the root of a non-JOIN'd drive see if we are searching for
|
|
; a JOIN'd drive directory
|
|
; On Entry:
|
|
; info_fcb = entry we are searching for
|
|
; On Exit:
|
|
; CY set if not a JOIN'd drive
|
|
;
|
|
|
|
cmp join_drv,0 ; need we do anything ?
|
|
jne oj_dochecks ; not, if we haven't JOIN'd
|
|
oj_rejected:
|
|
stc ; not a JOIN drive
|
|
ret
|
|
|
|
oj_dochecks:
|
|
;-----------
|
|
; Before we do anything else we must be at the physical root of a drive
|
|
; with a valid LDT. We then check for two cases
|
|
; 1) Doing a '..' from a JOIN'd drive to it's parental root
|
|
; 2) Opening a directory correpsonding to a JOIN'd drive
|
|
;
|
|
mov al,info_fcb ; AL -> drive we are looking for
|
|
call get_ldt ; ES:BX -> LDT for this drive
|
|
jc oj_rejected ; bad LDT - we can't be joined
|
|
cmp es:LDT_ROOTLEN[bx],2 ; is root at top level ?
|
|
ja oj_rejected ; no, skip the rest
|
|
cmp fdos_hds_blk,0 ; are we at the root ?
|
|
jne oj_rejected ; if not we needn't do anything
|
|
|
|
; We have validated HDS_, so now check for case 1)
|
|
call check_join ; is it a joined drive ?
|
|
jz oj_dir ; if not skip to case 2)
|
|
cmp word ptr info_fcb+1,'..'; else is it a '..' in JOIN'd root?
|
|
jne oj_rejected ; if not we needn't do anything
|
|
call mvhds_drvroot ; do '..' from root -> real root
|
|
clc ; we handled it it !
|
|
ret
|
|
|
|
oj_dir:
|
|
;------
|
|
; We are in the physical root of a non-joined drive. We now need to see if
|
|
; the dir we are searching for corresponds with a joined drive
|
|
;
|
|
push si ! push di
|
|
call build_match_name ; join_name = what we are looking for
|
|
call look_for_match ; see if we can find it
|
|
jc oj_dir10
|
|
call mvhds_drvroot ; we have a match - move into it
|
|
clc ; say we handled it
|
|
oj_dir10:
|
|
pop di ! pop si
|
|
ret
|
|
|
|
|
|
look_for_match:
|
|
;--------------
|
|
; Compare join_name against available JOIN drives.
|
|
; Return with CY clear if we found it, ES:BX -> LDT, AL the drive
|
|
xor ch,ch
|
|
mov cl,join_drv ; search this many drives
|
|
xor ax,ax ; start with drive A:
|
|
mov ah,last_drv
|
|
call get_ldt ; ES:BX -> LDT for this drive
|
|
jnc lfm20
|
|
ret ; no LDT's...
|
|
lfm10:
|
|
inc al ; next drive
|
|
cmp al,ah ; paranioa - check if we have reached
|
|
jae lfm50 ; last_drv and exit if so
|
|
add bx,LDT_LEN ; next LDT
|
|
lfm20:
|
|
test es:LDT_FLAGS[bx],LFLG_JOINED
|
|
jz lfm10 ; if not JOIN'd try next candidate
|
|
|
|
lea di,LDT_NAME[bx] ; ES:DI -> JOIN info
|
|
mov si,offset join_name ; lets see if it matches the
|
|
push ax
|
|
lfm30:
|
|
lodsb ; get a byte
|
|
scasb ; does it match ?
|
|
jne lfm40 ; no, forget it
|
|
test al,al ; end of the string yet ?
|
|
jnz lfm30 ; no, keep trying
|
|
lfm40:
|
|
pop ax
|
|
je lfm60 ; did we match ?
|
|
loop lfm10 ; no, if any JOIN's left try them
|
|
lfm50:
|
|
stc ; we didn't find it
|
|
lfm60:
|
|
ret
|
|
|
|
build_match_name:
|
|
;----------------
|
|
; Fill join_name with the "C:\JOIN" we want to find
|
|
;
|
|
mov al,info_fcb ; AL -> drive we are looking for
|
|
dec al ; make it zero based
|
|
call get_ldt ; ES:BX -> LDT for this drive
|
|
mov al,es:LDT_NAME[bx] ; get the drive "D"
|
|
push ds ! pop es ; ES -> SYSDAT
|
|
mov di,offset join_name ; construct the target name
|
|
stosb ; plant the drive letter
|
|
mov ax,'\:'
|
|
stosw ; now we have "d:\"
|
|
mov bx,offset info_fcb+1 ; DS:SI -> name we are looking for
|
|
jmp unparse ; unparse the name
|
|
|
|
BDOS_DATA dseg word
|
|
|
|
join_name db 'd:\filename.ext',0
|
|
|
|
BDOS_CODE cseg
|
|
|
|
Public mv_join_root
|
|
|
|
mv_join_root:
|
|
;------------
|
|
; Poke the fdos_hds to be the root. If it's the physical root we then
|
|
; see if it is a JOIN'd drive. If it is we poke the drive and reselect
|
|
; the disk so we are at the real root of the drive.
|
|
;
|
|
push bx ! push si ! push di ; save index registers
|
|
mov ax,fdos_hds_root
|
|
mov fdos_hds_blk,ax ; move us to virtual root
|
|
test ax,ax ; is it the real root ?
|
|
jnz mvj_root10 ; if not forget about JOIN'd drives
|
|
call check_join ; are we joined ?
|
|
jz mvj_root10 ; no, we've done enough
|
|
call mvhds_drvroot ; make it real root
|
|
mvj_root10:
|
|
pop di ! pop si ! pop bx ; restore index registers
|
|
ret
|
|
|
|
|
|
mvhds_drvroot:
|
|
;-------------
|
|
; On Entry:
|
|
; AL = Drive (0 based physical)
|
|
; On Exit:
|
|
; None
|
|
;
|
|
; Poke the HDS to be at the root of drive AL and select that drive
|
|
;
|
|
mov fdos_hds_drv,al ; change to joined drive
|
|
xor dx,dx
|
|
mov fdos_hds_blk,dx ; put us back to the root again
|
|
mov fdos_hds_root,dx
|
|
cmp al,physical_drv ; already there ?
|
|
je mvhds_drvroot10 ; then skip the selection
|
|
call select_physical_drv ; select the drive
|
|
mvhds_drvroot10:
|
|
jmp path_prep_root ; info_fcb = '.'
|
|
; ret
|
|
|
|
endif ;JOIN
|
|
|
|
eject
|
|
stamp_dir_entry:
|
|
;---------------
|
|
; On Entry:
|
|
; DIRP -> None
|
|
; On Exit:
|
|
; None
|
|
;
|
|
; Apply current date/time stamp to a directory, along with any other
|
|
; security information required.
|
|
;
|
|
if PASSWORD
|
|
mov cx,local_password ; were we given a password ?
|
|
jcxz stamp_dir_entry10 ; if so apply it
|
|
mov bx,dirp
|
|
mov DPWM[bx],PWM_ANY ; deny all for compatibility
|
|
or DATTS[bx],DA_HIDDEN ; make dir entry hidden
|
|
mov DPWD[bx],cx ; with this password
|
|
stamp_dir_entry10:
|
|
endif
|
|
call ReadTOD ; get current time/dat
|
|
mov bx,dirp
|
|
; jmp stamp_date_and_time
|
|
stamp_date_and_time:
|
|
; On Entry:
|
|
; BX -> directory entry
|
|
; AX = time
|
|
; DX = date
|
|
; On Exit:
|
|
; None
|
|
; stamp a directory entry with a given date and time
|
|
mov DDATE[bx],dx
|
|
mov DTIME[bx],ax
|
|
mov ah,PASSWD_CREAT ; call out to SECURITY TSR
|
|
callf ss:fdos_stub
|
|
jc stamp_date_and_time10
|
|
jmp fdos_error ; return an error if required
|
|
stamp_date_and_time10:
|
|
ret
|
|
|
|
|
|
public ReadTOD
|
|
|
|
ReadTOD:
|
|
;-------
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; DX = internal date format
|
|
; AX = internal time format
|
|
;
|
|
call ReadTimeAndDate ; get current time/date from BIOS
|
|
; jmp GetTOD
|
|
|
|
GetTOD:
|
|
;-------
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; DX = internal date format
|
|
; AX = internal time format
|
|
;
|
|
mov ax,yearsSince1980 ; year is bits 9-15
|
|
mov cl,4
|
|
shl ax,cl
|
|
add al,month ; month is bits 4-8
|
|
mov cl,5
|
|
shl ax,cl
|
|
add al,dayOfMonth ; day is bits 0-3
|
|
xchg ax,dx ; DX = date
|
|
|
|
mov al,hour ; hour is bits 11-15
|
|
mov cl,6
|
|
shl ax,cl
|
|
add al,minute ; minute is bits 5-10
|
|
mov cl,5
|
|
shl ax,cl
|
|
mov cl,second ; second/2 is bits 0-4
|
|
shr cl,1 ;
|
|
add al,cl
|
|
ret
|
|
|
|
|
|
|
|
select_from_DTA:
|
|
;----------------
|
|
; called by search next/undelete/purge to select a disk and prepare for
|
|
; a directory search based upon DTA contents.
|
|
;
|
|
push ds
|
|
call lds_si_dmaptr ; DS:SI -> DMA address
|
|
lodsb ; get search drive
|
|
pop ds
|
|
dec ax ; (stored 1-relative)
|
|
mov path_drive,ax ; restore path drive
|
|
call select_physical_drv ; select the drive in AL
|
|
push ds
|
|
push ds ! pop es ; ES = local segment
|
|
call lds_si_dmaptr ; DS:SI -> DMA address
|
|
inc si ; skip the drive #
|
|
mov di,offset info_fcb+1 ; copy FCB back to INFO_FCB
|
|
mov cx,11
|
|
rep movsb
|
|
|
|
lodsb ; get search attribute
|
|
mov es:attributes,al
|
|
|
|
lodsw ; get directory count
|
|
mov es:dcnt,ax
|
|
|
|
lodsw ; get the directory block
|
|
pop ds ; restore data segment
|
|
mov fdos_hds_blk,ax
|
|
ret
|
|
|
|
if PASSWORD
|
|
|
|
check_pwd_d: ; check if PW protected in any mode
|
|
;-----------
|
|
mov ax,PWM_D
|
|
jmps check_pwd
|
|
|
|
check_pwd_any: ; check if PW protected in any mode
|
|
;-------------
|
|
mov ax,PWM_ANY
|
|
; jmps check_pwd
|
|
|
|
check_pwd: ; check for password mismatch
|
|
;---------
|
|
; entry: AX = password mode to be checked
|
|
; DIRP -> directory entry for file
|
|
; exit: BX, SI preserved
|
|
|
|
push ax
|
|
push bx
|
|
mov bx,dirp ; BX -> file to be opened/deleted/etc.
|
|
xchg ax,cx ; password mode in CX
|
|
mov ah,PASSWD_CHECK ; call out to SECURITY TSR
|
|
callf ss:fdos_stub
|
|
jnc check_pwd20
|
|
|
|
mov ax,DPWD[bx] ; get password hash code from entry
|
|
jcxz check_pwd10 ; exit if no password
|
|
cmp ax,global_password ; compare with default password
|
|
je check_pwd10 ; yes, go ahead
|
|
cmp ax,local_password ; is it one we've just parsed ?
|
|
je check_pwd10 ; yes, go ahead
|
|
; else we've got a password mismatch
|
|
|
|
test cx,DPWM[bx] ; test if password mode affects us
|
|
jz check_pwd10 ; skip if attempted access O.K.
|
|
mov ax,ED_PASSWORD ; return password error
|
|
check_pwd20:
|
|
jmp fdos_error
|
|
|
|
check_pwd10:
|
|
pop bx
|
|
pop ax
|
|
ret
|
|
|
|
Public hash_pwd
|
|
|
|
hash_pwd: ; compute 16-bit hash code for 1st COMMON_DMA password
|
|
;--------
|
|
; On Entry:
|
|
; DS:SI -> 8 character password
|
|
; On Exit:
|
|
; AX = password hash code or 0000 if none (ZF set)
|
|
; SI corrupted, all other regs preserved
|
|
;
|
|
|
|
push cx
|
|
push bp
|
|
mov cx,8 ; ch = 0, cl = 8
|
|
mov bp,sp ; reverse and upper-case the name
|
|
sub sp,cx ; using temp buffer on the stack
|
|
xor ax,ax ; zero null password flag
|
|
|
|
hash_pwd1:
|
|
lodsb ; get next ASCII character
|
|
call toupper ; international upper case
|
|
dec bp
|
|
mov ss:[bp],al ; copy password char to encrypted buff
|
|
or al,al ; is password char zero?
|
|
jz hash_pwd2 ; yes
|
|
cmp al,' ' ; is password char blank?
|
|
je hash_pwd2 ; yes
|
|
inc ah ; password is not null
|
|
hash_pwd2:
|
|
add ch,al ; add password char to encrypt CRC
|
|
dec cl
|
|
jnz hash_pwd1
|
|
|
|
mov al,ch ; AL = password CRC
|
|
mov cx,8 ; encrypt 8 characters
|
|
or ah,al ; if CRC = 0 and all 00 or ' '
|
|
jz hash_pwd6 ; then there is no password
|
|
|
|
hash_pwd3:
|
|
xor ss:[bp],al ; encrypt password on stack
|
|
inc bp
|
|
loop hash_pwd3
|
|
|
|
xor bp,bp ; initialize hash code
|
|
mov cx,4 ; 8 bytes = 4 words
|
|
hash_pwd4:
|
|
pop ax ; get two encrypted characters
|
|
rol ax,cl ; juggle them about a bit
|
|
xor bp,ax ; "add" them into hash code
|
|
loop hash_pwd4 ; repeat for whole password
|
|
jnz hash_pwd5 ; skip if result is non-zero
|
|
inc bp ; else force it to be non-zero
|
|
hash_pwd5:
|
|
xchg ax,bp ; return hash code in AX
|
|
hash_pwd6:
|
|
add sp,cx ; tidy up stack if buffer not poped
|
|
test ax,ax ; set ZF flag appropriately
|
|
pop bp
|
|
pop cx
|
|
ret
|
|
|
|
endif
|
|
|
|
|
|
public share_delay
|
|
|
|
; waste time before retrying operation that failed due to SHARE intervention
|
|
share_delay:
|
|
cmp ss:byte ptr remote_call,0
|
|
jne share_delay_30
|
|
push cx
|
|
mov cx,ss:net_delay
|
|
jcxz share_delay_20
|
|
share_delay_10:
|
|
push cx
|
|
xor cx,cx
|
|
loop $
|
|
pop cx
|
|
loop share_delay_10
|
|
share_delay_20:
|
|
pop cx
|
|
share_delay_30:
|
|
ret
|