; DIRS.ASM
; (c) 1989, 1990 Ashok P. Nadkarni
;
;This module implements the directory stack feature of the cmdedit program.
;
;jmh 990521: removed the check_dircmd_allowed code.
;	     replaced function abort_dir_op with value [abort_dir_op].

	INCLUDE ascii.inc
	INCLUDE common.inc
	INCLUDE general.inc
	INCLUDE	buffers.inc
	INCLUDE dos.inc
	INCLUDE bios.inc

	PUBLIC	dir_stk 		;added by jmh 980509
	PUBLIC	execute_popd
	PUBLIC	execute_pushd
	PUBLIC	execute_chd
	PUBLIC	execute_rstdir
	PUBLIC	execute_backdir		;added by wd
	PUBLIC	save_drv_path		;added by jmh 980624

	PUBLIC	Dirs_instance_offset	;added by jmh
	PUBLIC	Dirs_instance_size

PATHBUF_SIZE EQU 68

CSEG	SEGMENT	BYTE PUBLIC 'CODE'

	EXTRN	user_command:BYTE
	EXTRN	in_appl:BYTE
	EXTRN	linebuf:BYTE		;line buffer
	EXTRN	lastchar:WORD
	EXTRN	abort_dir_op:WORD	;added by jmh 990521
	EXTRN	line_too_long:PROC	;added by jmh 990520
	EXTRN	skip_whitespace:PROC
	EXTRN	skip_nonwhite:PROC
	EXTRN	tolower:PROC
	EXTRN	strlen:PROC		;added by jmh 980623
	EXTRN	strsilen:PROC		;added by jmh 990516
	EXTRN	get_string:PROC 	;added by jmh 990522

DGROUP	GROUP	CSEG
	ASSUME	CS:DGROUP,DS:DGROUP,ES:DGROUP,SS:DGROUP

Dirs_instance_offset = $

dir_stk	$string_stack	<>	;Buffer descriptors
backbuf db PATHBUF_SIZE DUP (0) ;Buffer for backdir command added by wd
				;(jmh 990521: moved from cmdedit.asm)

Dirs_instance_size = $ - offset Dirs_instance_offset


;+
; FUNCTION: execute_rstdir
;
;	Called to reset the directory stack.
;
; Parameters:
;	None.
;-
execute_rstdir proc near
	mov	bx,offset DGROUP:dir_stk
	jmp	near ptr strstk_reset
;	ret				;commented jmh 990517
execute_rstdir endp


;+
; FUNCTION : dir_push
;
;	Called to push a drive and path onto the directory stack. The
;	string must end in a 0 byte.
;
; Parameters:
;	DX	->string to push
;
; Returns:
;	CF	= 1 if errors
;		  0 no errors
;
; Register(s) destroyed:
;	AX,BX,CX,DX
;-
dir_push proc	near
	@save	di
	mov	di,dx				;di->string
	call	near ptr strlen
	xchg	ax,cx
	inc	ax				;AX<-length of string
;						 including null terminator
	mov	bx,offset DGROUP:dir_stk
	call	near ptr strstk_push		;Params AX,BX,DX
	; strstk_push sets/resets CF
	@restore
	ret
dir_push endp



;+
; FUNCTION : save_drv_path
;
;	Saves the current drive and path in the specified buffer in the
;	form `D:\dir1....' where the current drive letter. The buffer
;	MUST be long enough.
;
; Parameters:
;	AX	= address of save buffer
;
; Returns:
;	Nothing.
;
; Register(s) destroyed:
;	AX,DX
;-
save_cur_path	label near			;added by jmh 990521
	mov	ax,offset backbuf

save_drv_path	proc near
	@save	si,di
	xchg	di,ax				;DI->save buffer
	@GetDrv					;AL<-drive number
	add	al,'A'		;AL<-drive letter. wd changed 'a' to 'A'
	stosb					;Remember current drive
	mov	ax,':' or ('\' shl 8)           ;jmh 980511: replaced two
	stosw					; stosb's with one stosw
	@GetDir di				;Remember current path
	@restore
	ret
save_drv_path	endp



;+
; FUNCTION : change_drv_path
;
;	Changes the current drive and directory to the one specified.
;
; Parameters:
;	AX	->path in the form `D:\dir1\dir2\...' terminated
;		  with a 0 byte where D is the drive letter. Entire
;		  path name need not be given. It is OK if the last
;		  directory in the path is followed by a '\' or a '/'.
;
; Returns:
;	CF	= 0 if current drive and directory succesfully changed,
;		  1 if any errors (drive and path both unchanged)
; Register(s) destroyed:
;
; jmh 990521: if an error occurs, the current directory does not change,
;	      so there's no point in remembering it. @SetDrv does not
;	      actually return an error, so use @GetDrv to validate.
; jmh 990522: do an LFN chdir first.
;-
change_drv_path proc near
	@save	si,di
	xchg	si,ax				;SI->new path
	call	near ptr strsilen 		;DI->byte after NULL
	dec	di
	dec	di				;DI->last character
	mov	al,[di]
	cmp	al,'\'                          ;Extra '\' char ?
	je	@change_drv_path_5		;Yes
	cmp	al,'/'                          ;Extra '/' char ?
	jne	@change_drv_path_10		;No
@change_drv_path_5:
	;If '\' at the end, make sure it is not of the form d:\ or just \
	cmp	byte ptr [di-1],':'
	je	@change_drv_path_10
	cmp	di,si				;Solitary '\' ?
	je	@change_drv_path_10		;Yes
	mov	[di],ch 			;No, replace '\' with 0
@change_drv_path_10:
	cmp	word ptr 1[si],':'              ;Drive only specified ?
	je	@change_drv_path_80		;Yep (jmh 990522: done first)
	@LFNChDir si
	jnc	@change_drv_path_80
	cmp	ax,7100h			;LFN supported?
	jne	@change_drv_path_85		;Yes, directory doesn't exist
	@ChDir
	jc	@change_drv_path_85		;Error
@change_drv_path_80:
	lodsw					;AL<-drive
	cmp	ah,':'                          ;Drive spec ?
	clc					;To indicate success
	jne	@change_drv_path_99		;No, all done
;	call	near ptr tolower
;	sub	al,'a'                          ;AL<-drive number
	and	al,31				;jmh 990521: who says drive
	dec	ax				; needs to be a letter?
	@SetDrv al				;Change the drive (DL<-drive)
	@GetDrv 				;Get the drive (AL<-drive)
	cmp	al,dl
	je	@change_drv_path_99		;If they're equal, no carry
@change_drv_path_85:
	stc					;Indicate error to caller
@change_drv_path_99:
	@restore
	ret
change_drv_path endp



;+
; FUNCTION : execute_backdir by wd
;
;	Go to the previously visited directory.
;
; Parameters:
;	None.
;
; Returns:
;	Nothing.
;
; Register(s) destroyed:
;	AX,BX,CX,DX
;-
execute_backdir proc near
	mov	bx,offset backbuf
	cmp	byte ptr [bx],0
	jne	@execute_backdir_99
	ret

@execute_backdir_99:
	mov	dx,bx
	call	near ptr dir_push
;jmh 990521: fall through to execute_popd
execute_backdir endp



;+
; FUNCTION : execute_popd
;
;	Pops the topmost element off the directory stack and makes it
;	the current drive/directory.
;
; Parameters:
;	None.
;
; Returns:
;	Nothing.
;
; Register(s) destroyed:
;	AX,BX,CX,DX
;-
execute_popd proc near
	@link	PATHBUF_SIZE
topdir	equ	PATHBUF_SIZE
	call	near ptr save_cur_path
	mov	bx,offset DGROUP:dir_stk	;BX->dir stack descriptor
	call	near ptr strstk_settop
	lea	ax,[bp-topdir]
	push	ax				;added by jmh 980511
	mov	cx,PATHBUF_SIZE
	call	near ptr strstk_copy		;Get top of stack
;						 String is terminated
;						 by a 0 byte
;	jc	@dirstk_empty			;jmh 990521: always enough
	or	ax,ax				;Empty stack ?
	jz	@dirstk_empty			;Yes, error
	pop	ax				;AX->new path
	call	near ptr change_drv_path	;Change drive and path
	jc	@dirstk_empty
	call	near ptr strstk_kill		;Delete top of stack
	jmp	short @end_dircmd		;added by jmh 980511

@dirstk_empty:
	mov	al,E_DIRSTK_EMPTY
@abort_dircmd:
	jmp	abort_dir_op
execute_popd	endp



;+
; FUNCTION : execute_chd
;
;	Changes to the disk and directory specified as parameter.
;
; Parameters:
;	SI	-> first char in linebuf following this command
;	CX	= remaining num chars in line
;
; Returns:
;	CF	= 0 if successful
;		= 1 if no parameter
;
; Register(s) destroyed:
;	AX,BX,CX,DX
;-
execute_chd	proc	near
	call	near ptr save_cur_path		;Save current drive and path
	mov	ah,2				;Strip quotes
	call	near ptr get_string
	jc	@execute_chd_99 		;No args

	mov	byte ptr [si],ch		;Terminating 0 byte
;	We are guaranteed place for a 0 because of the dummy byte after linebuf
	xchg	ax,di				;AX->dir to change to
	call	near ptr change_drv_path	;Change path and drive
	jnc	@execute_chd_99 		;No error

@dirstk_error:
	mov	al,E_DIRSTK
	jmp	@abort_dircmd

@end_dircmd:
	@unlink
@execute_chd_99:
; Don't modify carry!
; Klugery - return a zero length string to DOS in order to set prompt properly.
	mov	lastchar,offset DGROUP:linebuf
	mov	user_command,1			;indicate line to be
;						 returned to DOS
	ret

execute_chd endp



;+
; FUNCTION : execute_pushd
;
;	If a parameter is given, pushes current directory/disk onto the
;	dir stack and changes to the parameter. If no parameter,
;	exchange the top of directory stack with current disk-directory.
;
; Parameters:
;	SI	-> first char in linebuf following this command
;	CX	= remaining num chars in line
;
; Returns:
;	Nothing.
;
; Register(s) destroyed:
;	AX,BX,CX,DX
;
; jmh 980511: removed wd's commented code.
;-
execute_pushd	proc	near
	@link	PATHBUF_SIZE
new_path	equ <byte ptr [bp-PATHBUF_SIZE]>

	call	near ptr execute_chd
	jc	@execute_pushd_50		;Exchange directory
;						 with top of stack
	mov	dx,offset backbuf
	call	near ptr dir_push		;Push onto stack
	jnc	@end_dircmd			;No error

@execute_pushd_30:
; Error. Restore original directory and display error message.
	mov	ax,offset backbuf
	call	near ptr change_drv_path
	jmp	@dirstk_error

@execute_pushd_50:
; Exhange current directory with the directory at the top of the stack
	mov	bx,offset DGROUP:dir_stk	;BX->dir stack descriptor
	call	near ptr strstk_settop
	lea	ax,new_path
	mov	cx,PATHBUF_SIZE
	call	near ptr strstk_copy		;Get top of stack
;						 String is terminated
;						 by a 0 byte
; It will always fit, since it can never push more than PATHBUF_SIZE.
	or	ax,ax				;Empty stack ?
	jz	@execute_pushd_55		;Yes, error
	lea	ax,new_path			;AX->new path
	call	near ptr change_drv_path	;Change drive and path
	jc	@dirstk_error
	call	near ptr strstk_kill		;Delete top of stack
	mov	dx,offset backbuf
	call	near ptr dir_push
	jnc	@end_dircmd			;All set
;	Error. Restore original top of stack
	lea	dx,new_path
	call	near ptr dir_push		;Push must succeed
	jmp	short @execute_pushd_30

@execute_pushd_55:
	mov	ax,offset backbuf
	call	near ptr change_drv_path
	jmp	@dirstk_empty

execute_pushd	endp



CSEG	ENDS

	END
