title 'REDIR - DOS file system network redirector interace support'
;    File              : $REDIR.A86$
;
;    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: $
;    REDIR.A86 1.30 94/12/01 10:05:21 
;    added attribute support for open/move/unlink  
;    REDIR.A86 1.29 94/11/11 15:10:03
;    Code at redir_lseek30 changed to ensure that the file offset is 
;    updated. Previously, this did not happen when INT 21/4202 (seek from
;    end of file) was called, and consequently the game MYST could not 
;    be installed under NWDOS7.    
;    REDIR.A86 1.28 94/10/03 15:41:12
;    fix problem where VLM network mapping gets deleted if you try to access
;    a path greater than 66 characters long on a network drive.
;    REDIR.A86 1.27 94/02/22 18:05:09
;    fix problem with "d:\\filename.ext" (Netware 4 LOGIN)
;    REDIR.A86 1.26 94/01/10 16:42:16
;    File delete uses file attributes of 06, not 16 (no directory bit)
;    REDIR.A86 1.25 93/12/10 00:03:09
;    Move non-inherited bit to correct place in file handle
;    REDIR.A86 1.22 93/11/19 23:59:00
;    If a read/write returns ED_LOCKFAIL turn into ED_ACCESS on shared files
;    REDIR.A86 1.21 93/09/24 19:50:50
;    Tidy up code on rename
;    REDIR.A86 1.15 93/06/16 16:22:21
;    Always initialise file search attributes to 16h (ie. sys+hidden+dir)
;    REDIR.A86 1.14 93/06/11 15:08:09
;    Return 0 from search first/next
;    REDIR.A86 1.13 93/06/11 02:06:53
;    zero space adjust on getddsc
;    ENDLOG

	eject ! include i:psp.def
	eject ! include i:fdos.equ
	eject ! include i:msdos.equ
	eject ! include i:mserror.equ
	eject ! include i:doshndl.def	; DOS Handle Structures
	eject ! include i:f52data.def	; DRDOS Structures
	eject ! include i:redir.equ

FD_EXPAND	equ	55h

	eject

PCMODE_DATA	dseg

	extrn	current_dsk:byte
	extrn	current_ddsc:dword
	extrn	current_dhndl:dword
	extrn	current_filepos:dword
	extrn	current_ldt:dword
	extrn	dma_offset:word
	extrn	dma_segment:word
	extrn	err_drv:byte
	extrn	file_attrib:word
	extrn	ldt_ptr:dword
	extrn	last_drv:byte
	extrn	phys_drv:byte
	extrn	pri_pathname:byte
	extrn	remote_call:word
	extrn	sec_pathname:byte
	extrn	srch_buf:byte
	
; Data for some int2f commands

	extrn	int2f_cmd:word
	extrn	int2f_stack:word
	extrn	file_mode:word

BDOS_CODE	cseg

	Public	islocal
	Public	redir_asciiz_dev_offer
	Public	redir_asciiz_offer
	Public	redir_asciiz_file_offer

	Public	redir_drv_offer
	Public	redir_dhndl_offer
	Public	redir_move_offer
	Public	redir_snext_offer

	extrn	toupper:near
	extrn	check_delim:near
	extrn	check_slash:near
	extrn	check_dslash:near
	extrn	copy_asciiz:near
	extrn	unparse:near
	extrn	find_xfn:near
	extrn	find_dhndl:near
	extrn	ifn2dhndl:near
	extrn	get_xftptr:near
if KANJI
	extrn	dbcs_lead:near
endif


redir_dhndl_offer:
;================
; The FDOS has called this hook to see if we are operating on an MSNET drive.
; We return if we are not, or stay and process function here if we are
	test	es:byte ptr DHNDL_WATTR+1[bx],DHAT_REMOTE/100h
	 jnz	redir_dhndl_accept
	ret
redir_dhndl_accept:
; copy some info from DHNDL_ to local variables
	push	ds
	push ss ! pop ds
	mov	ax,es:DHNDL_DEVOFF[bx]
	mov	word ptr current_ddsc,ax
	mov	ax,es:word ptr DHNDL_DEVSEG[bx]
	mov	word ptr current_ddsc+WORD,ax
	mov	ax,es:word ptr DHNDL_POS[bx]
	mov	word ptr current_filepos,ax
	mov	ax,es:word ptr DHNDL_POS+2[bx]
	mov	word ptr current_filepos+2,ax
	pop	ds
	jmps	redir_accept		; now we can process the call


redir_asciiz_dev_offer:
;======================
; The FDOS has called this hook to see if we are opening a redirected device.
; We return if we are not, or stay and process function here if we are

; Before we build it all ourselves do the appropriate INT 2F to allow
; other people to process the path themselves

	call	redir_dev_check		; is it a device ?
	 jnc	redir_accept		; it's been recognised
	ret

redir_move_offer:
;================
; The FDOS has called this hook to see if the rename operation is on a
; redirected drive. Lantastic relies on the path check broadcast's being
; done in the order new_name, old_name.

	mov	word ptr current_ldt,0ffffh
	mov	err_drv,DHAT_DRVMSK
	push	ds
	mov	si,2[bp]		; SI -> parameter block
	lds	si,10[si]		; DS:SI -> user supplied name
	push ss ! pop es
	mov	di,offset sec_pathname	; DI -> where to build pathname
	push ds ! push si ! push di ! push bp
	mov	ax,I2F_PPATH
	int	2fh			; offer path build to someone else
	pop bp ! pop di ! pop si ! pop ds
	mov	cx,0			; if accepted then it's a remote drive
	 jnc	redir_move_offer10
	call	build_remote_path	; build path if it a remote drive
redir_move_offer10:
	pop	ds
	 jc	redir_asciiz_error	; return error's if we have any
	 jcxz	redir_asciiz_offer	; it's a valid remote path
	ret				; it's not a remote path

redir_asciiz_offer:
;==================
; The FDOS has called this hook to see if we are operating on an redirected
; drive. We return if we are not, or stay and process function here if we are.
	call	redir_dev_check		; is it a device ?
	 jnc	redir_accept		; it's been recognised
;	jmp	redir_asciiz_file_offer	; now try offering as a file

redir_asciiz_file_offer:
;=======================
;	entry:	SS:BP -> function #, parameter address
;		1st level call (MXdisk not owned)
;	exit:	ZF = 1 if not networked
;
; This function is only called after an Int 2F/I2F_PPATH callout has been
; made and failed. On some functions (Open/Create) we do a check for devices
; between the redir_dev_offer and redir_asciiz_file offer

; We look for a "\\" as the start of "\\server\dir\file" form

	push	ds			; save DS
	mov	si,2[bp]		; SI -> parameter block
	lds	si,2[si]		; get ASCIIZ # from parameter block
	mov	di,offset pri_pathname	; SS:DI -> path buffer
	call	build_remote_path	; build path if it a remote drive
	pop	ds			; restore DS
	 jc	redir_asciiz_error	; return error's if we have any
	 jcxz	redir_accept		; it's a valid remote path
	ret				; it's not a remote path

redir_asciiz_error:
; On Entry:
;	BX = error code
;	DS on stack
; On Exit:
;	DS restored, BX preserved
;	near return on stack discarded, and error returned to fdos_entry
;
	pop	ax			; discard "offer" near ret
	ret				; drop straight back to caller

redir_drv_offer:
;===============
; The FDOS has called this hook to see if we are operating on an MSNET drive.
; We return if we are not, or stay and process function here if we are
;
;	entry:	SS:BP -> function #, parameter address
;		(MXdisk not owned)
;	exit:	return if not networked

	mov	dl,current_dsk		; assume it's the default drive
	mov	si,2[bp]		; SI -> parameter block
	mov	ax,2[si]		; get drive # from parameter block
	test	al,al			; test if default drive
	 jz	redir_drv_offer10	; skip if default drive
	dec	ax			; else decrement for 0..26
	xchg	ax,dx			; and use that
redir_drv_offer10:
	call	isremote
	 jnz	redir_accept
	ret				; return if not

redir_snext_offer:
;=================
; The FDOS has called this hook to see if we are operating on an MSNET drive.
; We return if we are not, or stay and process function here if we are

	call	redir_restore_srch_state
	test	al,80h			; MSNET drive ?
	 jnz	redir_accept
	ret


redir_accept:
;============
; We have decided to accept an FDOS function.
; Note by this time the functions have been validated as legal
	mov	file_attrib,16h		; default search attribs to all
	pop	si			; discard the near return address
	mov	si,2[bp]		; SI -> parameter block
	mov	si,ds:[si]		; fdos code number
	add	si,si			; make it a word offset
	jmp	cs:redir_tbl-(39h*WORD)[si]	; call the relevant function


redir_badfunc:
;-------------
	mov	bx,ED_FUNCTION		; bad function number
	ret				; (shouldn't get here...)

redir_tbl	dw	redir_mkdir	; 39-make directory
		dw	redir_rmdir	; 3A-remove directory
 		dw	redir_chdir	; 3B-change directory
		dw	redir_creat	; 3C-create file
		dw	redir_open	; 3D-open file
		dw	redir_close	; 3E-close file
		dw	redir_read	; 3F-read from file
		dw	redir_write	; 40-write to file
		dw	redir_unlink	; 41-delete file
		dw	redir_lseek	; 42-set file pointer
		dw	redir_chmod	; 43-get/set file attributes
		dw	redir_badfunc	; 44-IOCTL emulation
		dw	redir_badfunc	; 45-duplicate handle
		dw	redir_badfunc	; 46-force duplicate handle
		dw	redir_badfunc	; 47-get current directory
		dw	redir_getdpb	;*48*disk information
		dw	redir_badfunc	;*49*flush buffers
		dw	redir_badfunc	;*4A*drive select
		dw	redir_badfunc	;*4B*create child PSP
		dw	redir_badfunc	;*4C*close child PSP
		dw	redir_badfunc	;*4D*generic FCB call
		dw	redir_first	; 4E-find first matching file
		dw	redir_next	; 4F-find next matching file
		dw	redir_commit	;*50*commit file
		dw	redir_mknew	;*51*make new file
		dw	redir_lock	;*52*lock/unlock block
		dw	redir_badfunc	; 53 build DDSC from BPB
		dw	redir_badfunc	;*54*Int 25/26 emulation
		dw	redir_expand	; 55 expand file name
		dw	redir_move	; 56-rename file
		dw	redir_dattim	; 57-get/set file name

	Public	get_ldt

; To support func5D00 remote server calls we must bypass the LDT and
; go directly to the physical drive. To allow support of CDROM's etc
; on servers we do use the LDT's where no corresonding physical drive
; exists.

get_ldt:
;-------
; On Entry:
;	AL = drive (0 based)
; On Exit:
;	ES:BX -> LDT for that drive, set CY if no valid LDT
;	(All other regs preserved)
;
	cmp	al,ss:phys_drv		; if there is no conflict with
	 jae	get_ldt_raw		;  physical drives it's OK
	test	ss:remote_call,0ffh	; remote calls must get to physical
	 jz	get_ldt_raw		;  for server support
	mov	bx,ED_DRIVE		; invalid drive
	stc				; no-go
	ret

	Public	get_ldt_raw

get_ldt_raw:
;-----------
; On Entry:
;	AL = drive (0 based)
; On Exit:
;	ES:BX -> LDT for that drive, set CY if no valid LDT
;	(All other regs preserved)
;
	push	ax
	mov	bx,ED_DRIVE		; assume invalid drive
	cmp	al,ss:last_drv		; do we have an LDT for this drive ?
	 jae	get_ldt10		;  if not we can't have an LDT
	cmp	ss:word ptr ldt_ptr+2,0	; are the LDT's allocated yet ?
	 je	get_ldt10		;  if not return an error
	mov	bl,LDT_LEN
	mul	bl			; AX = offset of LDT entry for drive
	les	bx,ss:ldt_ptr		; get base of LDT's
	add	bx,ax			; DS:BX -> LDT for this drive
	stc				; ie. CLC on exit
get_ldt10:
	cmc
	pop	ax
	ret

get_ldt_flags:
;-------------
;	entry:	DL = drive number
;	exit:	AX = LDT_FLAGS entry for that drive
;		(All other regs preserved)
;
	push	es
	push	bx
	xor	ax,ax			; assume no LDT around...
	xchg	ax,dx			; AL = drive number
	call	get_ldt			; ES:BX -> LDT
	xchg	ax,dx			; restore drive to DX
	 jc	glf10			; AX = zero if no LDT
	mov	ss:word ptr current_ldt,bx ; set current_ldt
	mov	ss:word ptr current_ldt+2,es
	mov	ax,es:LDT_FLAGS[bx]	; return real flags
glf10:
	pop	bx
	pop	es
	ret
	
islocal:
;-------
;	entry:	AL = drive number (zero based)
;	exit:	CY = set if drive is remote
;		(All other regs preserved)
;
	push	ax
	push	dx
	xchg	ax,dx
	call	get_ldt_flags
if JOIN
	test	ax,LFLG_JOINED+LFLG_NETWRKD
					; are REMOTE/JOINED bits set ?
else
	test	ax,LFLG_NETWRKD		; is REMOTE bit set ?
endif
	 jz	islocal10
	stc				; indicate it's not local
islocal10:
	pop	dx
	pop	ax
	ret

isremote:
;--------
;	entry:	DL = drive number
;	exit:	ZF = clear if drive is remote
;		CY = set if drive is JOIN'd
;		(Only AX corrupted)
;
	call	get_ldt_flags
if JOIN
	test	ax,LFLG_JOINED		; is JOINED bit set ?
	 jnz	isremote10
endif
	test	ax,LFLG_NETWRKD		; is REMOTE bit set ?
	ret
if JOIN
isremote10:
	test	ax,LFLG_NETWRKD		; is REMOTE bit set ?
	stc				; STC to indicate JOIN'd
	ret
endif

redir_dev_check:
;---------------
; Offer the name to the network as a device
; On Entry:
;	PB+2 = dword ptr to ascii name
;	DI -> buffer to parse into
; On Exit:
;	CY set if not recognised
;
	mov	word ptr current_ldt,0ffffh
	mov	err_drv,DHAT_DRVMSK
	push ds ! push bp
	mov	si,2[bp]		; SI -> parameter block
	lds	si,2[si]		; DS:SI -> user supplied name
	mov	dx,si			; DS:DX as well..
	push ss ! pop es
	mov	di,offset pri_pathname	; DI -> where to build pathname
	mov	ax,I2F_PPATH
	int	2fh			; offer path build to someone else
	pop bp ! pop ds
	ret

build_remote_path:
;-----------------
; On Entry:
;	DS:SI -> path to check
;	SS:DI -> position to build remote path
; On Exit:
;	CY clear, CX == 0 if it's a valid remote path
;	CY clear, CX <> 0 if it's a local path
;	CY set if there is an error to be returned (BX = error code)
;
	mov	cx,si			; save source path in CX
	mov	dl,ss:current_dsk	; assume current disk
	lodsw				; get 1st two characters
	test	al,al			; make sure it's not a NUL string
	 jz	build_remote_path10	;  before we check it it's a
	cmp	ah,':'			;  drive specified
	 jne	build_remote_path10	; we want to find "d:\\" format too
	mov	bx,ED_DRIVE		; assume "invalid drive" error
	and	al,not 'a'-'A'		; cheap upper case
	sub	al,'A'
	 jb	build_remote_path30	; return "invalid drive" if error
	cmp	al,ss:last_drv		; check if > 'Z'
	 ja	build_remote_path30	; return "invalid drive" if error
	xchg	ax,dx			; DL = ASCIIZ supplied drive
	lodsw				; get possible '\\'
build_remote_path10:
	mov	ss:word ptr current_ldt,0ffffh
	call	check_dslash		; is it "\\"
	 je	build_remote_path20		; if so forget about the drive #
	call	isremote		; test if drive DL is remote
if JOIN
	 jb	build_remote_path30	; return "invalid drive" if JOINed
endif
	 jnz	build_remote_path20	; it's remote, go build a path
	xor	cx,cx			; CY clear (no error)
	inc	cx			; CX <> 0, (non-remote drive)
	ret

build_remote_path20:
; Build a path from the CSD and the pathname in the parameter block
	mov	si,cx			; DS:SI -> source ASIIZ name
	push ss ! pop es		; ES:DI -> MSNET buffer
	call	redir_build_path
	 jc	build_remote_path30
	xor	cx,cx			; CY clear, CX == 0
	ret				; it's a valid remote path

build_remote_path30:
	stc				; CY set, we have error BX
	ret



eject
;	MAKE DIRECTORY (MKDIR)

;	+----+----+----+----+----+----+
;	|    39   |        name       |
;	+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name

;	exit:
;	-----
;	BX:	0000 or error code ( < 0)

redir_mkdir:
	mov	ax,I2F_MKDIR		; it's a make dir
	jmps	redir_pathop_common


eject
;	REMOVE DIRECTORY (RMDIR)

;	+----+----+----+----+----+----+
;	|    3A   |        name       |
;	+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name

;	exit:
;	-----
;	BX:	0000 or error code ( < 0)


redir_rmdir:
	mov	ax,I2F_RMDIR		; it's a remove dir
;	jmp	redir_pathop_common

redir_pathop_common:
	push	ds
	push ss ! pop ds
	call	int2f_ldt
	pop	ds
	 jc	redir_pathop_common10
	xor	ax,ax			; no problems
redir_pathop_common10:
	xchg	ax,bx			; return result in BX
	ret



eject
;	CHANGE DIRECTORY (CHDIR)

;	+----+----+----+----+----+----+
;	|    3B   |        name       |
;	+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name

;	exit:
;	-----
;	BX:	0000 or error code ( < 0)

redir_chdir:
;------------
; The following code throws out the ASSIGN/SUBST form "f:=d:\"
; The alternative is to allow the Extentions to throw it out as a bad path
; but this is safer, and doesn't cost us much.

	push	ds
	mov	bx,ED_PATH		; assume we have a problem
					; BX = ED_PATH ready for error

	mov	si,2[bp]		; SI -> parameter block
	lds	si,ds:2[si]		; DS:SI -> ASCIIZ string
	cmp	ds:word ptr 1[si],'=:'	; 'd:=' specification?
	 je	redir_chdir40

	push ss ! pop ds		; DS = PCMODE

	cmp	word ptr current_ldt,-1
	 je	redir_chdir40		; we reject any chdir of the form
					; "\\server\path"
	mov	ax,I2F_CHDIR
	call	int2f_ldt		; is this a valid path ?
	 jc	redir_chdir30
	push 	ds ! pop es
	mov	si,offset pri_pathname

; DGM - don't allow path greater than 66 chars
	mov	cx, LDT_FLAGS-LDT_NAME	; calculate max pathlen from LDT
	mov	di, si
	sub	ax,ax
	repne	scasb
	mov	ax, ED_PATH		; assume path too long
	 jne	redir_chdir30		; jump if path too long

	les	di,current_ldt		; ES:DI -> path for this drive
	call	copy_asciiz		; copy new path to LDT current dir
	xor	ax,ax			; no errors
redir_chdir30:
	xchg	ax,bx			; return result in BX
redir_chdir40:
	pop	ds
	ret


eject
;	CREATE FILE (CREAT)

;	+----+----+----+----+----+----+----+----+
;	|    3C   |        name       |  mode   |
;	+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name
;   mode:   attribute for file

;	exit:
;	-----
;	BX:	file handle or error code ( < 0)


redir_creat:
	call	get_attrib_mode		; AX = mode, CX = attrib
	xchg	ax,cx
	or	ax,20h			; set the ARCHIVE bit on create
	mov	file_attrib,ax
	mov	int2f_stack,ax		; attrib on stack
	mov	int2f_cmd,I2F_CREATE
	cmp	word ptr current_ldt,-1	; valid LDT ?
	 jne	redir_creat10		; no, modify create function
	mov	int2f_cmd,I2F_XCREATE
redir_creat10:
	jmp	redir_open_create_common


eject
;	OPEN FILE (OPEN)

;	+----+----+----+----+----+----+----+----+----+----+
;	|    3D   |        name       |  mode   |  attrib |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name
;   mode:   open mode 
;	attrib:	file attrib for search (default = 16h)

;	exit:
;	-----
;	BX:	file handle or error code ( < 0)

redir_open:
;----------
;
	call	get_attrib_mode		; AX = mode, CX = attrib
	and	ax,7fh			; remove inheritance bit
	mov	int2f_stack,ax
	mov	int2f_cmd,I2F_OPEN
redir_open_create_common:
	call	find_xfn		; get external file handle in DI
	mov	bx,ED_HANDLE		;  assume no handles
	 jc	redir_open20
	push	di
	call	redir_openfile		; now do the open
	pop	di
	 jc	redir_open20
; On Entry:
;	AL = IFN
;	DI = XFN
;	ES:BX -> DHNDL_
; On Exit:
;	PSP fixed up
;
	mov	bx,di
	call	get_xftptr		; ES:DI -> XFN table
	 jc	redir_open10		; no PSP, skip xfn stuff
	add	di,bx			; add external file #
	stosb				; update table entry
	ret

redir_open10:
	xchg	ax,bx			; no PSP, return XFN in BX
redir_open20:
	ret


redir_openfile:
;--------------
; On Entry:
;	pri_pathname and file_attrib have been set up
; On Exit:
;	AL = IFN
;	ES:BX = DHNDL_
;	CY set on error, BX = error code
; We should set up COUNT, MODE, UID, PSP, and SHARE
	call	find_dhndl		; find DHNDL_
	 jc	redir_openf40		; return if a problem with this
	push	ax			; save IFN
	push es ! push bx		; save DHNDL_

	mov	ax,file_mode
	and	al,not DHM_LOCAL
	mov	es:DHNDL_MODE[bx],ax	; save mode in DOSHNDL
	mov	es:DHNDL_SHARE[bx],0	; zero share record

	mov	ax,int2f_cmd		; either open or create
	call	int2f_dhndl		; lets try the command

	pop bx ! pop es			; recover DHNDL_
	pop	dx			; recover IFN
	 jc	redir_openf30		; on error discard the handle
	xchg	ax,dx			; return AL = IFN
	mov	es:DHNDL_COUNT[bx],1	; handle now properly in use
	mov	cx,file_mode
	test	ch,DHM_FCB/256		; is this an FCB open ?
	 jz	redir_openf10
	or	es:byte ptr DHNDL_MODE+1[bx],DHM_FCB/100h
redir_openf10:
	test	cl,DHM_LOCAL		; is it a "private" file ?
	 jz	redir_openf20
	or	es:byte ptr DHNDL_WATTR+1[bx],DHAT_LOCAL/100h
redir_openf20:
	ret

redir_openf30:
	xchg	ax,bx			;  returning error in BX
	ret

redir_openf40:
	mov	bx,ED_HANDLE		; no handles are left
	ret

eject
;	CLOSE FILE (CLOSE)

;	+----+----+----+----+
;	|    3E   |  handle |
;	+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle to be closed

;	exit:
;	-----
;	BX:	0000 or error code ( < 0)

redir_close:
;------------
;
	mov	si,2[bp]		; SI -> parameter block
	mov	ax,2[si]		; get external file #
	call	get_xftptr		; ES:DI -> xft table
	 jc	redir_close10
	add	di,ax			; add in XFN
	mov	ax,0FFh			; get "unused" value for XFT
	xchg	al,es:[di]		; release file & get internal #
redir_close10:				; we can now do the actual close
	call	ifn2dhndl		; ES:BX -> DHNDL_
	 jc	redir_close30		;  exit if error occurrs
    mov ax,es:DHNDL_COUNT[bx]   
	mov	ah,3eh			;  count, AH = 3E
	push	ax
	mov	ax,I2F_CLOSE
	call	int2f_dhndl		; close it
	pop	bx
	 jnc	redir_close20		; errors ?
	xchg	ax,bx			; recover return code
redir_close20:
	ret
redir_close30:
	mov	bx,ED_H_MATCH		; assume invalid IFN
	ret

eject
;	READ FROM FILE (READ)

;	+----+----+----+----+----+----+----+----+----+----+
;	|    3F   |  handle |       buffer      |  count  |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle
;	buffer:	buffer to read into
;	count:	max. number of bytes to read

;	exit:
;	-----
;	BX:	error code ( < 0)

redir_read:
;----------
	mov	ax,I2F_READ
redir_rw_handle:
	mov	si,2[bp]		; SI -> parameter block
	mov	cx,8[si]		; CX = Count
	push	ds
	lds	dx,4[si]		; DS:DX = DMA address
	call	redir_rw
	pop	ds
	mov	si,2[bp]		; SI -> parameter block
	mov	8[si],cx		; CX = Count
	ret

eject
;	WRITE TO FILE (WRITE)

;	+----+----+----+----+----+----+----+----+----+----+
;	|    40   |  handle |       buffer      |  count  |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle
;	buffer:	buffer to be wriiten
;	count:	max. number of bytes to write

;	exit:
;	-----
;	BX:	error code ( < 0)

redir_write:
;-----------
	mov	ax,I2F_WRITE
	jmps	redir_rw_handle

redir_rw:
;--------
; On Entry:
;	AX = command code
;	ES:BX = DHNDL_
;	CX = count
;	DS:DX = buffer
; On Exit:
;	CX = count transferred
;	BX = zero or error code
;
	cmp	ax,I2F_WRITE		; is it a write ?
	 jne	redir_rw10
	and	es:DHNDL_WATTR[bx],not (DHAT_CLEAN+DHAT_TIMEOK)
redir_rw10:
	push	ss:dma_offset
	push	ss:dma_segment
	push	cx
	mov	cl,4
	mov	di,dx			; save dma offset
	and	dx,15			; make offset within para
	shr	di,cl			; convert offset to para offset
	mov	si,ds			; add to segment
	add	di,si			; DI:DX -> DMA address
	 ja	redir_rw15		; are we within normal TPA ?
	inc	di			; no adjust to offset within
	shl	di,cl			;  magic segment FFFF
	add	dx,di
	mov	di,0ffffh		; use magic segment
redir_rw15:
	pop	cx
	mov	ss:dma_offset,dx	; save for xfer
	mov	ss:dma_segment,di
	call	int2f_dhndl		; try the xfer
	pop	ss:dma_segment
	pop	ss:dma_offset
	mov	bx,0			; assume no error
	 jnc	redir_rw20
	xchg	ax,bx			; return error code
	cmp	bx,ED_LOCKFAIL		; is it a lockfail error ?
	 jne	redir_rw20		; no, just return it
	les	di,ss:current_dhndl	; compatibility modes should generate
	test	es:DHNDL_MODE[di],DHM_SHAREMSK
	 jz	redir_rw20		;  critical errors fro ED_LOCKFAIL
	mov	bx,ED_ACCESS		; sharing modes return access denied
redir_rw20:
	ret
	

eject
;	DELETE FILE (UNLINK)

;	+----+----+----+----+----+----+
;	|    41   |        name       |
;	+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name

;	exit:
;	-----
;	BX:	0000 or error code ( < 0)


redir_unlink:
	mov	ax,I2F_DEL		; it's a delete
;	mov	file_attrib,6		; only delete files
redir_unlink_move_common:
	push	ax
	call	get_attrib_mode		; allow overrides for server calls
	pop	ax
	jmp	redir_pathop_common

eject
;	GET/SET FILE POSITION (LSEEK)

;	+----+----+----+----+----+----+----+----+----+----+
;	|    42   |  handle |       offset      |  method |
;	+----+----+----+----+----+----+----+----+----+----+

; On Entry:
;	ES:BX = DHDNL_
;	method:	0 = begin, 1 = current, 2 = end of file
;
; On Exit:
;	BX = Error Code, offset updated with new value
;

redir_lseek:
;-----------
	mov	di,bx			; ES:DI -> DHNDL_
	mov	si,2[bp]		; SI -> parameter block
	mov	dx,4[si]		; get 32-bit file offset
	mov	cx,6[si]		; into CX,DX
	mov	ax,8[si]		; get seek mode
	test	ax,ax
	 jz	redir_lseek20		; seek from beginning
	dec	ax
	 jz	redir_lseek10		; seek from current position
	dec	ax
	 jz	redir_lseek30		; seek from end
	mov	bx,ED_DATA		; else invalid seek mode
	ret

redir_lseek10:				; seek mode 1: relative to position
	add	dx,es:DHNDL_POSLO[di]	; add position + offset
	adc	cx,es:DHNDL_POSHI[di]
;	jmps	redir_lseek20

redir_lseek20:				; seek mode 0: set absolute position
	mov	es:DHNDL_POSLO[di],dx	; set new file offset
	mov	es:DHNDL_POSHI[di],cx	; SI = error code/0 at this point
;	jmps	redir_lseek90

redir_lseek90:
	mov	4[si],dx		; set 32-bit file offset
	mov	6[si],cx		; for return
	xchg	ax,bx			; error code in BX
	ret

redir_lseek30:				; seek mode 2: relative to end
	mov	bx,es:DHNDL_MODE[di]	; ask MSNET if anyone else can write
	and	bl,DHM_SHAREMSK		; isolate sharing bits
	cmp	bl,DHM_DENY_READ	; Only DENY_READ and DENY_NONE a
	 jb	redir_lseek40		;  problem - others might write to
	push	si			;  the file so we must ask the server
					;  how long the file is now
	mov	ax,I2F_LSEEK		; CX:DX = position now
	call	int2f_dhndl		; do a remote seek
	pop	si			; DX:AX = EOF relative position
	 jc	redir_lseek90		; (unless we have an error)
	xchg	ax,dx			; AX:DX = new EOF relative position
	xchg	ax,cx			;  and finally get into CX:DX 
	xor	ax,ax			; no problems...
	;jmps	redir_lseek90		; MYST-removed,file offset wasn't updated
	jmps	redir_lseek20		; MYST-added,go and update the new file offset.
redir_lseek40:
	add	dx,es:DHNDL_SIZELO[di]	; add file size + offset
	adc	cx,es:DHNDL_SIZEHI[di]
	jmps	redir_lseek20


eject
;	GET/SET FILE ATTRIBUTES (CHMOD)

;	+----+----+----+----+----+----+----+----+----+----+
;	|    43   |        name       |   flag  | attrib  |
;	+----+----+----+----+----+----+----+----+----+----+
;	|        size       |
;	+----+----+----+----+

;	entry:
;	------
;	name:	pointer to ASCIIZ file name
;	flag:	0 = get attribute,
;		1 = set attribute,
;	      2-5 = passwords (ignored)
;	attrib:	new attribute if flag = 1
;		password mode if flag = 3
;	
;	exit:
;	-----
;	BX:	0000 or error code ( < 0)
;	attrib:	file's attribute if flag = 0
;	size:	file's size

redir_chmod:
;-----------
;
	mov	bx,2[bp]		; BX -> parameter block
	mov	cx,8[bx]		; get attribs
	mov	bx,6[bx]		; get access flag
	mov	ax,I2F_SET_ATTR		; assume it's a set
	cmp	bl,1			; is it a set ?
	 je	redir_chmod10		; if not then
	mov	ax,I2F_GET_ATTR		; assume it's a get
	mov	cx,16h			;  with everything attribs
	 jb	redir_chmod10		;  was it ?
	mov	ax,ED_ACCESS		; no, return access denied
	jmps	redir_chmod20		;  cause it DR password stuff
redir_chmod10:
	mov	int2f_stack,cx		; attribs on the stack
	call	int2f_ldt		; do the Int 2F
	 jc	redir_chmod20
	mov	si,2[bp]		; SI -> parameter block
	mov	8[si],ax		;  return attribute
	mov	10[si],di
	mov	12[si],bx
	xor	ax,ax			;  success
redir_chmod20:
	xchg	ax,bx			; return result in BX
	ret

eject
;	GET DISK PARAMETER BLOCK

;	+----+----+----+----+----+----+----+----+----+----+
;	|    48   |  drive  |        dpb        | adjust  |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	drive:	drive to get information about

;	exit:
;	-----
;	BX:	0000 or error code ( < 0)
;	dpb:	address of DOS DPB (offset/segment)
;	adjust:	delwatch adjustment of free space

; NB. We only fill in the fields required by the Disk Free Space call.

redir_getdpb:
;------------
	mov	ax,I2F_SPACE
	call	int2f_ldt		; get the info
	 jnc	redir_getdpb10		; if we get an error then make CLMSK=FE
    mov al,0ffh        
redir_getdpb10:        
	mov	si,offset sec_pathname	; let's re-use this as a temp DPB
	dec	al			; make cluster mask
	mov	ds:DDSC_CLMSK[si],al	; and stuff into DPB
	mov	ds:DDSC_FREE[si],dx
	mov	ds:DDSC_SECSIZE[si],cx
	inc	bx			; inc number of clusters
	mov	ds:DDSC_NCLSTRS[si],bx
	mov	al,err_drv		; also fill in drive number
	mov	ds:DDSC_UNIT[si],al
	mov	si,2[bp]		; DI -> parameter block
	mov	4[si],offset sec_pathname
	mov	6[si],ds		; point to my dummy DPB
	mov	word ptr 8[si],0	; zero adjust value
	mov	bx,0ffh			; return 0xFF (ie. bad drive)
	ret

eject
;	FIND FIRST FILE

;	+----+----+----+----+----+----+----+----+----+----+
;	|    4E   |        name       |  *****  |  attrib |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	name:	pointer to ASCIIZ file name
;	attrib:	attribute to be used in search
;	
;	exit:
;	-----
;	BX:	0001 or error code ( < 0)

;	Note:	This call returns matching files in
;		the current DMA address and also saves
;		the BDOS state in the there.
;		
;		If there is space for multiple file names we
;		could return as many as will fit into the DTA
;		but it's easier just to return 1 file
;

redir_first:	; 13-find first matching file
;----------
; ONLY 1 file returned for now.....
;
	call	get_attrib_mode		; AX = mode, CX = attrib
	mov	ax,I2F_SFIRST		; srch first, valid LDT
	les	di,current_ldt
	cmp	di,-1		; valid LDT ?
	 jne	redir_first10
	mov	ax,I2F_XSFIRST		; srch first, no valid LDT
redir_first10:
	jmps	srch_buf_common

eject
;	FIND NEXT FILE

;	+----+----+
;	|    4F   |
;	+----+----+
;
;	entry:
;	------
;	
;	exit:
;	-----
;	BX:	0000 or error code ( < 0)

;	Note:	This call returns matching files in
;		the current DMA address and also saves
;		the BDOS state in the there.

redir_next:	; 14-find next matching file
;---------
; ONLY 1 file returned for now.....
;
	push ss ! pop es
	mov	di,offset srch_buf	; point ES:DI -> search buffer
	mov	ax,I2F_SNEXT
srch_buf_common:
	push	dma_offset
	push	dma_segment
	mov	dma_offset,offset srch_buf
	mov	dma_segment,ds
	call	int2f_op

	pop	dma_segment
	pop	dma_offset
	 jc	srch_buf_common10
	call	redir_save_srch_state	; if no error save state
	xor	ax,ax			;  return AX = 1 file found
srch_buf_common10:
	xchg	ax,bx			; return code in BX
	ret


redir_save_srch_state:
; On entry DS=PCMODE
	les	di,dword ptr dma_offset	; ES:DI -> search state in DMA address
	
	mov	si,offset srch_buf
	mov	cx,21			; save 1st 21 bytes
	rep	movsb

	push	si			; save name/ext
	add	si,11			; skip to name/ext
	movsb				; copy the attribute
	add	si,10			; skip reserved bytes
	movsw				; copy the time
	movsw				; copy the date
	add	si,2			; skip starting cluster
	movsw				; copy the file size
	movsw
	pop	bx			; recover name
	jmp	unparse			; unparse the name

; Restore DOS search area from user DTA
;
redir_restore_srch_state:
;------------------------
; On Entry:
;	DS = SYSDAT
; On Exit:
;	AL = 1st byte of srch buf
;	DS preserved
;
	push	ss
	pop	es
	mov	di,offset srch_buf	; ES:DI -> internal state
	push	ds
	lds	si,ss:dword ptr dma_offset
	lodsb				; DS:SI -> search state in DMA address
	stosb
	mov	cx,20			; copy 1st 21 bytes
	rep	movsb
	pop	ds
	ret

eject
;	COMMIT FILE (COMMIT)

;	+----+----+----+----+
;	|    50   |  handle |
;	+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle to be flushed

;	exit:
;	-----
;	BX:	0000 or error code ( < 0)

redir_commit:
	test	es:DHNDL_MODE[bx],DHM_WO+DHM_RW
	 jz	redir_commit10		;  we don't need to commit it
	mov	ax,I2F_COMMIT
	call	int2f_dhndl		; commit this file
	 jc	redir_commit20
redir_commit10:
	xor	ax,ax			; success
redir_commit20:
	xchg	ax,bx			; return result in BX
	ret


eject
;	CREATE NEW FILE

;	+----+----+----+----+----+----+----+----+
;	|    51   |        name       |  mode   |
;	+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name
;   mode:   attribute for file 

;	exit:
;	-----
;	BX:	file handle or error code ( < 0)

;	Note:	The function is identical to CREATE FILE
;		with the exception that an error is returned
;		if the specified file already exists.

redir_mknew:
	call	get_attrib_mode		; AX = mode, CX = attrib
	xchg	ax,cx
	or	ax,120h			; set ARCHIVE + MKNEW bits
	mov	file_attrib,ax
	mov	int2f_stack,ax
	mov	int2f_cmd,I2F_CREATE
	mov	file_mode,DHM_RW	; Open Compatibility mode, Read/Write
	jmp	redir_open_create_common

eject
;	LOCK/UNLOCK FILE DATA (LOCK/UNLOCK)

;	+----+----+----+----+----+----+----+----+
;	|    52   |  handle |       offset      |
;	+----+----+----+----+----+----+----+----+
;	|       length      |   lock  |
;	+----+----+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle
;	offset:	long integer offset
;	length:	long integer byte count
;	lock:	0 = lock, 1 = unlock

;	exit:
;	-----
;	BX:	byte count or error code ( < 0)

redir_lock:
; Lock uses I2F_LOCK, with CX,DX,SI as per INT 21, DI on stack
; Unlock uses I2F_UNLOCK with same.
	mov	bx,2[bp]		; BX -> parameter block
if DOS5
	push	bp
	lea	bp,4[bx]		; BP -> parameter block 
	mov	dx,bp			; as does DX
	mov	ax,I2F_LOCK
	mov	bl,ds:byte ptr 12[bx]
	mov	bh,5Ch			; lock/unlock in BX
	call	int2f_dhndl		; try the operation
	pop	bp
else
	mov	dx,ds:4[bx]		; get low word of offset in DX
	mov	cx,ds:6[bx]		;  and the hi word in CX
	mov	di,ds:8[bx]		; get low word of length in DI
	mov	si,ds:10[bx]		;  and high in SI
	mov	int2f_stack,di		; length low is on stack
	mov	ax,I2F_LOCK		; assume lock
	cmp	ds:word ptr 12[bx],0
	 je	redir_lock10
	mov	ax,I2F_UNLOCK		; no, it must be unlock
redir_lock10:
	call	int2f_dhndl		; try the operation
endif
	 jc	redir_lock20
	xor	ax,ax			; success
redir_lock20:
	xchg	ax,bx			; return result in BX
	ret


eject
;	EXPAND FILE

;	+----+----+----+----+----+----+----+----+----+----+
;	|    55   |      old name     |      new name     |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	old name:	segmented address of ASCIIZ name
;	new name:	segmented address of ASCIIZ name

;	exit:
;	-----
;	BX:	0000 or error code ( < 0)

; If we got here pri_pathname already contains expanded name - just copy it
; back to the users buffer.

redir_expand:
;------------
	mov	si,2[bp]		; SI -> parameter block
	les	di,10[si]		; ES:DI -> user supplied name
	mov	si,offset pri_pathname
	call	copy_asciiz
	xor	bx,bx			; no errors
	ret

eject
;	RENAME FILE

;	+----+----+----+----+----+----+----+----+----+----+
;	|    56   |      old name     |      new name     |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	old name:	segmented address of ASCIIZ name
;	new name:	segmented address of ASCIIZ name

;	exit:
;	-----
;	BX:	0000 or error code ( < 0)

;	Note:	R/O files can be renamed.
;			  ---

redir_move:
	mov	ax,I2F_REN		; it's a rename
	jmp	redir_unlink_move_common

eject
;	GET/SET FILE DATE/TIME

;	+----+----+----+----+----+----+----+----+----+----+
;	|    57   |  handle |   mode  |   date  |   time  |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle
;	mode:	0 = get date/time, 1 = set date/time
;	date:	date as in directory FCB
;	time:	time as in directory FCB

;	exit:
;	-----
;	BX:	0000 or error code ( < 0)
;	date:	date of last modification if mode = 0
;	time:	date of last modification if mode = 0

redir_dattim:
;------------
	mov	si,2[bp]		; DS:SI -> param block
	cmp	ds:word ptr 4[si],1	; is it set ?
	 ja	redir_dattim30		; if illegal say so
	 je	redir_dattim10
	mov	ax,es:DHNDL_DATE[bx]	; we need to copy the file date
	mov	word ptr 6[si],ax	;  into the parameter block
	mov	ax,es:DHNDL_TIME[bx]
	mov	word ptr 8[si],ax	; and the time	
	jmps	redir_dattim20
redir_dattim10:
	mov	ax,word ptr 6[si]	; copy the date we are given
	mov	es:DHNDL_DATE[bx],ax	;  into the DOSHNDL
	mov	ax,word ptr 8[si]	; and the time	
	mov	es:DHNDL_TIME[bx],ax
	or	es:DHNDL_WATTR[bx],DHAT_TIMEOK
	and	es:DHNDL_WATTR[bx],not DHAT_CLEAN
redir_dattim20:
	xor	bx,bx			;  all went OK
	jmps	redir_dattim40
redir_dattim30:
	mov	bx,ED_FUNCTION		; bad function number
redir_dattim40:
	ret

	Public	redir_build_path

; AH contains state of path processing, and is normally zero

BP_DOT		equ	1	; we have had a DOT end are processing the .EXT
BP_WILD		equ	2	; we have encountered a wild card
BP_BSLASH	equ	4	; the last character was a BSLASH

; May be called with or without MXDisk

redir_build_path:
;----------------
; On Entry:
;	DS:SI -> source name
;	ES:DI -> destination name (in PCMODE data segment)
;	current_ldt -> LDT_ for specified drive
; On Exit:
;	CY clear if we could build the path
;
; firstly we discard any drive portion
	lodsw				; get 1st two characters
	cmp	ah,':'			; is a drive specified ?
	 jne	redir_bp10		; discard "d:" if so
	lodsw
redir_bp10:
; Is it a "\\server\sharename\subdir\filename.ext" format ?
	call	check_dslash		; if it is \\
	 je	redir_bp20		;  then just copy the lot
; it's a normal "A:\subdir\filename.ext" pathname format
	dec	si			; we have swallowed the 1st two
	dec	si			;  chars - now we change our mind
	push ds ! push si
	lds	si,es:current_ldt
	mov	cx,ds:LDT_ROOTLEN[si]	; copy the root portion of the name
	rep	movsb			; copy the server stub name
	mov	dx,di			; ES:DX -> root position
	call	check_slash		; do we start from the root ?
	 je	redir_bp11		; yes, then that's all
	call	copy_asciiz		; no, copy the rest of the path
	call	redir_bp_append_slash	; append '\' for our path processing
redir_bp11:
	pop si ! pop ds
	jmps	redir_bp_next_level	; continue processing given path

redir_bp20:
; It is a "\\server\sharename\subdir\filename.ext" format
	stosw				; copy the "\\"
	xor	ax,ax			; clear "flags" in AH
redir_bp21:
	lodsb				; work along the name
	test	al,al			; unexpected end of name ?
	 jz	redir_bp_exit20		; go with what we've got....
	call	check_slash		; have we found the root '\' ?
	 je	redir_bp23
if KANJI
	call	dbcs_lead		; is it the 1st of a kanji pair
	 jne	redir_bp22		; no, onto next char
	stosb				; copy the 1st character
	movsb				; copy 2nd byte of KANJI pair
	jmps	redir_bp21		; now we can move onto next char
redir_bp22:
endif
	call	toupper			; upper case the character
	stosb				; copy the character
	jmps	redir_bp21		; go and do another one

redir_bp23:
	mov	dx,di			; bodge root to the top
;	mov	al,'\'
	stosb				; put in a '\'
	jmps	redir_bp_next_level	; yes, this is the new "root"

; We have reached the terminating NUL
redir_bp_exit:
	test	ah,BP_WILD		; did we get any wildcards ?
	 je	redir_bp_exit10
	mov	si,2[bp]		; SS:SI -> parameter block
	mov	al,ss:[si]		; fdos code number
	cmp	al,MS_X_FIRST		; search first ?
	 je	redir_bp_exit10
	cmp	al,FD_EXPAND		; expand pathname
	 je	redir_bp_exit10
	cmp	ss:remote_call,0	; should we wildcard REN/DEL ?
	 je	redir_bp_ED_PATH	; reject as not allowed here
	cmp	al,MS_X_RENAME		; rename ?
	 je	redir_bp_exit10
	cmp	al,MS_X_UNLINK		; delete ?
	 jne	redir_bp_ED_PATH	; reject as not allowed here
redir_bp_exit10:
	xor	al,al			; make sure our string is zero
	stosb				;  terminated
	call	redir_bp_append_slash	; append a trailing '\' if we don't
	dec	di			;  have one so we can remove it
	cmp	di,dx			; are we talking about the root ?
	 jne	redir_bp_exit20
	cmp	es:byte ptr 0FFFFh[di],':'
	 jne	redir_bp_exit20		; if we have a trailing ':' allow
	inc	di			;  a '\' at the root
redir_bp_exit20:
	xor	al,al			;  KANJI aware way of removing trailing
	stosb				;  '\' from the path....
;	clc
	ret


redir_bp_next_level:
; DS:SI -> source pathname, ES:DI -> destination pathname, ES:DX -> root
; AH = current status
	mov	cx,8			; expansion count ready
	xor	ax,ax
redir_bp_next_char:
	lodsb
	test	al,al			; end of the line ?
	 jz	redir_bp_exit		; do the exit

; Is it a '\' ?
redir_bp40:
	call	check_slash		; was it a seperator ?
	 jne	redir_bp50
	test	ah,BP_WILD		; have we encountered wildcards ?
	 jnz	redir_bp_ED_PATH	; reject as not allowed here
	mov	al,'\'			; make sure it's a BSLASH
	stosb
	jmps	redir_bp_next_level	; start at a new level

; Is it a '.' ?
redir_bp50:
	cmp	al,'.'			; seperator ?
	 jne	redir_bp60
	lodsb				; get next letter
	test	al,al			; is it trailing '.' ?
	 je	redir_bp_exit		;  then exit now
	cmp	cx,8			; check for '.' and '..'
	 je	redir_bp52		;  if at start of field
redir_bp51:				; othewise it's a ".EXT"
	mov	bx,ED_FILE		; assume we have a problem
	test	ah,BP_DOT		; have we had an extention before ?
	 jnz	redir_bp_err		;  we've got a problem
	dec	si			; rewind a character
	mov	ax,'.'+256*BP_DOT	; so we are back to the '.'
	stosb				; store the '.' in destination
	mov	cx,3			; expand the extention
	jmps	redir_bp_next_char
redir_bp52:				; It might be a "." or ".."
	call	check_slash		; is it '.\'
	 je	redir_bp_next_char	; discard them both
	cmp	al,'.'			; is it '..' ?
	 jne	redir_bp51		; no, must be an extention after all
	call	redir_bp_ddot		; rewind a level
	 jnc	redir_bp_next_level	; onto next level
redir_bp_ED_PATH:
; set CY flag to indicate error then return
	mov	bx,ED_PATH		; return "invalid path" error
redir_bp_err:
	stc
	ret
	
; Is it a '*' ?
redir_bp60:
	call	check_delim		; is it a delimiter ?
	 jz	redir_bp_ED_PATH	; thats illegal at this point
	cmp	al,'*'			; wildcards ?
	 jne	redir_bp70
	mov	al,'?'
	rep	stosb			; expand it
;	or	ah,BP_WILD		; remember wild-card encountered
;	jmps	redir_bp_next_char	;  check for wildcards
;
;	 				; we can just fall through
;
redir_bp70:
	cmp	al,'?'			; wild card is possibly OK
	 jne	redir_bp80
	or	ah,BP_WILD		; remember wild-card encountered
redir_bp80:
; Normal Character
; AL contains a normal character for us to process.
; Uppercase the character, look out for KANJI etc
	 jcxz	redir_bp_next_char	; discard if no space is left
	dec	cx			; one less to expand
if KANJI
	call	dbcs_lead		; is it the 1st of a kanji pair
	 jne	redir_bp90
	inc	si			; skip 2nd byte
	 jcxz	redir_bp_next_char	; discard if no room for kanji char
	dec	cx			; one less to expand
	stosb				; store 1st byte of kanji character
	dec	si			; point at 2nd byte again
	movsb				; copy 2nd byte of kanji character
	jmps	redir_bp_next_char
redir_bp90:
endif
	call	toupper			; make it upper case
	stosb				; plant the character
	jmps	redir_bp_next_char


redir_bp_ddot:
; We have encountered a '..' in a pathname, so rewind up a level
; On Entry:
; DS:SI -> source position, ES:DI -> destination position
; ES:DX -> root position, AL = Character
; On Exit:
; ES:DI -> start of field one level up, unless we get an error when CY set.
	lodsb				; look at char after '..'
	call	check_slash		; is it '..\'
	 je	redir_bp_ddot10
	test	al,al			; is it a trailing '..'
	 jnz	redir_bp_ED_PATH	; no, anything else is bad...
	dec	si			; trailing '..', rewind to NUL
redir_bp_ddot10:
	dec	di			; move back to last '\'
	cmp	di,dx			; are we at the root anyway ?
	 jle	redir_bp_ED_PATH	;  then don't discard any
; We now start at ES:DX and work along till ES:DI -> char after last '\'
	push ds ! push si
	push es ! pop ds
	mov	si,dx			; DS:SI -> char after root
	mov	cx,dx			; last '\' position in CX
redir_bp_ddot20:
	lodsb
	cmp	si,di			; end of the line yet ?
	 jae	redir_bp_ddot30		; yes, stop now
if KANJI
	call	dbcs_lead		; is it 1st of a kanji pair ?
	 jne	redir_bp_ddot25
	lodsb				; skip the 2nd too
	jmps	redir_bp_ddot20		; then go on to next char
redir_bp_ddot25:
endif
	call	check_slash		; is it a '\'
	 jne	redir_bp_ddot20		; no, do next
	mov	cx,si			; save position after the '\'
	jmps	redir_bp_ddot20
redir_bp_ddot30:
	mov	di,cx			; last '\' was here..
	pop si ! pop ds
	clc				; no errors
	ret

redir_bp_append_slash:	
; On Entry:
;	ES:DX -> ASCIIZ string to append a '\' to
; On Exit:
;	ES:DI -> terminating NUL
;	All but AX preserved
;
	xor	ax,ax			; AH = 0, initially no previous char
	mov	di,dx			; start processing from here
redir_bp_append_slash10:
	mov	ah,al			; save previous char in AH
	mov	al,es:[di]		; get a character
	inc	di			; point to next
	test	al,al			; is it NUL ?
if KANJI
	 je	redir_bp_append_slash20
	call	dbcs_lead		; is it 1st of a kanji pair ?
	 jne	redir_bp_append_slash10
	inc	di			; skip the 2nd too
	jmps	redir_bp_append_slash10	;  (it might be '\')
redir_bp_append_slash20:
else
	 jnz	redir_bp_append_slash10
endif
	mov	al,ah			; get previous char
	call	check_slash		; did we have a '\' ?
	 je	redir_bp_append_slash30
	dec	di			; ES:DI -> existing NUL
	mov	ax,'\'
	stosw				; new terminating '\'
redir_bp_append_slash30:
	dec	di			; ES:DI -> NUL
	ret



get_attrib_mode:
;---------------
; On Entry:
;	SS:2[BP] -> param block
; On Exit:
;	AX = file mode
;	CX = file attributes
;
	mov	si,2[bp]		; SI -> parameter block
	mov	ax,ds:6[si]		; get mode
	mov	cx,ds:8[si]		; get attribs
	mov	file_mode,ax		; save open mode
	mov	file_attrib,cx		; save file attribute
	ret

int2f_ldt:
;---------
; On Entry:
;	AX = Int2F command
; On Exit:
;	CY clear, AX returned unchanged
;	CY set, AX = Our Negative Error Code
;
	les	di,ss:current_ldt
	jmps	int2f_op

int2f_dhndl:
;----------
; On Entry:
;	AX = Int2F command
; On Exit:
;	CY clear, AX returned unchanged
;	CY set, AX = Our Negative Error Code
;
	les	di,ss:current_dhndl
int2f_op:
	push	ds
	push ss ! pop ds
	push	int2f_stack		; put word on stack
	int	2fh			; get the info
	pop	int2f_stack		; clean up the stack
	pop	ds
	 jnc	int2f_op10
	neg	ax			; AX = we keep error codes negative
;	stc				; we have a problem
int2f_op10:
	ret

end
