Files
DR-DOS-OpenDOS/IBMDOS/UTILS.FDO
2020-11-04 23:59:28 +01:00

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