RENDIR segment para public 'code'
	assume	cs:RENDIR, ds:RENDIR, es:RENDIR, ss:NOTHING
	org	100h		; .COM format
BEGIN:
	jmp	CODE_START	; Jump around data declarations
DECLARE:	; Equates and Storage allocation for messages & data areas
	COPYRIGHT	db	'RENDIR (C) 1986, Ziff-Davis Publishing Co.'
			db	13,10,'$'
	PATH_FILE_LEN	equ	77  ;Length = 1, Path = 63, FileName = 12, 0 = 1
	SOURCE_NAME	db	PATH_FILE_LEN dup (0)
	TARGET_NAME	db	PATH_FILE_LEN dup (0)
	TARGET_END	dw	$ - 1
	VALID_IN	db	'/abcdefghijklmnopqrstuvwxyz,;=',9
	VALID_OUT	db	'\ABCDEFGHIJKLMNOPQRSTUVWXYZ',4 dup(32)
	VALID_NUM	equ	$ - VALID_OUT + 1
	ERR_HEAD	db	10,13,'RENDIR Error - $'
	BAD_VERSION	db	'Must be PC-DOS 3.0 or Higher$'
	NO_PARMS	db	'Correct Syntax is:',13,10,10
			db	'RENDIR [D:][Source_Path]Source_Name[.Ext] '
			db	'Target_Name[.Ext]$'
	NO_TARGET	db	'No Target Name Specified$'
	BAD_TARGET	db	'Invalid "\" or ":" in Target Name$'
	DIR_NOT_FOUND	db	'Source Directory Not Found$'
	NOT_DIRECTORY	db	'Source File Not a Directory$'
	PATH_NOT_FOUND	db	'Source or Target Path Not Found$'
	ACCESS_DENIED	db	'Access to Target Denied or Target Exists$'
	PC_DOS_2_PATCH	db	13,10,'or $'
	DRIVES_CONFLICT db	'Source and Target Disk Drives Conflict$'
	UNDEFINED_ERR	db	'Undefined Error: PC-DOS Function 56H$'
	ERR_TAIL	db	10,10,13,' . . . Aborting',10,13,13,'$'
	GOOD_MSG1	db	10,13,'Directory "$'
	GOOD_MSG2	db	'" . . renamed to . . "$'
	GOOD_MSG3	db	'"$'
	END_LINE	db	10,13,'$'
CODE_START:	; Processing logic starts here
	mov	dx,offset COPYRIGHT	; Display copyright notice
	mov	ah,9h			; Request display string
	int	21h			; Call PC-DOS
	mov	ah,30h			; Request PC-DOS version
	int	21h			; Call PC-DOS
	cmp	al,3			; Must be 3.0 or higher
	jae	VERSION_OK		; If it is, keep going
	mov	dx,offset BAD_VERSION	; Otherwise, exit with error
	jmp	ERROR_EXIT
VERSION_OK:	; Parse command line into source name and target name
	mov	si,80h			; PSP parameter byte count pointer
	mov	cl,[si] 		; Move byte count to CL
	xor	ch,ch			; Zero CH
	jcxz	PARMS_NO_GOOD		; If CX is zero, there are no parameters
	mov	dx,cx			; Save byte count in dx
	inc	si			; Point to parameter area
	mov	di,si			; Copy SI to DI for cleanup routine
	cld				; Set direction flag to forward
CLEAN_PARMS:	; Change valid delimiters to blanks, lower to upper case
	lodsb				; Load each character to AL
	push	di			; Save DI on stack
	mov	di,offset VALID_IN	; Point to table of valid inputs
	push	cx			; Save CX on stack
	mov	cx,VALID_NUM		; Set CX to number of inputs to look for
repne	scasb				; See if any are in AL
	jcxz	CLEAN_END		; If not, change nothing
	mov	bx,VALID_NUM		; Set up BX to point to valid output
	sub	bx,cx			; This will leave BX one off
	mov	al,VALID_OUT [bx - 1]	; Load the valid output to AL
CLEAN_END:
	pop	cx			; Restore CX
	pop	di			; Restore DI
	stosb				; Store modified AL back to PSP
loop	CLEAN_PARMS			; Loop until CX is zero
	mov	cx,dx			; Restore number of bytes in PSP to CX
	mov	dx,2			; Set DX to look for up to 2 parameters
	mov	bx,offset SOURCE_NAME	; Set BX to address of 1st parameter
	mov	al,' '                  ; Set up to scan for first non-blank
	mov	di,81h			; Set DI to PC-DOS parameter pointer
FIND_PARMS:	; Start looking for parameters, load to program storage
repe	scasb				; Scan while blanks
	mov	si,di			; Set SI to second non-blank byte
	dec	si			; Adjust it to first non-blank byte
	inc	cx			; Adjust CX to compensate
	jcxz	PARMS_LOADED		; If CX is zero, no parameters left
	mov	di,bx			; Set DI to parameter hold area
	mov	ax,cx			; Store CX to first byte of hold area
	stosb				; DI is adjusted to second byte here
STORE:	lodsb				; Load each byte to AL
	cmp	al,' '                  ; Is it a blank?
	jz	END_STORE		; Yes, end of this parameter
	stosb				; No, store the byte to hold area
END_STORE:
	loopnz	STORE			; Keep looking
	sub	[bx],cx 		; Store number of bytes in each
	jcxz	PARMS_LOADED		; If CX is zero, no more parameters
	dec	byte ptr [bx]		; parameter to first byte of hold area
	mov	di,si			; Set up to scan for next non-blank
	dec	di			; Adjust DI to point to the blank
	inc	cx			; Adjust CX to compensate
	dec	dx			; Decrement DX counter
	cmp	dx,0			; Is DX zero?
	jz	PARMS_LOADED		; Yes, all expected parameters loaded
	add	bx,PATH_FILE_LEN	; No, point to next part of hold area
	jmp	FIND_PARMS		; Go back and look for more
PARMS_LOADED:				; All parameters are loaded
	cmp	SOURCE_NAME[0],0	; If there are no bytes in the
	je	PARMS_NO_GOOD		; SOURCE_NAME, no parameters present
	cmp	TARGET_NAME[0],0	; Same for TARGET_NAME
	ja	CHECK_PARMS
PARMS_NO_GOOD:				; Exit with an error if there
	mov	dx,offset NO_PARMS	; are no or incorrect parameters passed
	jmp	ERROR_EXIT
CHECK_PARMS:				; Check for '\' or ':' in target name
	mov	di,TARGET_END		; Point DI to end of parameter area
	mov	si,offset TARGET_NAME	; Point SI to number of bytes area
	lodsb				; Load number of bytes to AL
	xor	ah,ah			; Zero AH
	mov	cx,ax			; Move to CX for loop
	push	cx			; Save CX on stack
	add	si,cx			; Adjust SI to end of target name
	dec	si			;
	std				; Set direction flag to reverse
MOVE_TARGET:				; Move target to end of area while
	lodsb				; checking for '\'
	cmp	al,'\'
	je	BAD_TARGET_FOUND	; If found, target is bad
	cmp	al,':'
	je	BAD_TARGET_FOUND
	stosb				; Store byte to end of area
	loop	MOVE_TARGET		; Keep looking
	add	di,1			; Adjust DI
	push	di			; Save DI on stack
	jmp	FIND_SOURCE_DIR 	; Go process source name
BAD_TARGET_FOUND:			; Exit with error if target
	mov	dx,offset BAD_TARGET	; contains a '\'
	jmp	ERROR_EXIT
FIND_SOURCE_DIR:			; Look for end of path name in source
	mov	si,offset SOURCE_NAME	; Point SI to number of bytes
	lodsb				; Load number of bytes to AL
	xor	ah,ah			; Zero AH
	mov	di,si			; Move SI to DI for scan
	mov	cx,ax			; Move AX to CX for scan repeat
	add	di,cx			; Adjust DI to end of source name
	dec	di			;
	mov	al,'\'                  ; Scan for '\'
repnz	scasb
	cld				; Set direction flag to forward
	jnz	NO_DIR			; Directory is in current directory
	dec	cx			; Adjust CX
	mov	di,offset TARGET_NAME + 1	; Point DI to target name
	mov	si,offset SOURCE_NAME + 1	; Point SI to source name
rep	movsb				; Move path name to target name
	jmp	MOVE_NAME		; DI points to position for target name
NO_DIR: 				; No path, point to beginning of name
	cmp	SOURCE_NAME[2],':'      ; Check for disk drive specification
	jne	NO_DISK
	mov	di,offset TARGET_NAME + 1	; Point DI to target name
	mov	si,offset SOURCE_NAME + 1	; Point SI to source name
	mov	cx,2			; Move the two bytes into target name
rep	movsb				; that contain disk drive specification
	jmp	MOVE_NAME
NO_DISK:
	mov	di,offset TARGET_NAME + 1
MOVE_NAME:				; Move target name to correct position
	pop	si			; Restore saved DI in SI
	pop	cx			; Restore CX
rep	movsb
	mov	al,0			; Finish ASCIIZ string with a binary 0
	stosb
FIND_DIRECTORY: 			; Does source directory exist?
	mov	dx,offset SOURCE_NAME+1 ; DX points to SOURCE_NAME
	mov	ah,4eh			; Request find file
	mov	cx,10h			; Set attribute for directories
	int	21h			; Call PC-DOS
	jnc	FOUND_FILE		; If no error, a file was found
	jmp	NO_DIRECTORY
FOUND_FILE:
	mov	si,95h			; Position of attribute byte in DTA
	lodsb				; Load attribute byte to AL
	and	al,10h			; Check to see if it's a directory
	jnz	RENAME_DIRECTORY	; If not zero, it's a directory
	mov	dx,offset NOT_DIRECTORY
	jmp	ERROR_EXIT
NO_DIRECTORY:				; Exit with error if no directory
	mov	dx,offset DIR_NOT_FOUND ; If file not found, exit
	jmp	ERROR_EXIT		; program with error message
RENAME_DIRECTORY:
	mov	dx,offset SOURCE_NAME + 1	; DX points to old file name
	mov	di,offset TARGET_NAME + 1	; DI points to new file name
	mov	ah,56h			; Request rename file
	int	21h			; Call PC-DOS
	jnc	GOOD_RENDIR		; If no error, call was successful
	cmp	ax,3			; Check for error 3 (path not found)
	jne	ERR_5
	mov	dx,offset PATH_NOT_FOUND
	jmp	ERROR_EXIT		; Exit program with error message
ERR_5:	cmp	ax,5			; Check for error 5 (file inaccessible)
	jne	ERR_17
	mov	dx,offset ACCESS_DENIED
	jmp	ERROR_EXIT
ERR_17: cmp	ax,17			; Check for error 17 (drive conflict)
	jne	UNDEF
	mov	dx,offset DRIVES_CONFLICT
	jmp	ERROR_EXIT		; Exit program with error message
UNDEF:	mov	dx,offset UNDEFINED_ERR ; Undefined error from function 56H
	jmp	ERROR_EXIT		; Exit program with error message
GOOD_RENDIR:				; Exit with message

	mov	ah,9h			; Request display string
	mov	dx,offset GOOD_MSG1	; Display GOOD_MSG1
	int	21h			; Call PC-DOS
	mov	cx,2			; 2 fields - source & target file
	mov	bx,offset SOURCE_NAME + 1	; Point to source file
START1: mov	si,bx			; Copy BX to SI
START2: lodsb				; Load each byte to AL
	cmp	al,0			; If ASCII 0, end of field
	je	BETWEEN
	mov	dl,al			; Copy byte to DL for funciton 2H
	mov	ah,2h			; Request display one byte
	int	21h			; Call PC-DOS
	jmp	START2			; Get next character
BETWEEN:cmp	cx,2			; Is it first or second field?
	jne	CR_LF			; If second, display end of message
	mov	ah,9h			; Request display string
	mov	dx,offset GOOD_MSG2	; Display GOOD_MSG2
	int	21h			; Call PC-DOS
	jmp	NEXT			; Go process next field
CR_LF:	mov	ah,9h			; Request display string
	mov	dx,offset GOOD_MSG3	; Display GOOD_MSG3
	int	21h			; Call PC-DOS
	mov	dx,offset END_LINE	; Terminate display line
	mov	ah,9h			; Request display string
	int	21h			; Call PC-DOS
NEXT:	add	bx,PATH_FILE_LEN	; Move BX to point to next field
	loop	START1			; Loop for second field
	jmp	EXIT			; Exit RENDIR
ERROR_EXIT:				; Print Error Message and Exit
	push	dx			; Save error message pointer on stack
	mov	ah,9h			; Request display string
	mov	dx,offset ERR_HEAD	; Display error header
	int	21h			; Call PC-DOS
	pop	dx			; Display error message
	int	21h			; Call PC-DOS
	mov	dx,offset ERR_TAIL	; Display error tail
	int	21h			; Call PC-DOS
EXIT:	int	20h			; Exit to PC-DOS
RENDIR	ends
	end	BEGIN
