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
