        TITLE   'Cmd - RxDOS Command Shell'
        PAGE 59, 132
        .LALL

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  RxDOS Command Shell                                          ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Real Time Dos                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This material  was created as a published version  of a DOS  ;
        ;  equivalent product.   This program  logically  functions in  ;
        ;  the same way as  MSDOS functions and it  is  internal  data  ;
        ;  structure compliant with MSDOS 6.0                           ;
        ;                                                               ;
        ;  This product is distributed  AS IS and contains no warranty  ;
        ;  whatsoever,   including  warranty  of   merchantability  or  ;
        ;  fitness for a particular purpose.                            ;
        ;                                                               ;
        ;                                                               ;
        ;  (c) Copyright 1990, 1997. Api Software and Mike Podanoffsky  ;
        ;      All Rights Reserved Worldwide.                           ;
        ;                                                               ;
        ;  This product is protected under copyright laws and  may not  ;
        ;  be reproduced  in whole  or in part, in any form  or media,  ;
        ;  included but not limited to source listing, facsimile, data  ;
        ;  transmission, cd-rom, or  floppy disk without the expressed  ;
        ;  written consent of the author.                               ;
        ;                                                               ;
        ;  License  for  distribution  for commercial  use  or  resale  ;
        ;  required from:                                               ;
        ;                                                               ;
        ;  Api Software                                                 ;
        ;  12 South Walker Street                                       ;
        ;  Lowell,  MA   01851                                          ;
        ;                                                               ;
        ;  internet: mikep@world.std.com                                ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;  Compile with MASM 5.1                                        ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  RxDOS Command Shell                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Programmer's Notes:                                          ;
        ;                                                               ;
        ;  Command Shell consists of  two parts bound  together into a  ;
        ;  single executable load.  There  exists  a  single  resident  ;
        ;  command shell which is accessible by an Int 2Eh.             ;
        ;                                                               ;
        ;...............................................................;

        include rxdosmac.asm
        include rxdosdef.asm
        include rxdoscin.asm

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Execution Control                                            ;
        ;...............................................................;

        EXECCONTROL struc

exCtrlArgArray                  dd ?
exCtrlStdInHandle               dw ?
exCtrlStdOutHandle              dw ?

exCtrlFlags                     dw ?
exCtrlStdInFileName             db sizeEXPANDNAME dup(?)
exCtrlStdOutFileName            db sizeEXPANDNAME dup(?)
exCtrlExecBlock                 db sizeEXEC dup(?)

        EXECCONTROL ends

exCtrlPiped                     equ 8000h
exCtrlAppend                    equ 4000h               ; append to stdout
exTempInputFile                 equ 2000h               ; temp stdin created

sizeEXECCONTROL                 equ size EXECCONTROL
maxArgArray                     equ 64 

_BAT                            equ 0
_EXE                            equ 1
_COM                            equ 2

DYNAMIC_BUFFERSIZE              equ 4 * 1024            ; cannot exceed stack

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  If Special Values                                            ;
        ;...............................................................;

IF_NOT                          equ 01h
IF_ERRORLEVEL                   equ 02h
IF_EXIST                        equ 03h

_ARGTEXT                        equ _high
_ARGTYPE                        equ _low

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  RxDOS Command Shell                                          ;
        ;...............................................................;

        public CommandBegin

RxDOSCMD SEGMENT PARA PUBLIC 'CODE'
         assume cs:RxDOSCMD, ds:RxDOSCMD, es:RxDOSCMD, ss:RxDOSCMD

        org 100h

        public CheckOptOneArg
        public CmndError_BadSwitch
        public CmndError_CannotCopyUntoSelf
        public CmndError_CannotCreateFile
        public CmndError_ContentsLostBeforeCopy
        public CmndError_FileAlreadyExists
        public CmndError_FileNotFound
        public CmndError_InvalidDate
        public CmndError_InvalidDrive
        public CmndError_InvalidTime
        public CmndError_NoFilesFound
        public CmndError_SyntaxError
        public CmndError_InvalidNumberArguments
        public CmndError_OutOfEnvironmentSpace

        public CRLF
        public CmndLookup
        public _Copy_FilesCopied
        public CountArgs

        public deleteArg
        public deleteEnvVariable
        public DisplayErrorCode
        public DisplayErrorMessage
        public DisplayOutEnvSpace
        public DisplayLine
        public insertEnvVariable
        public PreProcessCmndLine
        public ReplaceForVariables
        public returnVolumeName
        public searchEnvVariable
        public setPagingMode

        public RxDOSIntl_DateTemplate
        public RxDOSIntl_DayOfWeek
        public RxDOSIntl_TimeTemplate
        public RxDOSIntl_DateTimeTable

        public RxDOS_AllFiles
        public RxDOS_DefaultPrompt
        public RxDOS_DTA
        public RxDOS_ForArgs
        public RxDOS_Prompt
        public RxDOS_PromptSpec
        public RxDOS_Version

        public _AppendPathName
        public _CmndParse_SeparatorCheck
        public _CopyString
        public _DirAttribSwitch
        public _DirBareSwitch
        public _DirLowerCaseSwitch
        public _DirOrderSwitch
        public _DirPauseSwitch
        public _DirSubDirSwitch
        public _DirSwitches
        public _DirWideSwitch
        public _Dir_DirectoryOf
        public _Dir_DirEntry
        public _Dir_FileEntry
        public _Dir_Files
        public _Dir_NoVolumeLabel
        public _Dir_VolumeLabel
        public _Dir_VolumeSerialNumber
        public _endofString
        public _EnvSegment
        public _GetNumber
        public _getStdinLine
        public _CommandParser
        public _lowerCase
        public _lowerCaseString
        public _PleaseEnterDate
        public _PleaseEnterTime
        public _ShowCurrentDate
        public _ShowCurrentTime
        public _sprintf
        public _SwitchChar
        public _computeLength
        public _Seeif_WildCharacter

    ; in rxdoscpy.asm

        extrn _Copy                                     : near

    ; in rxdosdir.asm

        extrn _Dir                                      : near

    ; in rxdosfor.asm

        extrn _For                                      : near

    ; in rxdosprm.asm

        extrn _Prompt                                   : near
        extrn _Date                                     : near
        extrn _Time                                     : near

        extrn DisplayPrompt                             : near
        extrn formatCurrentDate                         : near
        extrn formatCurrentTime                         : near

    ; in rxdosren.asm

        extrn _Rename                                   : near
        extrn RXDOSLASTADDRESS                          : near

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  RxDOS Command Parser/ Execute                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ds:si  command line beginning with a count                  ;
        ;          (this fct does not rely on CR at end of buffer)      ;
        ;                                                               ;
        ;   The command is executed, which may require loading another  ;
        ;   program.                                                    ;
        ;                                                               ;
        ;...............................................................;

_CommandParser  PROC FAR

        Entry
        ddef _OriginalCmdLine, ds, si                   ; argument is copied
        defbytes _commandLine, 128                      ; argument is copied
        defwords __argarray, maxArgArray                ; argument array
                                                        ; (last arg is null)
        SaveAllRegisters                                ; save all registers
        cld                                             ; and direction

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get switch character
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        Int21 GetSetSwitchChar, 00                      ; get switch char
        mov byte ptr cs:[ _SwitchChar ], dl             ; save switch character

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  copy command line 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getdarg ds, si, _OriginalCmdLine

        lodsb 
        mov cl, al                                      ; length
        xor ch, ch
        or cx, cx                                       ; any arguments ?
        jz _commandParser_36                            ; if no arguments -->

        setES cs
        lea di, offset [ _commandLine ][ bp ]
        rep movsb                                       ; copy command line
        
        xor ax, ax
        stosb                                           ; add a null terminator

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  parse
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        setDS cs        
        mov cx, maxArgArray - 2                         ; # argument array entries
        lea si, offset [ _commandLine ][ bp ]
        lea di, offset [ __argarray   ][ bp ]
        call _BuildArgArray                             ; break up into arg list
        jz _CommandParser_36                            ; if no arguments -->

        mov si, word ptr [ __argarray ][ bp ]           ; get lead argument
        cmp byte ptr [ si ], ':'                        ; label line ?
        jz _CommandParser_36                            ; ignore -->

        lea di, offset [ __argarray   ][ bp ]
        call _executeCommandArray
        
        cmp byte ptr [ _EchoStatus ], No                ; echo ?
        jz _CommandParser_36                            ; if no echo -->
        call CRLF

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  command completed
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_CommandParser_36:
        restoreAllRegisters
        Return

_CommandParser  ENDP

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  RxDOS Command Parser/ Execute                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ds:si  command line beginning with a count                  ;
        ;          (this fct does not rely on CR at end of buffer)      ;
        ;                                                               ;
        ;...............................................................;

_Int2E_CommandParser  PROC FAR

        push ds
        push si
        mov si, ss
        mov ds, si
        mov si, sp                                      ; old stack to ds: si

        cld
        cli
        setSS cs
        mov sp, offset RxDOS_AltStack                   ; switch stacks
        sti

        saveRegisters ds, si                            ; save pointer to other stack
        lds si, dword ptr [ si ]                        ; restore old ds:si values.
        call _CommandParser                             ; call command parser

        cli
        pop si
        pop ss    
        mov sp, si                                      ; back to old stack.
        sti

        pop si
        pop ds                                          ; restore ds: si values

        iret
_Int2E_CommandParser  ENDP

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Control C Interrupt Service Routine                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ds:si  command line beginning with a count                  ;
        ;          (this fct does not rely on CR at end of buffer)      ;
        ;                                                               ;
        ;   The command is executed, which may require loading another  ;
        ;   program.                                                    ;
        ;                                                               ;
        ;...............................................................;

_ControlC_ISR   PROC FAR

        Int21 GetPSPAddress
        cmp bx, word ptr cs:[ _PSPSegment ]             ; current cmd processor copy ?
        jz _ControlC_ISR_12                             ; yes, accept ctlC -->

        stc
        ret 2                                           ; cause abort

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  abort current command, batch files
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ControlC_ISR_12:
        cli
        mov ax, cs
        mov ds, ax
        mov es, ax
        mov ss, ax
        mov sp, word ptr [ RxDOS_PrevStackFrame ]       ; current stack frame
        sti

_ControlC_ISR_16:
        cmp word ptr [ RxDOS_BatchFile. batchFileHandle ], 0000
        jz _ControlC_ISR_24                             ; if not running a batch file -->

        call _EndCall                                   ; terminate batch file
        jmp _ControlC_ISR_16                            ; undo all nested batch calls

_ControlC_ISR_24:
        jmp CommandInput   

_ControlC_ISR   ENDP

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Execute Command Stored in Arg Array                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  argument array                                       ;
        ;...............................................................;

_executeCommandArray:

        Entry
        def __argarray, di                              ; argument array
        def __stdin                                     ; stdin on entry
        def __stdout                                    ; stdout on entry
        
        defbytes __execCtrlBlock, sizeEXECCONTROL
        defbytes __pathArg, 128

        mov cx, STDIN
        call _assignGetCurrHandle                       ; get current handle
        storarg __stdin, ax                             ; stdin on entry

        mov cx, STDOUT
        call _assignGetCurrHandle                       ; get current handle
        storarg __stdout, ax                            ; stdout on entry

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  scan remainder of command line for pipe, stdin and stdout args
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor ax, ax
        lea bx, offset [ __execCtrlBlock ][ bp ]
        getarg di, __argarray                           ; argument array

        push di
        mov di, bx
        mov cx, sizeEXECCONTROL
        rep stosb                                       ; clear exec control block
        pop di

        mov dx, -1
        mov word ptr [ exCtrlStdInHandle ][ bx ], dx    ; no stdin file

_executeArray_04:
        mov dx, -1
        mov word ptr [ exCtrlStdOutHandle ][ bx ], dx   ; no stdout file (stdin may have been piped)

        lea bx, offset [ __execCtrlBlock ][ bp ]
        call _assignRedirectedDevices                   ; arg array in [di]

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if command is valid
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getarg di, __argarray                           ; restore pointer to arg array
        mov si, word ptr [ di ]                         ; get lead argument
        call checkInstallableCommandInterface           ; command executed ?
        jnz _executeArray_36                            ; yes -->

        getarg di, __argarray                           ; restore pointer to arg array
        mov si, word ptr [ di ]                         ; get lead argument
        mov di, offset RxDOS_InternalCommands
        call CmndLookup                                 ; lookup command
        jnc _executeArray_26                            ; if command found -->

        call checkUnixStyleCommands                     ; Unix style commands ?
        jnz _executeArray_10                            ; if no unix cmds -->

        getarg di, __argarray                           ; restore pointer to arg array
        mov si, word ptr [ di ]                         ; get lead argument
        mov di, offset RxDOS_UNIXStyleCommands
        call CmndLookup                                 ; lookup command
        jnc _executeArray_26                            ; if command found -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if valid drive letter
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_executeArray_10:
        mov di, word ptr [ __argarray ][ bp ]           ; arguments pointer
        mov si, word ptr [ di ]                         ; get lead argument
        cmp word ptr ss:[ si+1 ], ':'                   ; disk select ?
        jnz _executeArray_12                            ; if unknown -->

        call CountArgs                                  ; count how many arguments
        call _DiskSelect                                ; process disk select
        jmp _executeArray_36

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if .exe, .com, or .bat (filename in ss:si )
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_executeArray_12:
        lea dx, offset [ __PathArg ][ bp ]
        call _executeProgram                            ; try to execute program
        jc _executeArray_36                             ; if error -->

        call _saveBatchArguments                        ; if batch file, save arguments
        jmp _executeArray_36
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  go process command
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_executeArray_26:
        mov di, word ptr [ __argarray ][ bp ]           ; arguments pointer
        call SplitArgs                                  ; for cd.. cases

        inc di
        inc di                                          ; argument array past arg 
        mov si, word ptr [ di ]                         ; get lead argument
        call CountArgs                                  ; count how many arguments
        call bx                                         ; go execute commands

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  done
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_executeArray_36:
        mov byte ptr [ PageLines ], 00                  ; cancel page lines

        mov bx, STDIN
        mov ax, word ptr [ __execCtrlBlock. exCtrlStdInHandle ][ bp ]
        call _CloseRedirectedDevice

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  temp input file created ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getarg di, __argarray
        test word ptr [ __execCtrlBlock. exCtrlFlags ][ bp ], exTempInputFile
        jz _executeArray_42                             ; not piped in -->

        mov bx, STDIN
        Int21 DeleteFile                                ; delete temp stdin

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is arg piped ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_executeArray_42:
        getarg di, __argarray
        test word ptr [ __execCtrlBlock. exCtrlFlags ][ bp ], exCtrlPiped
        jnz _executeArray_46

        mov bx, STDIN
        getarg ax, __stdin                              ; stdin on entry
        call _CloseRedirectedDevice                     ; close device

        mov bx, STDOUT
        getarg ax, __stdout                             ; stdout on entry
        call _CloseRedirectedDevice                     ; close device
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  arg is piped
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_executeArray_46:
        mov ax, word ptr [ di ]
        inc di
        inc di
        or ax, ax                                       ; at end of search ?
        jnz _executeArray_46                            ; keep loopking -->

        and word ptr [ __execCtrlBlock. exCtrlFlags ][ bp ], NOT exCtrlPiped
        or word ptr [ __execCtrlBlock. exCtrlFlags ][ bp ], exTempInputFile
        storarg __argarray, di                          ; next in arg array

    ; must reopen file given by temp...
    ; maybe should not have closed it.

        mov cx, STDOUT
        call _assignGetCurrHandle                       ; get current handle
        mov word ptr [ __execCtrlBlock. exCtrlStdInHandle ][ bp ], ax

        mov bx, STDOUT
        Int21 CommitFile                                ; close stdout

        xor cx, cx
        xor dx, dx
        mov bx, STDOUT
        Int21 MoveFilePointer, SEEK_BEG                 ; point to beg of file

        mov bx, STDOUT
        mov cx, STDIN
        Int21 ForceFileHandle                           ; redirect stdout -> stdin

        mov bx, STDOUT
        mov ax, word ptr [ __execCtrlBlock. exCtrlStdOutHandle ][ bp ]
        call _CloseRedirectedDevice

        getarg di, __argarray                           ; next in arg array
        jmp _executeArray_04

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Build Argument Array                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ds:si  command line beginning with a count                  ;
        ;          (this fct does not rely on CR at end of buffer)      ;
        ;   ss:di  argument array                                       ;
        ;          (returns a pointer into the command line at the      ;
        ;           start of each argument.  Multiple switches are      ;
        ;           detected by testing the get command switch char).   ;
        ;   cx     max number of arguments allowed                      ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   zr     if no arguments passed                               ;
        ;...............................................................;

_BuildArgArray:

        Entry
        ddef _commandLine, ds, si
        ddef __argarray, ss, di                         ; (maxArgArray) arguments
        def _args, cx

        setES ss

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  scan argument
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_buildArgArray_06:
        mov ch, ' '                                     ; pretend previous char is a space

_buildArgArray_08:
        lodsb                                           ; get character
        or al, al                                       ; end of line ?
        jz _buildArgArray_36                            ; yes, end of arg -->

        cmp al, ' '                                     ; space ?
        jz _buildArgArray_14                            ; yes, arg separator -->
        cmp al, byte ptr [ _SwitchChar ]                ; switch character ?
        jz _buildArgArray_18                            ; yes, record argument -->

        call _CmndParse_SeparatorCheck                  ; parse break ?
        jz _buildArgArray_18                            ; yes -->

        cmp ch, ' '                                     ; previous also a space ?
        jz _buildArgArray_18                            ; yes, continue scanning -->

_buildArgArray_14:
        mov ch, al                                      ; save previous character
        jmp _buildArgArray_08                           ; continue scanning -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  record argument
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_buildArgArray_18:
        mov ch, al                                      ; save possible string terminator

        push ax
        mov ax, si
        dec ax
        stosw                                           ; store argument pointer
        pop ax
        dec word ptr [ _args ][ bp ]                    ; more args allowed ?
        jle _buildArgArray_36                           ; no -->

        cmp ch, singleQuote
        jz _buildArgArray_30
        cmp ch, doubleQuote
        jz _buildArgArray_30

        cmp ch, '\'                                     ; path info ?
        jz _buildArgArray_24                            ; not a word breaker -->
        cmp ch, byte ptr [ _SwitchChar ]                ; switch character ?
        jz _buildArgArray_24                            ; yes, keep as part of word -->
        call _CmndParse_SeparatorCheck                  ; parse break ?
        jnz _buildArgArray_24                           ; if not a separator -->

        mov ch, ' '                                     ; indicate argument break

_buildArgArray_24:
        jmp _buildArgArray_08                           ; continue scanning -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  set inside string mode
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_buildArgArray_30:
        lodsb                                           ; get character
        or al, al                                       ; end of line ?
        jz _buildArgArray_36                            ; yes, end of arg -->
        cmp al, ch                                      ; string terminator ?
        jnz _buildArgArray_30                           ; no, continue saving -->
        jmp _buildArgArray_06

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_buildArgArray_36:
        mov word ptr ss:[ di ], 0000                    ; add null table terminator
        mov word ptr ss:[ di+2 ], 0000                  ; two nulls is complete end

        mov di, word ptr [ __argarray. _pointer ][ bp ]
        cmp word ptr [ di ], 0000                       ; args passed ?
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Split Args                                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This routine detects and corrects concatenated args.         ;
        ;                                                               ;
        ;  Some args are passed in the arg array as a single arg but    ;
        ;  in fact they need to be split up, as in 'cd..'               ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   di     pointer to args array                                ;
        ;...............................................................;

SplitArgs:

        Entry
        def __argarray, di                              ; pointer to args

        mov si, word ptr [ di ]                         ; get arg
        or si, si                                       ; args passed ?
        jz _SplitArgs_36                                ; none -->

_SplitArgs_08:
        lodsb
        or al, al                                       ; null ?
        jz _SplitArgs_36                                ; end of search -->
        cmp al, ' '+1                                   ; space or other break ?
        jc _SplitArgs_36                                ; end of search -->
        cmp al, '='                                     ; if = in arg, 
        jz _SplitArgs_36                                ; no need to continue ->
        cmp al, '\'                                     ; dir break ?
        jz _SplitArgs_12                                ; yes, insert arg -->
        cmp al, '.'                                     ; period break ?
        jnz _SplitArgs_08                               ; continue if none of the above -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Insert Arg
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SplitArgs_12:
        mov ax, si                                      ; set arg
        dec ax

_SplitArgs_16:
        inc di
        inc di
        xchg ax, word ptr [ di ]                        ; push arg
        or ax, ax                                       ; end of table ?
        jnz _SplitArgs_16                               ; not yet -->

        cmp word ptr [ di+2 ], 0000                     ; 2nd null follows ?
        jnz _SplitArgs_16                               ; no, keep inserting -->

        mov word ptr [ di+2 ], ax                       ; make sure we put two NULLS
        mov word ptr [ di+4 ], ax                       ; 

_SplitArgs_36:
        getarg di, __argarray                           ; pointer to args
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Command Parse Separator Check                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   al     character from parser                                ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   zr     is a separator character                             ;
        ;...............................................................;

_CmndParse_SeparatorCheck:

        push si
        mov si, offset _CmndParse_Separators 

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  lookup
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_CmndParse_SepCheck_08:
        cmp byte ptr cs:[ si ], 0
        jz _CmndParse_SepCheck_12
        cmp al, byte ptr cs:[ si ]
        jz _CmndParse_SepCheck_16

        inc si
        jmp _CmndParse_SepCheck_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_CmndParse_SepCheck_12:
        or al, al                                       ; non-zero if end of table 

_CmndParse_SepCheck_16:
        pop si
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Scan and Assign Redirection                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  argument array                                       ;
        ;   ss:bx  pointer to Execution Control Block                   ;
        ;...............................................................;

_assignRedirectedDevices:

        Entry
        def __argarray, di                              ; argument array
        def __contargarray, di                          ; continue argument array
        def __execCtrlBlock, bx                         ; execution control block

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  scan remainder of command line for pipe, stdin and stdout args
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_assignRedirect_06:
        getarg di, __contargarray                       ; continue argument array

_assignRedirect_08:
        mov si, word ptr [ di ]
        or si, si                                       ; end of args ?
        ifz _assignRedirect_36                          ; yes -->

        mov ax, word ptr [ si ]                         ; get arg assignment
        cmp ax, '>>'                                    ; append to stdout ?
        ifz _assignRedirect_AppendStdOut                ; yes -->
        cmp al, '>'                                     ; stdout ?
        jz _assignRedirect_StdOut                       ; yes -->
        cmp al, '<'                                     ; stdin ?
        jz _assignRedirect_StdIn                        ; yes -->
        cmp al, '|'                                     ; pipe ?
        jz _assignRedirect_Pipe                         ; yes -->

        inc di
        inc di                                          ; advance over command index
        jmp _assignRedirect_08                          ; go to next -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  stdout
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_assignRedirect_StdOut:
        mov byte ptr [ si ], 00                         ; set a null at terminator
        storarg __contargarray, di                      ; continue argument array
        call deleteArg                                  ; kill '>' arg

        mov bx, word ptr [ __execCtrlBlock ][ bp ]      ; execution control block
        lea dx, offset [ exCtrlStdOutFileName ][ bx ]   ; offset to filename
        call _asgnGetFileName                           ; arg to [dx]

        xor cx, cx
        Int21 CreateFile                                ; if not found, create 
        ifc _assignRedirect_Error                       ; just display error -->

        push ax
        mov bx, ax
        mov cx, STDOUT
        call _assignGetCurrHandle
        getarg di, __execCtrlBlock
        mov word ptr [ exCtrlStdOutHandle ][ di ], ax   ; save old handle
        Int21 ForceFileHandle                           ; redirect stdout

        pop bx                                          ; this close frees file handle after 
        Int21 CloseFile                                 ; force replicate handle
        jmp _assignRedirect_06

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  stdin
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_assignRedirect_StdIn:
        mov byte ptr [ si ], 00                         ; set a null at terminator
        storarg __contargarray, di                      ; continue argument array
        call deleteArg                                  ; kill '<' arg

        mov bx, word ptr [ __execCtrlBlock ][ bp ]      ; execution control block
        lea dx, offset [ exCtrlStdInFileName ][ bx ]    ; offset to filename
        call _asgnGetFileName                           ; arg to [dx]

        Int21 OpenFile, OPEN_ACCESS_READONLY            ; try to open file
        ifc _assignRedirect_Error                       ; just display error -->

        mov bx, ax
        mov cx, STDIN
        call _assignGetCurrHandle
        getarg di, __execCtrlBlock
        mov word ptr [ exCtrlStdInHandle ][ di ], ax    ; save old handle
        Int21 ForceFileHandle                           ; redirect stdout

        jmp _assignRedirect_06

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  pipe
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_assignRedirect_Pipe:
        mov byte ptr [ si ], 00                         ; set a null at terminator
        storarg __contargarray, di                      ; continue argument array
        mov word ptr [ di ], 0000                       ; place an end marker in arg list

        mov bx, word ptr [ __execCtrlBlock ][ bp ]      ; execution control block
        lea bx, offset [ exCtrlStdOutFileName ][ bx ]   ; offset to filename
        mov byte ptr [ bx ], 0

        mov dx, bx
        mov cx, OPEN_ACCESS_READWRITE                   ; create read/write
        Int21 CreateUniqueFile                          ; if not found, create 
        ifc _assignRedirect_Error                       ; just display error -->

        push ax
        mov bx, ax
        mov cx, STDOUT
        call _assignGetCurrHandle

        getarg di, __execCtrlBlock
        mov word ptr [ exCtrlStdOutHandle ][ di ], ax   ; save old handle
        Int21 ForceFileHandle                           ; redirect stdout

        pop bx                                          ; this close frees file handle after 
        Int21 CloseFile                                 ; force replicate handle

        getarg di, __contargarray                       ; continue argument array
        getarg bx, __execCtrlBlock                      ; execution control block
        mov word ptr [ exCtrlArgArray. _pointer ][ bx ], di
        or word ptr [ exCtrlFlags ][ bx ], exCtrlPiped  ; say arg is piped.
        jmp _assignRedirect_36                          ; exit -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  append stdout
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_assignRedirect_AppendStdOut:
        mov byte ptr [ si ], 00                         ; set a null at terminator
        storarg __contargarray, di                      ; continue argument array
        call deleteArg                                  ; kill '>' arg
        call deleteArg                                  ; kill '>' arg

        mov bx, word ptr [ __execCtrlBlock ][ bp ]      ; execution control block
        lea dx, offset [ exCtrlStdOutFileName ][ bx ]   ; offset to filename
        call _asgnGetFileName                           ; arg to [dx]
        Int21 OpenFile, OPEN_ACCESS_READWRITE           ; try to open file
        jnc _assignRedirect_Append_08

        cmp ax, errFileNotFound                         ; if other than not found
        jnz _assignRedirect_Error                       ; just display error -->

        xor cx, cx
        mov bx, word ptr [ __execCtrlBlock ][ bp ]      ; execution control block
        lea dx, offset [ exCtrlStdOutFileName ][ bx ]   ; offset to filename
        Int21 CreateFile                                ; if not found, create 
        jc _assignRedirect_Error                        ; just display error -->

_assignRedirect_Append_08:
        push ax
        xor cx, cx
        xor dx, dx
        mov bx, ax
        Int21 MoveFilePointer, SEEK_END                 ; point to end of file

        mov cx, STDOUT
        call _assignGetCurrHandle
        getarg bx, __execCtrlBlock
        mov word ptr [ exCtrlStdOutHandle ][ bx ], ax   ; save old handle

        pop bx
        push bx
        Int21 ForceFileHandle                           ; redirect stdout

        pop bx
        Int21 CloseFile

        jmp _assignRedirect_06

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  redirection error
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_assignRedirect_Error:
        call DisplayErrorCode
        stc

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_assignRedirect_36:
        getarg di, __argarray                           ; argument array
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Kill Arg From List                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   cx     handle whose value we want                           ;
        ;   ax     value of handle returned                             ;
        ;...............................................................;

_assignGetCurrHandle:

        push es
        push si
        push bx
        push cx
        Int21 GetPSPAddress

        pop si                                          ; restore handle offset
        mov es, bx                                      ; set PSP address
        les bx, dword ptr es:[ pspFileHandlePtr ]       ; point to file handles
        mov al, byte ptr es:[ bx + si ]                 ; recover existing handle
        xor ah, ah

        pop bx
        pop si
        pop es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Copy Arg                                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   si     pointer to argument                                  ;
        ;   di     pointer to copy location                             ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   zr     no characters copies                                 ;
        ;...............................................................;

_copyArg:

        or si, si
        jz _copyArg_36
        or cx, cx                                       ; address of next arg zero ?
        sub cx, si                                      ; real length

_copyArg_08:
        lodsb                                           ; get character
        stosb
        or al, al
        jz _copyArg_36
        loop _copyArg_08

_copyArg_36:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Filename From A Piped Or Redirected Argument             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   di     points to arg list                                   ;
        ;   dx     destination where to copy arg                        ;
        ;                                                               ;
        ;  arg is removed from arg list (it is deleted) once copied     ;
        ;...............................................................;

_asgnGetFileName:

        push ax
        push dx
        push di
        mov cx, word ptr [ di + 2 ]                     ; get arg that follows
        mov si, word ptr [ di ]                         ; get current arg
        mov di, dx
        call _copyArg

        pop di
        call deleteArg                                  ; kill arg

        pop dx
        pop ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Close Redirected Std Device                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   al     restore value for handle (or -1)                     ;
        ;   bx     stdout or stdin                                      ;
        ;...............................................................;

_CloseRedirectedDevice:

        cmp al, -1                                      ; std device redirected ?
        jz _closeRedirect_42                            ; no -->

        push es
        push bx
        push ax
        Int21 CloseFile                                 ; close std device

        Int21 GetPSPAddress
        pop ax
        pop si
        mov es, bx                                      ; set PSP address
        les bx, dword ptr es:[ pspFileHandlePtr ]       ; point to file handles
        xchg al, byte ptr es:[ bx + si ]                ; recover existing handle

        pop es

_closeRedirect_42:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Lookup Argument                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ds:si  pointer to argument (term by a null or switch char)  ;
        ;   cs:di  pointer to supported commands                        ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   bx     pointer to command execution address                 ;
        ;   cy     argument not supported internally                    ;
        ;...............................................................;

CmndLookup:

        push di
        push si

        inc di
        inc di                                          ; point to arg that follows

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  skip leading @ sign
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cmp byte ptr [ si ], '@'                        ; command begins with @ sign ?
        jnz cmndLookup_12                               ; no -->
        inc si                                          ; skip @ sign 

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compare argument against table
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

cmndLookup_12:
        mov al, byte ptr [ si ]                         ; from command
        inc si

        cmp al, ' '+1                                   ; if end of command 
        jc cmndLookup_18                                ; see if all characters match -->

        cmp al, byte ptr [ _SwitchChar ]                ; switch character ?
        jz cmndLookup_18                                ; yes, end of arg -->
        call _CmndParse_SeparatorCheck                  ; separator character ?
        jz cmndLookup_18                                ; yes, end of arg -->

        call _lowerCase                                 ; lower case ...
        cmp al, byte ptr cs:[ di ]                      ; compare
        jnz cmndLookup_18                               ; if not this command -->

        inc di                                          ; else keep looking
        jmp cmndLookup_12

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if end, see if end of both args
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

cmndLookup_18:
        cmp byte ptr cs:[ di ], 0                       ; end of command ?
        jnz cmndLookup_28                               ; not this command then -->

        dec si
        mov al, byte ptr [ si ]                         ; get last character
        cmp al, ' '+1                                   ; end of command ?
        jc cmndLookup_20                                ; yes -->
        cmp al, byte ptr [ _SwitchChar ]                ; end of command ?
        jz cmndLookup_20                                ; yes -->
        call _CmndParse_SeparatorCheck                  ; separator character ?
        jz cmndLookup_20                                ; yes -->
        cmp al, '.'                                     ; end of command ?
        jz cmndLookup_20                                ; yes -->
        cmp al, '/'                                     ; end of command ?
        jz cmndLookup_20                                ; yes -->
        cmp al, '\'                                     ; end of command ?
        jnz cmndLookup_28                               ; not this command -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return valid arg
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

cmndLookup_20:
        pop ax                                          ; leave si pointing at end 
        pop di
        mov bx, word ptr cs:[ di ]                      ; where to execute
        cmp byte ptr [ si ], '\'                        ; command ends with \ ?
        clc                                             ; NoCarry
        jmp short cmndLookup_36

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  find next entry in table
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

cmndLookup_28:
        inc di
        cmp byte ptr cs:[ di ], 0                       ; end of command ?
        jnz cmndLookup_28                               ; keep looking -->

        pop si
        pop bx                                          ; don't care about saved di
        inc di                                          ; get next word
        cmp word ptr cs:[ di ], -1                      ; end of command table ?
        jnz cmndLookup                                  ; no, lookup next -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return invalid arg
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

cmndLookup_32:
        stc
        mov bx, offset _NotValidCommand                 ; default not valid command

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

cmndLookup_36:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Installable Command Interface                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  pointer to argument array                            ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   nz     if commadn executed                                  ;
        ;...............................................................;

checkInstallableCommandInterface:

        xor ax, ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Count Number of Arguments                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  pointer to argument array                            ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   ax     number of arguments in arg array                     ;
        ;...............................................................;

CountArgs:
        push es
        push di
        push cx

        mov cx, maxArgArray                             ; max number of entries
        setES ss

CountArgs_06:
        cmp word ptr ss:[ di ], 0000                    ; terminating arg ?
        jz CountArgs_08                                 ; yes, done -->
        inc di
        inc di                                          ; next arg
        loop CountArgs_06                               ; else, keep looking -->

CountArgs_08:
        mov ax, maxArgArray
        sub ax, cx                                      ; # arguments

        pop cx
        pop di
        pop es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Check Number of Arguments                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   bx/ dx are set with the number of expected arguments.       ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   cy     too many arguments message output                    ;
        ;...............................................................;

CheckNoArgs:
        xor dx, dx                                      ; no args
        xor bx, bx                                      ; no switches
        jmp short PreProcessCmndLine                    ; check

CheckOneArg:
        mov cx, 0001                                    ; must have one arg
        mov dx, 0001                                    ; must have one arg
        xor bx, bx                                      ; no switches
        jmp short PreProcessCmndLine                    ; check

CheckOptOneArg:
        mov cx, 0000                                    ; must have none, 
        mov dx, 0001                                    ;   or one arg
        xor bx, bx                                      ; no switches

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Pre Process Command Line                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  pointer to argument array                            ;
        ;   cx     acceptable min # args                                ;
        ;   dx     acceptable max # args                                ;
        ;   bx     pointer to switches                                  ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   cy     too many arguments message output                    ;
        ;...............................................................;

PreProcessCmndLine:

        push di
        push cx
        push dx

        call _GetSwitches                               ; switches ok ?
        jc _preProcessCmndLine_08                       ; no -->

        pop dx
        pop cx
        push dx
        push cx
        call CountArgs                                  ; see how many args remain
        call _TooManyArguments                          ; too many still left ?
        jc _preProcessCmndLine_08                       ; yes -->

        call nullTerminateArgs                          ; null terminate args
        or ax, ax                                       ; how many args passed

_preProcessCmndLine_08:
        pop dx
        pop cx
        pop di
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Tests for Too Many Arguments                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  pointer to argument array                            ;
        ;   ax     actual argument count                                ;
        ;   cx     acceptable min # args                                ;
        ;   dx     acceptable max # args                                ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   cy     too many arguments message output                    ;
        ;...............................................................;

_TooManyArguments:

        push di
        push ax
        push cx

        cmp ax, dx                                      ; 
        jz _TooManyArguments_16                         ; if expected # args -->
        jg _TooManyArguments_06                         ; if greater than expected -->
        cmp ax, cx
        jge _TooManyArguments_16                        ; if within min/max
        mov dx, offset CmndError_ParametersMissing
        jmp short _TooManyArguments_08

_TooManyArguments_06:
        mov dx, offset CmndError_TooManyParameters
        call DisplayLine

        pop cx
        push cx
        add cx, cx
        add di, cx                                      ; argument ptr that is extra
        mov dx, word ptr ss:[ di ]                      ; get arg address

_TooManyArguments_08:
        call DisplayLine                                ; show arg
        stc

_TooManyArguments_16:
        pop cx
        pop ax
        pop di
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Test/ Process Switches                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  pointer to argument array                            ;
        ;   bx     pointer to switches to process (or null)             ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   cy     if invalid use of switches                           ;
        ;                                                               ;
        ;          Otherwise, switch array passed is filled with info.  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Switch array consists of a switch letter followed by:        ;
        ;                                                               ;
        ;    'x'        switch letter                                   ;
        ;    flags      type of data value expected                     ;
        ;    min        min value expected                              ;
        ;    max        max value expected                              ;
        ;    actual     actual value passed in command                  ;
        ;                                                               ;
        ;    a flag bit is set if the flag was encountered in cmnd.     ;
        ;...............................................................;

_GetSwitches:
        
        Entry
        def _switches, bx
        
        push di
        push si
        push cx

        or bx, bx                                       ; switches expected ?
        jz _getSwitches_06                              ; no -->
        call _initSwitchTable                           ; init switches expected
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  walk through all args
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_getSwitches_06:
        mov si, word ptr ss:[ di ]                      ; get arg 
        or si, si                                       ; is it a null arg ?
        jz _getSwitches_36                              ; yes, exit -->

        mov al, byte ptr ss:[ si ]                      ; get value at arg
        cmp al, byte ptr [ _SwitchChar ]                ; switch arg ?
        jnz _getSwitches_12                             ; no -->

        getarg bx, _switches
        or bx, bx                                       ; any switches allowed ?
        jz _getSwitches_24                              ; if not allowed, drop from list

        mov ax, word ptr ss:[ si + 1 ]                  ; get switch characters
        call _matchSwitch                               ; is this switch valid ?
        jc _getSwitches_24                              ; not allowed -->

        or word ptr [ swFlags ][ bx ], SW_SWITCHSET     ; switch is set
        call deleteArg                                  ; remove this argument
        jz _getSwitches_36                              ; if no more args -->

_getSwitches_12:
        inc di
        inc di
        jmp _getSwitches_06

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  switch is illegal
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_getSwitches_24:
        push si                                         ; save argument ptr
        mov dx, offset CmndError_BadSwitch
        call DisplayLine                                ; error message

        pop dx
        call DisplayLine                                ; show arg
        stc

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  done
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_getSwitches_36:
        pop cx
        pop si
        pop di
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Match Switch                                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   al     character                                            ;
        ;   bx     switch table                                         ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   bx     pointer to matchin switch entry                      ;
        ;   cy     match not found                                      ;
        ;...............................................................;

_matchSwitch:

        call _lowerCase

_matchSwitch_04:
        cmp al, byte ptr cs:[ bx ]
        jz _matchSwitch_08

        add bx, sizeSWITCHENTRY                         ; next entry
        cmp byte ptr cs:[ bx ], -1                      ; end of table ?
        jnz _matchSwitch_04                             ; not yet -->

        stc

_matchSwitch_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Init Switch Table                                            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   bx     switch table                                         ;
        ;...............................................................;

_initSwitchTable:

        push bx

_initSwitchTable_04:
        cmp byte ptr cs:[ bx ], -1                      ; end of table ?
        jz _initSwitchTable_08                          ; yes, all done -->
        and word ptr cs:[ swFlags ][ bx ], not SW_SWITCHSET
        add bx, sizeSWITCHENTRY                         ; next entry
        jmp _initSwitchTable_04

_initSwitchTable_08:
        pop bx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Delete Argument                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  argument list                                        ;
        ;...............................................................;

deleteArg:

        push di
        cmp word ptr [ di ], 0000                       ; at end of list ?
        jz deleteArg_12                                 ; yes -->

deleteArg_08:
        mov ax, word ptr [ di+2 ]                       ; get arg
        mov word ptr [ di ], ax                         ; and move it
        inc di
        inc di
        or ax, ax                                       ; any more ?
        jnz deleteArg_08                                ; yes -->

        cmp word ptr [ di+2 ], 0000                     ; two args reqd to actually end copy
        jnz deleteArg_08                                ; not yet -->

deleteArg_12:
        pop di
        cmp word ptr [ di ], 0000                       ; at end of list ?
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Null Terminate Args                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  argument list                                        ;
        ;...............................................................;

nullTerminateArgs:

        push di
        push ax

nullTerminateArgs_04:
        mov si, word ptr [ di ]
        or si, si                                       ; more args ?
        jz nullTerminateArgs_12                         ; no -->

nullTerminateArgs_06:
        lodsb                                           ; get character 
        cmp al, ' '+1                                   ; space or control character ?
        jc nullTerminateArgs_10                         ; yes -->

        cmp al, byte ptr [ _SwitchChar ]                ; switch character ?
        jz nullTerminateArgs_10                         ; yes -->
        call _CmndParse_SeparatorCheck                  ; separator character ?
        jnz nullTerminateArgs_06                        ; no, go to next char -->

nullTerminateArgs_10:
        mov byte ptr [ si-1 ], 0                        ; place null terminator
        inc di
        inc di
        jmp nullTerminateArgs_04

nullTerminateArgs_12:
        pop ax
        pop di
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Replace Temporary Variables                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   Returns ZR if environment string contains RXDOS=UNIX        ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   NZ     no Unix argumnets                                    ;
        ;...............................................................;

checkUnixStyleCommands:

        push es
        mov si, offset RxDOS_RXDOSSpec                  ; locate RXDOS=
        call searchEnvVariable                          ; env variable located ?
        jnz _checkUnixEnv_16                            ; if no unix cmds -->

        mov es, word ptr [ _EnvSegment ]                ; point to segment
        add di, dx                                      ; point to value
        mov si, offset RxDOS_UNIXSpec

_checkUnixEnv_08:
        mov al, byte ptr es:[ di ]                      ; from env string
        call _upperCase

        cmp al, byte ptr [ si ]                         ; compare against string
        jnz _checkUnixEnv_16                            ; if no unix cmds -->

        inc si
        inc di
        cmp byte ptr [ si ], 00                         ; end of string ?
        jnz _checkUnixEnv_08                            ; not yet -->

_checkUnixEnv_16:
        pop es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Save Batch File Arguments                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:si  points to null terminated string                     ;
        ;   ss:di  pointer to ArgArray                                  ;
        ;   ss:dx  pointer to fully expanded pathname                   ;
        ;   ax     type, which must be set to _BAT                      ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   cy     if error                                             ;
        ;...............................................................;

_saveBatchArguments:

        Entry
        def __argarray, di
        def __pathArg, dx
        def __type, ax
        def __tempargptr, di

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is type _BAT ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cmp ax, _BAT                                    ; is type BATCH
        jnz _saveBatchArguments_Error                   ; no -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if file accessible ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_saveBatchArguments_08:
        cmp word ptr [ RxDOS_StackFrameNumEntries ], 0000
        jg _saveBatchArguments_10                       ; don't close if nested call -->

        mov bx, word ptr [ RxDOS_BatchFile. batchFileHandle ]
        or bx, bx                                       ; batch file in progress ?
        jz _saveBatchArguments_10                       ; no -->
        Int21 CloseFile                                 ; close current batch file
        mov word ptr [ RxDOS_BatchFile. batchFileHandle ], 0000

_saveBatchArguments_10:
        getarg dx, __pathArg
        mov al, ( OPEN_ACCESS_NOINHERIT + OPEN_ACCESS_READONLY )
        Int21 OpenFile
        jc _saveBatchArguments_Error                    ; quit batch file -->
        mov word ptr [ RxDOS_BatchFile. batchFileHandle ], ax

        getarg di, __argarray                           ; arguments pointer
        call nullTerminateArgs

        mov di, offset ( RxDOS_BatchFile. batchArgStore )
        mov word ptr [ RxDOS_BatchFile. batchNumArgs ], 0

_saveBatchArguments_32:
        getarg si, __tempargptr
        mov si, word ptr [ si ]                         ; arg
        or si, si                                       ; null terminator ?
        jz _saveBatchArguments_Exit                     ; then all done -->

        add word ptr [ __tempargptr ][ bp ], 2
        mov bx, word ptr [ RxDOS_BatchFile. batchNumArgs ]
        add bx, bx
        mov word ptr [ RxDOS_BatchFile. batchArgPtrs ][ bx ], di
        mov word ptr [ RxDOS_BatchFile. batchArgPtrs + 2 ][ bx ], 0000
        inc word ptr [ RxDOS_BatchFile. batchNumArgs ]

        call _CopyString
        jmp _saveBatchArguments_32

_saveBatchArguments_Error:
        stc

_saveBatchArguments_Exit:
        getarg di, __argarray
        getarg dx, __pathArg
        getarg ax, __type
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Replace Temporary Variables                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:si  points to null terminated string                     ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   cx     length of new string                                 ;
        ;...............................................................;

ReplaceTempVariables:

        Entry
        def  _stringPointer, si
        def  _deleteFrom
        defbytes _tempVar, 128

        push di
        push si
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  scan for % argument
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_replaceTempVar_08:
        lodsb                                           ; scan for % symbol
        or al, al                                       ; null terminator ?
        ifz _replaceTempVar_36                          ; yes -->
        cmp al, '%'                                     ; percent character ?
        jnz _replaceTempVar_08

        mov al, byte ptr [ si ]                         ; get character immed after
        or al, al                                       ; null terminator ?
        ifz _replaceTempVar_36                          ; yes -->

        cmp al, '%'                                     ; %% case ?
        jz _replaceTempVar_PercentPercent               ; yes -->

        dec si                                          ; backup over %
        cmp al, '9'+1                                   ; outside arg values ?
        jnc _replaceTempVar_EnvVariable                 ; yes -->
        sub al, '0'                                     ; outside argument values ?
        jc _replaceTempVar_EnvVariable                  ; yes -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  numeric argument
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov cx, 2                                       ; bytes to delete
        call deleteString                               ; delete cx bytes at si

        xor ah, ah
        cmp ax, word ptr [ RxDOS_BatchFile. batchNumArgs ]
        jge _replaceTempVar_16                          ; just delete arg -->

        add ax, ax                                      ; index pointer
        mov di, ax
        mov di, word ptr [ RxDOS_BatchFile. batchArgPtrs ][ di ]
        or di, di
        jz _replaceTempVar_16                           ; if no arg value -->
        call insertString                               ; insert arg at [ di ]

_replaceTempVar_16:
        jmp _replaceTempVar_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  %% case
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_replaceTempVar_PercentPercent:
        mov cx, 1
        call deleteString                               ; delete cx bytes at si
        jmp _replaceTempVar_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  environment variable
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_replaceTempVar_EnvVariable:
        lea di, offset [ _tempVar ][ bp ]
        storarg _deleteFrom, si
        inc si                                          ; skip init %

_replaceEnvVariable08:
        lodsb                                           ; scan for next % symbol
        stosb                                           ; save at _tempvar
        or al, al                                       ; null terminator ?
        jz _replaceTempVar_36                           ; yes -->
        cmp al, '%'                                     ; percent character ?
        jnz _replaceEnvVariable08                       ; keep searching -->

        mov word ptr [ di-1 ], '='                      ; cancel trailing %

        mov cx, si
        sub cx, word ptr [ _deleteFrom ][ bp ]          ; delete from
        push si
        push cx                                         ; delete length

        lea si, offset [ _tempVar ][ bp ]
        call searchEnvVariable                          ; environment string found ?
        jnz _replaceEnvVariable36                       ; not found -->

        pop cx
        push cx                                         ; length to delete 
        mov si, word ptr [ _deleteFrom ][ bp ]          ; delete from
        call deleteString                               ; delete cx bytes at si

        push ds
        mov si, di
        add si, dx                                      ; contents of variable
        mov ds, word ptr [ _EnvSegment ]                ; point to segment
        lea di, offset [ _tempVar ][ bp ]               ; where to copy
        call _CopyString                                ; copy null term string

        pop ds
        mov si, word ptr [ _deleteFrom ][ bp ]          ; delete from
        lea di, offset [ _tempVar ][ bp ]               ; where to copy
        call insertString                               ; insert

_replaceEnvVariable36:
        pop cx
        pop si
        jmp _replaceTempVar_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  done
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_replaceTempVar_36:
        xor ax, ax
        mov cx, -1
        getarg di, _stringPointer
        repnz scasb
        neg cx
        sub cx, 2

        pop si
        pop di
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Replace 'For' Variable                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:si  points to null terminated string                     ;
        ;   ss:bx  points to null terminated replacement string         ;
        ;   al     replacement letter                                   ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   cx     length of new string                                 ;
        ;...............................................................;

ReplaceForVariables:

        Entry
        def  _stringPointer, si
        def  _replaceText, bx
        def  _replaceVar

        push di
        push si

        call _lowerCase                                 ; replace vars in lower case
        mov word ptr [ _replaceVar ][ bp ], ax          ; replace variable to ah
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  scan for % argument
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_replaceForVar_08:
        lodsb                                           ; scan for % symbol
        or al, al                                       ; null terminator ?
        ifz _replaceForVar_36                           ; yes -->
        cmp al, '%'                                     ; percent character ?
        jnz _replaceForVar_08

        mov al, byte ptr [ si ]                         ; get character immed after
        call _lowerCase
        cmp al, byte ptr [ _replaceVar ][ bp ]          ; same as var requested ?
        jnz _replaceForVar_08                           ; not yet -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  numeric argument
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        dec si                                          ; backup over %
        mov cx, 2                                       ; bytes to delete
        call deleteString                               ; delete cx bytes at si

        mov di, word ptr [ _replaceText ][ bp ]         ; insert text
        call insertString                               ; insert [ di ] arg
        jmp _replaceForVar_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  done
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_replaceForVar_36:
        xor ax, ax
        mov cx, -1
        getarg di, _stringPointer
        repnz scasb
        neg cx
        sub cx, 2

        pop si
        pop di
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Delete String                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:si  points inside string                                 ;
        ;   cx     bytes to delete                                      ;
        ;...............................................................;

deleteString:

        push di
        push ax
        push si
        push cx
        or cx, cx                                       ; if null call
        jz deleteString_36                              ; nothing to do -->

deleteString_08:
        lodsb                                           ; test for null term
        or al, al
        jz deleteString_32
        loop deleteString_08

        pop cx
        pop si
        push si
        push cx
        mov di, si
        add si, cx                                      ; skip pointer

deleteString_12:
        lodsb                                           ; copy byte
        stosb
        or al, al
        jnz deleteString_12

        dec si                                          ; point to null byte

deleteString_32:
        mov byte ptr [ si ], 0

deleteString_36:
        pop cx
        pop si
        pop ax
        pop di
        ret        

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Insert String                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:si  points inside string                                 ;
        ;   ds:di  points to insert string (null term)                  ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   ss:si  points to char past insert string                    ;
        ;...............................................................;

insertString:

        push cx
        push ax
        push di

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute length of insert string
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor ax, ax
        mov cx, -1
        repnz scasb                                     ; search length of insert string

        neg cx                                          ; make len-1 positive
        sub cx, 2                                       ; kill null string arg
        sub di, 2                                       ; point to last valid byte before null
        push di                                         ; end of arg pointer
        push cx                                         ; length of insert string

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute length of master string
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor ax, ax
        mov cx, -1
        mov di, si
        repnz scasb                                     ; search length of insert string
        neg cx                                          ; make len-1 positive
        dec cx                                          ; kill null string arg
        dec di

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  open string
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        std                                             ; copy reverse direction
        pop ax
        push ax                                         ; length of insert string
        mov si, di                                      ; 
        add di, ax                                      ; where to copy to
        rep movsb                                       ; copy

        pop cx
        pop si
        push di                                         ; save return pointer
        rep movsb                                       ; copy
        cld                                             ; restore direction

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  done
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        pop si                                          ; return pointer - 1
        inc si

        pop di
        pop ax
        pop cx
        ret        

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Length of String                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   stack  address of string                                    ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   cx     length of string                                     ;
        ;...............................................................;

_stringLength:

        Entry 2
        darg  __string

        push es
        push di
        getdarg es, di, __string
        xor ax, ax
        mov cx, -1
        repnz scasb                                     ; search length of insert string

        neg cx                                          ; make len-1 positive
        sub cx, 2                                       ; kill null string arg
        pop di
        pop es
        Return 

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Number at String                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ss:si  pointer to text                                      ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   ax     contains value                                       ;
        ;   cy     if value contained text                              ;
        ;...............................................................;

_GetNumber:
        
        xor dx, dx

_GetNumber_04:
        lodsb
        cmp al, ' '+1                                   ; end of string ?
        jc _GetNumber_16                                ; yes, exit ok -->

        cmp al, '9'+1                                   ; valid number ?
        jnc _GetNumber_14                               ; no, error exit -->
        sub al, '0'                                     ; valid number ?
        jc _GetNumber_14                                ; no, error exit -->

        and ax, 15
        push ax
        mov ax, dx
        add dx, dx                                      ; 2
        add dx, dx                                      ; 4
        add dx, ax                                      ; 5
        add dx, dx                                      ; 10
        pop ax
        add dx, ax                                      ; add digit
        jmp _GetNumber_04                               ; continue -->

_GetNumber_14:
        xor ax, ax
        stc
        ret

_GetNumber_16:
        mov ax, dx
        or ax, ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Lower Case Character (AL)                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   al     character                                            ;
        ;...............................................................;

_lowerCase:

        cmp al, 'A'
        jc _lowerCase_10
        cmp al, 'Z'+1
        jnc _lowerCase_10
        or al, 20h                                      ; lower case

_lowerCase_10:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Lower Case String                                            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   stack  string pointer                                       ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   di     points to end (null) byte                            ;
        ;   cx     characters in string                                 ;
        ;...............................................................;

_lowerCaseString:
        
        Entry 2
        darg _string

        push es
        xor cx, cx
        getdarg es, di, _string

_lowerCaseString_08:
        mov al, byte ptr es:[ di ]
        or al, al
        jz _lowerCaseString_16

        cmp al, 'A'
        jc _lowerCaseString_10
        cmp al, 'Z'+1
        jnc _lowerCaseString_10
        or al, 20h                                      ; lower case

_lowerCaseString_10:
        mov byte ptr es:[ di ], al
        inc di
        inc cx
        jmp _lowerCaseString_08

_lowerCaseString_16:
        pop es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Upper Case Character (AL)                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   al     character                                            ;
        ;...............................................................;

_upperCase:

        cmp al, 'a'
        jc _upperCase_10
        cmp al, 'z'+1
        jnc _upperCase_10
        and al, not 20h                                 ; upper case

_upperCase_10:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Compare Sub Strings                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   si     points to a null terminated string                   ;
        ;   di     points to longer string                              ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   zr     strings match regardless of case                     ;
        ;...............................................................;

_compareSubString:

        mov al, byte ptr [ si ]
        or al, al
        jz _compareSubString_08

        inc si
        call _lowerCase
        mov bl, al

        mov al, byte ptr [ di ]
        inc di
        call _lowerCase

        cmp al, bl
        jz _compareSubString

_compareSubString_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Copy NULL Terminated String                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   si     points to a null terminated string                   ;
        ;   di     points to destination string                         ;
        ;...............................................................;

_copyString:
        lodsb                                           ; copy character
        stosb                                           ; copy character
        or al, al                                       ; until zero byte
        jnz _copyString

        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Locate End Of String                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   di     points to a null terminated string                   ;
        ;...............................................................;

_endofString:

        push ax
        push cx

        xor ax, ax
        mov cx, -1
        repnz scasb                                     ; locate null terminator
        dec di                                          ; backup over null byte

        pop cx
        pop ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Append Path Name                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   si     points to a null terminated string                   ;
        ;   di     points to a null terminated string                   ;
        ;...............................................................;

_AppendPathName:

        cmp byte ptr [ di ], 00
        jz _AppendPathName_12                           ; if no path given, don't search -->

        xor ax, ax
        mov cx, -1
        repnz scasb

        dec di                                          ; backup to null
        cmp byte ptr [ di - 1 ], '\'                    ; does path have terminating \ ?
        jz _AppendPathName_12                           ; yes, no need to add another -->

_AppendPathName_08:
        mov byte ptr [ di ], '\'                        ; add terminating \
        inc di

_AppendPathName_12:
        call _CopyString
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Search Env String                                            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ds:si  points to input command string                       ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ds:si  points to end of input string (imm after =)          ;
        ;   di     points to keyword value in env string                ;
        ;   dx     offset in env variable to value past = sign          ;
        ;   nz     env variable not found                               ;
        ;...............................................................;

searchEnvVariable:

        Entry
        def _search, si                                 ; search text
        def _envbegpointer, 0000                        ; located env variable

        push es
        mov es, word ptr [ _EnvSegment ]                ; point to segment
        xor di, di

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  search through each item
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

searchEnvVariable_08:
        storarg _envbegpointer, di                      ; located env variable
        getarg si, _search                              ; get search text pointer
        cmp byte ptr es:[ di ], 00                      ; end of env area ?
        jz searchEnvNotFound                            ; not found -->
        
searchEnvVariable_12:
        mov al, byte ptr [ si ]
        or al, al                                       ; if end string -->
        jz searchEnvVariable_Next                       ; not equal, skip to next -->

        call _upperCase                                 ; conv to upper case
        mov ah, al

        mov al, byte ptr es:[ di ]                      ; compare
        call _upperCase                                 ; conv to upper case

        cmp ah, al                                      ; compare
        jnz searchEnvVariable_Next                      ; not equal, skip to next -->

        inc si
        inc di
        cmp al, '='                                     ; at equal sign ?
        jz searchEnvVariableFound                       ; we have search item -->
        cmp byte ptr es:[ di-1 ], 00                    ; end of env string ?
        jnz searchEnvVariable_12                        ; continue -->
        jmp searchEnvVariable_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  skip to next env variable
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

searchEnvVariable_Next:
        inc di
        cmp byte ptr es:[ di-1 ], 00                    ; end of env string ?
        jnz searchEnvVariable_Next                      ; continue -->
        jmp searchEnvVariable_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  item not found
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

searchEnvNotFound:
        mov ax, es
        or ax, ax                                       ; not zero means not found
        jmp short searchEnvReturn

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  item found
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

searchEnvVariableFound:
        mov dx, word ptr [ _envbegpointer ][ bp ]       ; return start pointer
        sub di, dx                                      ; distance from beg pointer
        xchg di, dx                                     ; return offset in dx
        xor ax, ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

searchEnvReturn:
        pop es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Delete Env Variable                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   di     pointer to env entry to delete                       ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   di     pointer to end byte of environment                   ;
        ;...............................................................;

deleteEnvVariable:

        push es
        mov es, word ptr [ _EnvSegment ]                ; point to segment
        cmp byte ptr es:[ di ], 00                      ; at end of env block ?
        jz deleteEnvVariable_36                         ; yes, don't delete -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  locate start of next string
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov si, di                                      ; save di pointer

deleteEnvVariable_08:
        inc si
        cmp byte ptr es:[ si-1 ], 00                    ; locate end of this string 
        jnz deleteEnvVariable_08                        ; continue -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is this end of env block ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

deleteEnvVariable_12:
        mov byte ptr es:[ di ], 00                      ; put end mark.
        cmp byte ptr es:[ si ], 00                      ; at end of env block ?
        jz deleteEnvVariable_36                         ; yes, end of delete -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  copy a string
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

deleteEnvVariable_16:
        mov al, byte ptr es:[ si ]
        stosb                                           ; copy byte
        inc si
        or al, al                                       ; did we copy a null terminator ?
        jnz deleteEnvVariable_16                        ; not yet -->
        jmp deleteEnvVariable_12                        ; 

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

deleteEnvVariable_36:
        pop es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Insert Env Variable                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ds:si  pointer to text to insert into environment           ;
        ;   di     must point to end null byte of environment block     ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   ax     bytes remaining in Environment                       ;
        ;   cy     if could not add to environment block                ;
        ;...............................................................;

insertEnvVariable:

        Entry
        def endPointer, di

        push es
        mov es, word ptr ss:[ _EnvSegment ]             ; point to env segment
        mov cx, word ptr ss:[ _EnvSize ]
        sub cx, di                                      ; bytes available
        jle insertEnvVariable_36                        ; if none -->

insertEnvVariable_08:
        lodsb
        stosb                                           ; copy byte
        or al, al                                       ; all done ?
        jz insertEnvVariable_36                         ; yes -->

        loop insertEnvVariable_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  can't insert (insufficient space)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        stc                                             ; error, if not all copied
        mov ax, 0000                                    ; remaining space
        getarg di, endPointer                           ; restore pointer to old end
        mov byte ptr es:[ di ], 00                      ; restore end pointer

        pop es
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

insertEnvVariable_36:
        mov ax, word ptr ss:[ _EnvSize ]
        sub ax, di                                      ; space left
        mov byte ptr es:[ di ], 00                      ; put double end pointer
        dec di                                          ; restore pointer

        pop es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Find Program or Bat File                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:si  pointer to name (not null terminated)                ;
        ;   ss:dx  where to store path                                  ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   cy     not found anywhere in path                           ;
        ;   ax     contains type of program found                       ;
        ;           0000 - program is a batch file                      ;
        ;           0001 - program is a com or exe file                 ;
        ;   bx     contains handle to file                              ;
        ;...............................................................;

_findProgram:

        Entry
        def _pathArg, dx                                ; where to store path
        def _pathpointer, 0000                          ; PATH=, if any
        def _endofname                                  ; null this if extension
        def _endofpathname
        def _dontSearch, false
        def _extensioninname, false
        defbytes _execname, 128

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  isolate name from rest of command line
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        lea di, offset [ _execname ][ bp ]              ; pointer to copy field

_findProgram_08:
        lodsb                                           ; get character 
        stosb                                           ; copy it
        cmp al, ' '+1                                   ; space or control character ?
        jc _findProgram_20                              ; yes -->
        cmp al, '|'                                     ; pipe character ?
        jz _findProgram_20                              ; yes -->
        cmp al, '*'                                     ; wild character ?
        ifz _findProgram_Error                          ; yes -->
        cmp al, '?'                                     ; wild character ?
        ifz _findProgram_Error                          ; yes -->

        cmp al, byte ptr [ _SwitchChar ]                ; switch character ?
        jz _findProgram_20                              ; yes -->

        cmp al, '\'                                     ; file name contains path info ?
        jz _findProgram_12                              ; don't search path= -->
        cmp al, '/'                                     ; file name contains path info ?
        jnz _findProgram_14                             ; don't search path= -->

_findProgram_12:
        mov byte ptr [ _dontSearch ][ bp ], true

_findProgram_14:
        call _CmndParse_SeparatorCheck                  ; separator character ?
        jnz _findProgram_08                             ; yes -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  search through current directory first
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_findProgram_20:
        dec di
        mov byte ptr [ di ], 0                          ; place null terminator

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  search through current directory first
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        lea ax, offset [ _execname ][ bp ]              ; pointer to copy field

_findProgram_22:
        cmp ax, di                                      ; at beginning of field ?
        jz _findProgram_24                              ; yes, no extension -->

        dec di
        cmp byte ptr [ di ], '\'                        ; path character ?
        jz _findProgram_24                              ; yes, no extension -->
        cmp byte ptr [ di ], '/'                        ; path character ?
        jz _findProgram_24                              ; yes, no extension -->
        cmp byte ptr [ di ], '.'                        ; possible extension ?
        jnz _findProgram_22
        mov word ptr [ _extensioninname ][ bp ], true   ; disable

_findProgram_24:
        lea si, offset [ _execname ][ bp ]              ; pointer to copy field
        mov di, word ptr [ _pathArg ][ bp ]             ; pointer to path arg
        mov word ptr [ _endofpathname ][ bp ], di       ; save end of path name pointer
        call _CopyString                                ; append filename

        cmp word ptr [ _extensioninname ][ bp ], true
        jnz _findProgram_26                             ; if not explicit extension -->
        xor cx, cx
        mov dx, word ptr [ _pathArg ][ bp ]             ; pointer to path arg
        Int21 FindFirstFile                             ; locate file
        ifnc _findProgram_50                            ; if located -->

_findProgram_26:
        mov si, offset RxDOS_PathSpec
        call searchEnvVariable                          ; locate PATH=
        jnz _findProgram_30                             ; if not found -->

        add di, dx                                      ; address after =
        storarg _pathpointer, di                        ; location of path statement

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  search through search order (append extension)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_findProgram_30:
        lea si, offset [ _execname ][ bp ]              ; pointer to copy field
        getarg di, _endofpathname                       ; where to copy name
        call _CopyString                                ; append filename

        dec di
        mov word ptr [ _endofname ][ bp ], di           ; save end name pointer

        mov si, offset RxDOS_ExecOrder                  ; look for names in order, current dir

_findProgram_32:
        cmp word ptr [ _extensioninname ][ bp ], true   ; is extension search disabled ?
        jz _findProgram_34

        getarg di, _endofname
        call _CopyString                                ; copy extension

_findProgram_34:
        push si                                         ; save pointer
        xor cx, cx
        mov dx, word ptr [ _pathArg ][ bp ]             ; pointer to path arg
        Int21 FindFirstFile                             ; locate file
        pop si                                          ; restore si
        jnc _findProgram_50                             ; if located -->

        cmp word ptr [ _extensioninname ][ bp ], true   ; is extension search disabled ?
        jz _findProgram_38
        cmp byte ptr [ si ], -1                         ; end of search order list ?
        jnz _findProgram_32                             ; not yet -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  locate next search path
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_findProgram_38:
        cmp byte ptr [ _dontSearch ][ bp ], true        ; search path ?
        ifz _findProgram_CantFind                       ; no, can't find -->

        mov si, word ptr [ _pathpointer ][ bp ]         ; path=
        or si, si                                       ; if no path, 
        ifz _findProgram_CantFind                       ; then can't find -->

        push ds
        mov ds, word ptr [ _EnvSegment ]
        mov si, word ptr [ _pathpointer ][ bp ]         ; path=
        mov di, word ptr [ _pathArg ][ bp ]             ; pointer to path arg

_findProgram_40:
        lodsb
        or al, al                                       ; end of string ?
        jz _findProgram_44                              ; yes -->
        cmp al, ';'                                     ; end of term ?
        jz _findProgram_44                              ; yes -->

        stosb
        jmp _findProgram_40

_findProgram_44:
        pop ds                                          ; restore ds
        storarg _pathpointer, si                        ; save end of path search

        cmp di, word ptr [ _pathArg ][ bp ]             ; pointer to path arg
        jz _findProgram_CantFind                        ; if emptry string -->

        mov al, '\'
        cmp al, byte ptr [ di - 1 ]                     ; pathname terminated by \ ?
        jz _findProgram_46                              ; yes -->
        stosb                                           ; add \ to pathname

_findProgram_46:
        storarg _endofpathname, di                      ; save path name
        jmp _findProgram_30

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  file found, determine if .exe or .com
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_findProgram_50:
        mov si, offset [ RxDOS_DTA. findFileName ]

_findProgram_52:
        lodsb                                           ; get filename character
        or al, al                                       ; end of name ?
        jz _findProgram_58                              ; yes, assume .bat -->
        cmp al, '.'                                     ; extension located ?
        jnz _findProgram_52                             ; no, keep scanning -->

        or word ptr [ si ], '  '                        ; force lower case
        or byte ptr [ si + 2 ], ' '                     ; force lower case

        mov ax, _EXE                                    ; _exe
        cmp word ptr [ si ], 'xe'
        jnz _findProgram_54
        cmp byte ptr [ si + 2 ], 'e'
        jz _findProgram_62

_findProgram_54:
        mov ax, _COM                                    ; _com
        cmp word ptr [ si ], 'oc'
        jnz _findProgram_58
        cmp byte ptr [ si + 2 ], 'm'
        jz _findProgram_62

_findProgram_58:
        mov ax, _BAT                                    ; _bat
        cmp word ptr [ si ], 'ab'
        jnz _findProgram_Error
        cmp byte ptr [ si + 2 ], 't'
        jnz _findProgram_Error

_findProgram_62:
        or ax, ax                                       ; no carry (no error)
        getarg si, _pathArg                             ; expanded argument name
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  error
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_findProgram_CantFind:
_findProgram_Error:
        stc
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Load Program                                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:si  program to load                                      ;
        ;   ss:di  argument array                                       ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;                                                               ;
        ;                                                               ;
        ;                                                               ;
        ;  Note:                                                        ;
        ;   Must return error message.                                  ;
        ;                                                               ;
        ;...............................................................;

_loadProgram:

        Entry
        ddef     _args, ss, di
        ddef     _progname, ss, si
        defbytes _ExecBlock, sizeEXEC
        defbytes _CmdLine, 128
        defbytes _FCB1, sizeFCB
        defbytes _FCB2, sizeFCB

        push es
        push di
        setES ss
        lea di, offset _ExecBlock [ bp ]
        clearMemory sizeEXEC

        lea di, offset _FCB1 [ bp ]
        call _initFCB
        mov word ptr [ _ExecBlock. lexecFCB_1. _pointer  ][ bp ], di
        mov word ptr [ _ExecBlock. lexecFCB_1. _segment  ][ bp ], ss

        lea di, offset _FCB2 [ bp ]
        call _initFCB
        mov word ptr [ _ExecBlock. lexecFCB_2. _pointer  ][ bp ], di
        mov word ptr [ _ExecBlock. lexecFCB_2. _segment  ][ bp ], ss

        lea bx, offset _ExecBlock [ bp ]
        mov ax, word ptr [ _EnvSegment ]
        mov word ptr [ _ExecBlock. lexecEnvironment  ][ bp ], ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  copy command tail.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        lea di, offset _CmdLine [ bp ]
        mov word ptr ss:[ di ], 0D00h                   ; pre-init command line

        mov word ptr [ _ExecBlock. lexecCommandTail. _pointer  ][ bp ], di
        mov word ptr [ _ExecBlock. lexecCommandTail. _segment  ][ bp ], ss

        getdarg  es, si, _args
        add si, 2                                       ; point to next entry
        cmp word ptr es:[ si ], 0000                    ; any more arguments ?
        jz _loadProgram_20                              ; if no args -->

        xor ax, ax
        stosb                                           ; start with length term

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; capture leading spaces
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov si, word ptr es:[ si ]

_loadProgram_08:
        cmp byte ptr es:[ si - 1 ], ' '                 ; preceeding a space ?
        jnz _loadProgram_12                             ; no -->
        dec si
        jmp _loadProgram_08

_loadProgram_12:
        lodsb                                           ; copy command tail
        stosb
        or al, al
        jnz _loadProgram_12

        dec di
        mov byte ptr es:[ di ], 0Dh                     ; end with cr

        dec di
        mov cx, di
        lea di, offset _CmdLine [ bp ]
        sub cx, di                                      ; actual length
        mov byte ptr es:[ di ], cl                      ; save starter length

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get FCB arguments
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov ax, 2901h                                   ; ok to skip leading sep
        lea di, offset _FCB1 [ bp ]
        getdarg  es, si, _args
        mov si, word ptr es:[ si + 2 ]                  ; get source string
        int 21h

        mov ax, 2901h                                   ; ok to skip leading sep
        lea di, offset _FCB2 [ bp ]
        int 21h

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  go run program
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_loadProgram_20:
        setES ss
        lea bx, offset _ExecBlock [ bp ]
        getdarg ds, dx, _progname
        Int21 ExecuteProgram, 00

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get return status when done
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, cs
        mov ds, ax
        mov es, ax                                      ; restore registers

        Int21 GetReturnCode                             ; get return value
        and ax, 255                                     ; get previous return value
        mov word ptr [ RxDOS_ExecReturnCode ], ax       ; save return code

        pop di
        pop es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  initialize an FCB data structure                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   es:di  pointer to FCB                                       ;
        ;...............................................................;

_initFCB:
        push ax
        push di
        clearMemory sizeFCB

        lea di, offset fcbName [ di ]
        clearMemory sizeFILENAME, 2020h                 ; double spaces

        pop di
        pop ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  return volume name                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   al     is disk to read volume                               ;
        ;   di     pointer to volume name (in DTA)                      ;
        ;   cy     drive has no volume name                             ;
        ;...............................................................;

returnVolumeName:

        cmp al, 'Z'-40h
        jnc returnVolumeName_08

        or al, al                                       ; default disk ?
        jnz returnVolumeName_06                         ; no -->

        Int21 CurrentDisk
        inc al                                          ; a=1, ...

returnVolumeName_06:
        add al, 'a'-1

returnVolumeName_08:
        mov di, offset RxDOS_RootDirectory - 2
        mov byte ptr [ di ], al

        mov dx, di
        mov cx, ATTR_VOLUME
        mov di, offset [ RxDOS_DTA. findFileName ]
        mov byte ptr [ di ], 0
        Int21 FindFirstFile                             ; find volume name

        mov dx, 0                                       ; assume no error
        jnc returnVolumeName_18                         ; return zero (found )

        mov dx, 1                                       ; else assume not found error
        cmp al, errInvalidDrive                         ; if drive not found,
        jz returnVolumeName_16                          ; then return error -->
        cmp al, errPathNotFound                         ; if path not found,
        jnz returnVolumeName_18                         ; then return error -->

returnVolumeName_16:
        stc
        ret

returnVolumeName_18:
        or dx, dx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Error Code                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ax     error code                                           ;
        ;...............................................................;

DisplayErrorCode:

        push bx
        cmp ax, CmndError_RefTableEntries
        jnc DisplayErrorCode_08

        mov bx, ax
        add bx, bx
        add bx, offset CmndError_TextReferenceTable
        mov dx, word ptr cs:[ bx ]                      ; get address of message
        cmp dx, 0000                                    ; error not defined ?
        jz DisplayErrorCode_08
        call DisplayErrorMessage                        ; display message

DisplayErrorCode_08:
        pop bx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Display Out of Environment Space Message                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Only displays message if CY set                              ;
        ;                                                               ;
        ;...............................................................;

DisplayOutEnvSpace:

        jnc DisplayOutEnvSpace_08
        
        pushf
        push di
        mov dx, offset CmndError_OutOfEnvironmentSpace
        call DisplayErrorMessage
        pop di
        popf

DisplayOutEnvSpace_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Display Error Message                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   cs:dx  points to error message                              ;
        ;...............................................................;

DisplayErrorMessage:

        call DisplayLine                                ; print line
        call CRLF                                       ; new line
        stc
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Display Cr/Lf                                                ;
        ;...............................................................;

CRLF:
        push es
        push dx

        setES ds
        mov dx, offset RxDOS_NewLine
        call DisplayLine                                ; cr/lf

        pop dx
        pop es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Display Line                                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   es:si  points to message                                    ;
        ;...............................................................;

DisplayLine:

        push ax
        push cx
        push di
        mov di, dx                                      ; scan actual length of string

        xor ax, ax                                      ; null terminator
        cmp al, byte ptr es:[ di ]                      ; is string null ?
        jz DisplayLine_22                               ; yes, no need to display -->

        mov cx, -1
        repnz scasb                                     ; scan total length
        not cx                                          ; fixup
        dec cx                                          ;  ...length
        call DisplayLine_ByCount

DisplayLine_22:
        pop di
        pop cx
        pop ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Display Line Count                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   es:dx  points to message                                    ;
        ;   cx     characters                                           ;
        ;...............................................................;

DisplayLine_ByCount:

        saveAllRegisters

        setds es
        cmp byte ptr ss:[ PageLines ], 00               ; page lines ?
        jnz _displayPaginate_12                         ; go paginate -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if not paginate, can output entire stream
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_displayPaginate_10:
        setds es
        mov bx, STDOUT
        Int21 WriteFile                                 ; output
        jmp short _displayPaginate_22

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if paginate, must really test after each line
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_displayPaginate_12:
        push cx                                         ; starting length
        mov di, dx                                      ; string address
        mov al, ControlJ                                ; search for control J
        repnz scasb                                     ; scan total length
        mov ax, cx                                      ; remaining length after ctl-j
        pop cx                                          ; restore original length
        jnz _displayPaginate_10                         ; output line as is -->

        sub cx, ax                                      ; real length of current string
        push cx
        push ax                                         ; save remaining length
        mov bx, STDOUT
        Int21 WriteFile
        pop cx                                          ; remaining length
        pop ax                                          ; (exchanged registers)
        add dx, ax                                      ; next 

        or cx, cx                                       ; more ?
        jnz _displayPaginate_12                         ; yes -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_displayPaginate_22:
        restoreAllRegisters
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Paginate                                                     ;
        ;...............................................................;

_Paginate:
        push si
        call isStdOutAFile
        jz _Paginate_14                                 ; yes, no pause needed -->

        cmp byte ptr [ PageLines ], 00                  ; page lines ?
        jz _Paginate_14                                 ; don't worry about lines

        inc word ptr [ LinesDisplayed ]                 ; incr lines displayed

        call GetScreenLines
        dec ax                                          ; # lines on screen -1
        cmp ax, word ptr [ LinesDisplayed ]             ; beyond # screen lines ?
        jg _Paginate_14                                 ; not yet -->

        mov word ptr [ LinesDisplayed ], 0000           ; reset screen lines
        call _Pause

_Paginate_14:
        pop si
        ret 

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Set Paging Mode                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  If value is 8000 (switch is set), then set Paging Mode.      ;
        ;                                                               ;
        ;...............................................................;

setPagingMode:

        mov byte ptr [ PageLines ], 00                  ; paging mode
        and ax, SW_SWITCHSET
        jz setPagingMode_08

        mov byte ptr [ PageLines ], -1                  ; paging mode
        mov word ptr [ LinesDisplayed ], 0000           ; # lines displayed, curr page

setPagingMode_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Is StdOut A File ?                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   zr     stdout is a file                                     ;
        ;   nz     stdout is NOT a file                                 ;
        ;...............................................................;

isStdOutAFile:
        
        mov bx, STDOUT
        Int21 IoControl, 00h                            ; is device piped from a file ?
        mov ax, 1                                       ; non-zero value
        jc _isStdOutAFile_No                            ; we'll assume its not -->

        test dx, sftIsDevice                            ; if bit 7 = 0, handle is a file
        jnz _isStdOutAFile_No                           ; we'll assume its not -->
        
_isStdOutAFile_Yes:
        xor ax, ax                                      ; zero value if bat file

_isStdOutAFile_No:
        or ax, ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Scan Print Buffer                                            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This routine accepts a variable number of arguments and for- ;
        ;  mats an output buffer.  It works very similar to C's sprintf ;
        ;  function.                                                    ;
        ;                                                               ;
        ;  The input buffer may contain imbedded formatting codes:      ;
        ;                                                               ;
        ;  %c      insert character (pointer to character on stack)     ;
        ;  %s      insert string    (pointer to string on stack)        ;
        ;  %d      insert decimal   (pointer to decimal on stack)       ;
        ;  %ld     insert long      (pointer to long on stack)          ;
        ;                                                               ;
        ;  If a format command contains numbers, the number is inter-   ;
        ;  preted as a field width.  The output value will be right     ;
        ;  aligned within the field width if the width is preceeded     ;
        ;  with a negative sign.  Any characters that exceeds the field ;
        ;  width is ignored.                                            ;
        ;                                                               ;
        ;  If a format command contains a comma, the number will be     ;
        ;  decimal edited.                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   stack  argument                                             ;
        ;     .    argument                                             ;
        ;     .    argument                                             ;
        ;   stack  argument                                             ;
        ;   stack  format buffer                                        ;
        ;   stack  output buffer                                        ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   dx     pointer to output buffer                             ;
        ;   ax     number of arguments passed on stack                  ;
        ;                                                               ;
        ;  Note:                                                        ;
        ;   The number of arguments returned by _sprintf depends on the ;
        ;   number of arguments encountered.  It will never include the ;
        ;   two  standard  arguments,  the  format  and  output  buffer ;
        ;   addresses. These arguments are  poped off  the  stack  when ;
        ;   this routine performs an exit.                              ;
        ;                                                               ;
        ;...............................................................;

_sprintf:

        Entry 2
        arg _format
        arg _output
        def _args, 0000
        def _varg
        def _fieldwidth
        def _fieldflags

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  scan/copy buffer
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        lea di, word ptr [ _format ][ bp ]
        add di, 2                                       ; point to prev arg
        storarg _varg, di                               ; save var arg pointer

        mov si, word ptr [ _format ][ bp ]
        mov di, word ptr [ _output ][ bp ]

_sprintf_06:
        mov word ptr [ _fieldwidth ][ bp ], 0000        ; no width
        mov word ptr [ _fieldflags ][ bp ], 0000        ; no flags

_sprintf_08:
        lodsb                                           ; get character
        stosb                                           ; copy to output
        or al, al                                       ; all done ?
        ifz _sprintf_86                                 ; yes -->

        cmp al, '%'                                     ; format code ?
        jnz _sprintf_08                                 ; not yet -->
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  format code
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov word ptr [ _fieldwidth ][ bp ], 0000        ; no width
        mov word ptr [ _fieldflags ][ bp ], 0000        ; no flags

        dec di                                          ; kill format % in output
        mov byte ptr [ di ], 0                          ; stick a null code there

_sprintf_12:
        lodsb                                           ; get character that follows
        call _lowerCase                                 ; make it lower case

        or al, al                                       ; all done ?
        ifz _sprintf_86                                 ; yes -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is it long ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cmp al, 'l'                                     ; long flag ?
        jnz _sprintf_14                                 ; no -->
        or word ptr [ _fieldflags ][ bp ], SPRINTF_LONGFLAG
        jmp _sprintf_12

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is it a left justify
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_sprintf_14:
        cmp al, '-'                                     ; left justify
        jnz _sprintf_16                                 ; no -->
        or word ptr [ _fieldflags ][ bp ], SPRINTF_LEFTALIGN
        jmp _sprintf_12

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is it a comma (decimal) delimeter ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_sprintf_16:
        cmp al, ','                                     ; decimal delimeter ?
        jnz _sprintf_18                                 ; no -->
        or word ptr [ _fieldflags ][ bp ], SPRINTF_COMMADELIM
        jmp _sprintf_12

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is it a field width ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_sprintf_18:
        cmp al, '9'+1                                   ; number ?
        jnc _sprintf_22                                 ; no -->
        cmp al, '0'                                     ; number ?
        jc _sprintf_22                                  ; no -->

        and ax, 15                                      ; get number
        mov dx, word ptr [ _fieldwidth ][ bp ]          ; get width
        add dx, dx                                      ; x2
        add dx, dx                                      ; x4
        add dx, word ptr [ _fieldwidth ][ bp ]          ; x5
        add dx, dx                                      ; x10
        add dx, ax
        mov word ptr [ _fieldwidth ][ bp ], dx
        jmp _sprintf_12

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if its a valid formatting code
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_sprintf_22:
        cmp al, 'd'                                     ; decimal output ?
        jz _sprintf_26
        cmp al, 'x'                                     ; hex output ?
        jz _sprintf_26
        cmp al, 'c'                                     ; character ?
        jz _sprintf_32
        cmp al, 's'                                     ; string ?
        jz _sprintf_36
        cmp al, '%'                                     ; percent percent ?
        jz _sprintf_46

        jmp _sprintf_06

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  decimal
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_sprintf_26:
        push si
        getarg si, _varg                                ; get variable arg ptr
        mov si, word ptr [ si ]                         ; get argument pointer        
        add word ptr [ _varg ][ bp ], 2
        inc word ptr [ _args ][ bp ]

        xor dx, dx
        mov ax, word ptr [ si ]                         ; get number
        test word ptr [ _fieldflags ][ bp ], SPRINTF_LONGFLAG
        jz _sprintf_28
        mov dx, word ptr [ si+2 ]                       ; get long

_sprintf_28:
        mov bx, word ptr [ _fieldflags ][ bp ]          ; flags
        mov cx, word ptr [ _fieldwidth ][ bp ]          ; width
        call _sprintfNum                                ; convert numeric to ascii

        pop si
        jmp _sprintf_06

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  character
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_sprintf_32:
        push si
        getarg si, _varg                                ; get variable arg ptr
        mov si, word ptr [ si ]                         ; get argument pointer        
        add word ptr [ _varg ][ bp ], 2
        inc word ptr [ _args ][ bp ]

        mov cx, word ptr [ _fieldwidth ][ bp ]          ; width
        call _sprintfInitField

        mov al, byte ptr [ si ]
        stosb                                           ; store character

        pop si

        mov cx, word ptr [ _fieldwidth ][ bp ]          ; width
        or cx, cx
        ifz _sprintf_06

        dec cx
        call _sprintfPadField
        jmp _sprintf_06

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  string
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_sprintf_36:
        push si
        getarg si, _varg                                ; get variable arg ptr
        mov si, word ptr [ si ]                         ; get argument pointer        
        add word ptr [ _varg ][ bp ], 2
        inc word ptr [ _args ][ bp ]

        mov cx, word ptr [ _fieldwidth ][ bp ]          ; width
        call _sprintfInitField

_sprintf_38:
        lodsb
        or al, al                                       ; null terminator ?
        jz _sprintf_40                                  ; yes -->
        stosb
        or cx, cx                                       ; fixed count ?
        jz _sprintf_38
        loop _sprintf_38

_sprintf_40:
        or cx, cx                                       ; still more length to go ?
        jz _sprintf_42                                  ; no -->
        add di, cx                                      ; advance field length

_sprintf_42:
        pop si
        jmp _sprintf_06

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  %
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_sprintf_46:
        stosb
        jmp _sprintf_06

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  all done
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_sprintf_86:
        mov ax, word ptr [ _args   ][ bp ]
        add ax, ax                                      ; # words left on stack
        mov si, word ptr [ _output ][ bp ]
        mov dx, si
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Convert Long (dx:ax) to Ascii                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   dx:ax  long value                                           ;
        ;   cx     size of field to right justify                       ;
        ;   bx     display option flags:                                ;
        ;          8000  insert decimal commas                          ;
        ;                                                               ;
        ;...............................................................;

_sprintfNum:

        Entry
        defbytes _decDisplay, 16
        def _decimalflag, bx
        def _fieldwidth, cx
        def _spacing

        push si
        push di
        call _sprintfInitField                          ; init field to spaces
        mov byte ptr [ _spacing ][ bp ], 03             ; set spacing

        lea di, offset [ _decDisplay ][ bp ]
        push di

_sprintfNum_08:
        mov cx, 10
        call _div32                                     ; divide by 10
        or cl, '0'
        mov byte ptr [ di ], cl                         ; store character
        inc di
        
        mov cx, dx
        or cx, ax                                       ; more to go ?
        jz _sprintfNum_10                               ; no -->

        test word ptr [ _decimalflag ][ bp ], SPRINTF_COMMADELIM
        jz _sprintfNum_10                               ; no -->
        dec byte ptr [ _spacing ][ bp ]                 ; spacing break ?
        jnz _sprintfNum_10                              ; not yet -->

        mov byte ptr [ _spacing ][ bp ], 03             ; set spacing
        mov byte ptr [ di ], ','                        ; store comma
        inc di

_sprintfNum_10:
        mov cx, dx
        or cx, ax                                       ; more to go ?
        jnz _sprintfNum_08                              ; yes -->
        
        pop cx
        sub di, cx                                      ; total # chars output
        mov cx, di                                      ; length to cx

        pop di
        push di                                         ; where to begin right justify
        lea si, offset [ _decDisplay ][ bp ]            ; where data stored
        cmp word ptr [ _fieldwidth ][ bp ], 0000        ; left justified output ?
        jz _sprintfNum_20                               ; yes -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  right justified
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    ; be sure to blank fill field first

        add di, word ptr [ _fieldwidth ][ bp ]          ; 
        push di

_sprintfNum_16:
        lodsb                                           ; get character
        dec di
        mov byte ptr [ di ], al                         
        loop _sprintfNum_16

        pop di
        jmp short _sprintfNum_32

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  left justified
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_sprintfNum_20:
        lodsb                                           ; get character
        stosb
        loop _sprintfNum_20

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  done
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_sprintfNum_32:
        pop si                                          ; drop saved di
        pop si
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Init Field to Spaces                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   di     pointer to buffer                                    ;
        ;   cx     width of field                                       ;
        ;   bx     8000 if right justified                              ;
        ;...............................................................;

_sprintfInitField:
        
        or cx, cx                                       ; fixed count ?
        jz _sprintfInitField_08                         ; no -->

        push di
        push cx
        push ax
        mov al, ' '
        rep stosb

        pop ax
        pop cx
        pop di

_sprintfInitField_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Pad Field                                                    ;
        ;...............................................................;

_sprintfPadField:
        or cx, cx                                       ; still more length to go ?
        jz _sprintfPadField_08                          ; no -->
        add di, cx                                      ; advance field length

_sprintfPadField_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  32 Bit Divide                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax:dx  numerator                                            ;
        ;   cx     divisor                                              ;
        ;                                                               ;
        ;...............................................................;

_div32: or cx, cx                                       ; protect from zero divisor
        stc                                             ; in case of error
        jz _div32_return                                ; if so, just return with carry

        push bx
        mov bx, dx
        xchg ax, bx
        xor dx, dx
        div cx
      
        xchg ax, bx
        div cx                                          ; remainder will be in dx
        mov cx, dx
        mov dx, bx
        pop bx

_div32_return:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Screen Lines                                             ;
        ;...............................................................;

GetScreenLines:
        
        push ds
        push bx
        xor bx, bx
        mov ds, bx
        mov bx, offset 484h
        mov al, byte ptr [ bx ]                         ; Video Rows
        cbw                                             ; extend to full word
        inc al                                          ; correct number
        pop bx
        pop ds
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Physical Screen Clear                                        ;
        ;...............................................................;

PhysClearScreen:

        call GetScreenLines
        mov dh, al
        mov dl, 80

        mov ax, 0600h                                   ; entire screen
        mov bx, 0700h                                   ; blue/white        
    ;;  mov bx, 7100h                                   ; blue/white        
        mov cx, 0000h                                   ; from home row
        int 10h

        mov ax, 0F00h                                   ; read current mode
        int 10h                                         ; get page into BH

        mov ax, 0200h
        mov dx, 0100h
        int 10h                                         ; position cursor

        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Commands (Alphabetical)                                      ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Break [ ON | OFF ]                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Sets, clears or reports on RxDOS break option.               ;
        ;                                                               ;
        ;...............................................................;

_Break:

        Entry
        ddef __argarray, ss, di
        def _args, ax

        call CheckOptOneArg                             ; see if 1 arg 
        jc _BreakExit                                   ; if error -->
        
        or ax, ax                                       ; any arguments ?
        jz _BreakPrintcurrent                           ; none, print current status -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  test for On/ Off
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov si, word ptr [ di ]                         ; get single argument
        mov di, offset RxDOS_OnOff
        call CmndLookup                                 ; lookup command
        jc _BreakError                                  ; if not on/off, print line -->

        mov dl, bl
        Int21 CtrlBreakCheck, setControlC               ; set/clear Ctrl Break check
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  must specify on or off
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_BreakError:
        mov dx, offset CmndError_MustSpecifyOnOff
        call DisplayErrorMessage
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  print verify on/off value
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_BreakPrintcurrent:
        Int21 CtrlBreakCheck, getControlC               ; get/clear Ctrl Break check

        mov bl, dl
        xor bh, bh
        add bx, bx
        mov dx, word ptr [ _BreakOptions ][ bx ]
        call DisplayLine

_BreakExit:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Call batchfile arguments                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  Arg Array                                            ;
        ;   ax     Number of arguments in array                         ;
        ;...............................................................;

_Call:
_Loadhigh:

        Entry
        def __argarray, di
        def __tempargptr
        defbytes _pathArg, 128

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  find and execute program (unless its a batch file)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        lea dx, offset [ _pathArg ][ bp ]
        call _executeProgram                            ; load and execute
        jc _call_22                                     ; if some kind of error -->
        cmp ax, _BAT                                    ; is it a bat program ?
        jz _call_12                                     ; yes -->

        clc                                             ; clear error
        jmp short _call_22

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  execute (call) batch
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_call_12:
        xor cx, cx
        xor dx, dx
        mov bx, word ptr [ RxDOS_BatchFile. batchFileHandle ]
        Int21 MoveFilePointer, SEEK_CUR                 ; get current position

        mov word ptr [ RxDOS_BatchFile. batchFilePosition. _low ], ax
        mov word ptr [ RxDOS_BatchFile. batchFilePosition. _high ], dx

        mov si, offset RxDOS_BatchFile
        mov di, word ptr [ RxDOS_PrevStackFrame ]
        mov cx, sizeBATCH_ARGS
        rep movsb                                       ; copy current args to save area

        sub word ptr [ RxDOS_PrevStackFrame ], sizeBATCH_ARGS
        inc word ptr [ RxDOS_StackFrameNumEntries ]

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  setup batch file arguments
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov ax, _BAT                                    ; exit must contain _BAT type
        getarg di, __argarray
        lea dx, offset [ _pathArg ][ bp ]               ; pointer to file name
        call _saveBatchArguments                        ; if batch file, save arguments
        jnc _call_22                                    ; all done -->
         
        add word ptr [ RxDOS_PrevStackFrame ], sizeBATCH_ARGS
        dec word ptr [ RxDOS_StackFrameNumEntries ]     ; if error
        stc
             
_call_22:
        getarg di, __argarray
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Change Directory                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Changes the default directory for a drive.                   ;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  Arg Array                                            ;
        ;   ax     Number of arguments in array                         ;
        ;...............................................................;

_ChangeDir:
        
        Entry
        def  _disk
        ddef __argarray, ss, di
        defbytes _tempstring, 128

        call CheckOptOneArg                             ; see if 1 arg 
        ifc _changeDir_32                               ; if switch not expected -->
        jnz _changeDir_18                               ; if argument passed -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  display current
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        Int21 CurrentDisk                               ; get current disk

        mov dl, al                                      ; save drive letter
        or al, 'a'                                      ; drive
        lea di, offset [ _tempstring + 3][ bp ]
        mov byte ptr [ di - 3 ], al
        mov byte ptr [ di - 2 ], ':'
        mov byte ptr [ di - 1 ], '\'

        inc dl
        mov si, di
        Int21 GetCurrentDirectory                       ; get current directory

        push ss
        push si                                         ; where string
        call _lowerCaseString

        lea dx, offset _tempstring [ bp ]
        call DisplayLine
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  change drive: directory
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_changeDir_18:
        mov di, word ptr [ __argarray. _pointer ][ bp ]
        mov si, word ptr ss:[ di ]                      ; pointer to lead argument
        lea di, offset [ _tempstring ][ bp ]
        mov word ptr [ di ], '\'                        ; init area
        Int21 GetActualFileName                         ; expand name

        mov di, word ptr [ __argarray. _pointer ][ bp ]
        mov si, word ptr ss:[ di ]                      ; pointer to lead argument
        cmp byte ptr [ si+1 ], ':'                      ; drive ?
        jnz _changeDir_22                               ; no -->
        
        mov al, byte ptr [ si ]                         ; get drive letter
        call _lowerCase                                 ; lower case letter
        mov dl, al                                      ; to dl
        sub dl, 'a'                                     ; drive maps to 0, ...
        mov byte ptr [ _disk ][ bp ], dl                ; save disk

        Int21 SelectDisk                                ; can we select a drive ?
        Int21 CurrentDisk                               ; get current disk

        inc si
        inc si                                          ; point [si] past drive
        cmp al, byte ptr [ _disk ][ bp ]                ; disk changed ?
        jz _changeDir_22                                ; yes -->

        mov dx, offset CmndError_InvalidDrive
        call DisplayErrorMessage                        ; display message
        jmp short _changeDir_32

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  change directory
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_changeDir_22:
        mov dx, si                                      ; points to change string
        Int21 ChangeSubdirectory                        ; try changing dir
        jnc _changeDir_32                               ; if invalid
        call DisplayErrorCode                           ; if error

_changeDir_32:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Clear Screen                                                 ;
        ;...............................................................;

_Cls:
        call isStdOutAFile
        jnz _clsScreen                                  ; its a physical device -->

        mov dl, 'L'-40h                                 ; to a file we'll pipe a top of forms
        Int21 DisplayOutput
        jmp short _cls_36

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see how many lines
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_clsScreen:
        call PhysClearScreen

_cls_36:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Delete filename                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  Arg Array                                            ;
        ;   ax     Number of arguments in array                         ;
        ;...............................................................;

_Delete:

        Entry
        def __argarray, di
        def _nfiles, 0000
        def _filename
        defbytes _expandedname, 128

        call CheckOneArg
        jnc _Delete_06                                  ; arguments wrong -->

        mov dx, offset CmndError_FileNameMissing
        call DisplayErrorMessage
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  delete all files ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Delete_06:
        mov dx, word ptr [ di ]                         ; point to filename arg
        storarg _filename, dx                           ; save pointer

        xor cx, cx
        getarg dx, _filename                            ; get filename pointer
        Int21 FindFirstFile                             ; does file exist ?
        jc _Delete_36                                   ; no more -->

        mov al, '?'
        mov cx, sizeFILENAME
        mov di, offset [ RxDOS_DTA. findSrchName ]      ; expanded fcb match name
        repz scasb                                      ; *.* field ?
        jnz _Delete_16                                  ; no, proceed -->

_Delete_08:
        mov dx, offset _DeleteAllFiles
        call DisplayLine                                ; display line

        Int21 ConsoleInputNoEcho                        ; read keyboard
        call _upperCase

        push ax
        call CRLF
        pop ax
        cmp al, "Y"                                     ; if Yes -->
        jz _Delete_16
        cmp al, "N"                                     ; repeat if not right -->
        jnz _Delete_08
        jmp short _Delete_42                            ; else, its No -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  try to locate file (s)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Delete_16:
        xor cx, cx
        getarg dx, _filename                            ; get filename pointer
        Int21 FindFirstFile                             ; does file exist ?
        jc _Delete_36                                   ; no more -->

        getarg si, _filename                            ; get filename pointer
        lea di, offset [ _expandedname ][ bp ]
        Int21 GetActualFileName                         ; expand name
        jc _Delete_36                                   ; can't resolve name -->

        lea di, offset [ _expandedname ][ bp ]
        push ss
        push di
        call _stringLength
        add di, cx

_Delete_20:
        cmp byte ptr ss:[ di - 1 ], '\'
        jz _Delete_22
        dec di
        loop _Delete_20

_Delete_22:
        mov si, offset [ RxDOS_DTA. findFileName ]      ; current name
        call _copyString                                ; append directory name

        lea dx, offset [ _expandedname ][ bp ]
        Int21 DeleteFile                                ; delete file
        inc word ptr [ _nfiles ][ bp ]
        jmp _Delete_16

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  try to locate file 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Delete_36:
        cmp word ptr [ _nfiles ][ bp ], 0000            ; any files deleted ?
        jnz _Delete_42                                  ; yes -->

        mov dx, offset CmndError_NoFilesFound
        call DisplayErrorMessage

_Delete_42:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Disk Select Command                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  Arg Array                                            ;
        ;   ax     Number of arguments in array                         ;
        ;...............................................................;

_DiskSelect:

        push di
        inc di
        inc di
        call CheckNoArgs                                ; check for no arguments 
        jc _diskSelect_32                               ; if error -->

        pop di
        push di                                         ; restore pointer to arg 0
        mov si, word ptr ss:[ di ]                      ; pointer to lead argument
        cmp byte ptr [ si+1 ], ':'                      ; drive ?
        jnz _diskSelect_32                              ; no -->
        
        mov al, byte ptr [ si ]                         ; get drive letter
        call _lowerCase                                 ; lower case letter
        mov dl, al                                      ; to dl
        sub dl, 'a'                                     ; drive maps to 0, ...
        mov byte ptr [ _disk ][ bp ], dl                ; save disk

        Int21 SelectDisk                                ; can we select a drive ?
        Int21 CurrentDisk                               ; get current disk

        inc si
        inc si                                          ; point [si] past drive
        cmp al, byte ptr [ _disk ][ bp ]                ; disk changed ?
        jz _diskSelect_32                               ; yes -->

        mov dx, offset CmndError_InvalidDrive
        call DisplayErrorMessage                        ; display message

_diskSelect_32:
        pop di
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Echo                                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  Arg Array                                            ;
        ;   ax     Number of arguments in array                         ;
        ;...............................................................;

_Echo:

        Entry
        ddef __argarray, ss, di
        def _args, ax

        or ax, ax                                       ; no arguments ?
        jz _echo_printcurrent                           ; none, print current status -->

        dec ax                                          ; just one argument ?
        jnz _echo_printline                             ; no, print line -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  test for On/ Off
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov si, word ptr [ di ]                         ; get single argument
        mov di, offset RxDOS_OnOff
        call CmndLookup                                 ; lookup command
        jc _echo_printline                              ; if not on/off, print line -->

        mov byte ptr [ _EchoStatus ], bl                ; set echo status 
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  print current line
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_echo_printline:
        mov di, word ptr [ __argarray. _pointer ][ bp ]
        mov dx, word ptr [ di ]                         ; get start of string
        call DisplayLine
        call CRLF
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  print on/off value
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_echo_printcurrent:
        mov bl, byte ptr [ _EchoStatus ]
        xor bh, bh
        add bx, bx
        mov dx, word ptr [ _EchoOptions ][ bx ]
        call DisplayLine
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  End Call                                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  Arg Array                                            ;
        ;   ax     Number of arguments in array                         ;
        ;...............................................................;

_EndCall:

        mov bx, word ptr [ RxDOS_BatchFile. batchFileHandle ]
        Int21 CloseFile                                 ; close file
        mov word ptr [ RxDOS_BatchFile. batchFileHandle ], 0000

        cmp word ptr [ RxDOS_StackFrameNumEntries ], 0000
        jz _endcall_16                                  ; if no more files to backup to -->

        dec word ptr [ RxDOS_StackFrameNumEntries ]
        add word ptr [ RxDOS_PrevStackFrame ], sizeBATCH_ARGS
        mov si, word ptr [ RxDOS_PrevStackFrame ]
        mov di, offset RxDOS_BatchFile
        mov cx, sizeBATCH_ARGS
        rep movsb                                       ; copy

        mov si, word ptr [ RxDOS_PrevStackFrame ]
        cmp word ptr [ RxDOS_StackFrameNumEntries ], 0000
        jnz _endcall_18                                 ; if more files to backup to -->

_endcall_16:
        mov byte ptr [ _EchoStatus ], Yes

_endcall_18:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Execute Program or Batch File                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  Arg Array                                            ;
        ;   si     points to filename                                   ;
        ;   ax     Number of arguments in array                         ;
        ;...............................................................;

_executeProgram:

        Entry
        def __argarray, di
        def __progname, si
        def _pathArg, dx                                ; where to return fully qualified path name
        def __type

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  find program
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getarg dx, _pathArg                             ; where to return fully qualified path name
        call _findProgram                               ; locate program or bat file
        storarg __type, ax                              ; arg type
        jc _executeprogram_Error                        ; if program not found -->
        cmp ax, _BAT                                    ; is it a bat program ?
        jz _executeprogram_12                           ; yes -->

        getarg dx, _pathArg                             ; where to return fully qualified path name
        getarg di, __argarray
        call _loadProgram                               ; else load and execute program

_executeprogram_12:
        clc

_executeprogram_Exit:
        getarg di, __argarray
        getarg si, __progname
        getarg dx, _pathArg
        getarg ax, __type
        Return                                          ; return

_executeprogram_Error:
        getarg di, __argarray                           ; arg array
        call _NotValidCommand                           ; not valid (not found )
        stc                                             ; set error exit
        jmp _executeprogram_Exit

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Goto Label                                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  if batch file, locate label                                  ;
        ;...............................................................;

_Goto:

        Entry
        def  _labelPtr, si
        ddef _filePosition
        defbytes _buffer, sizeCmdLineStruct

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  prep argument
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        call CheckOneArg
        jc _Goto_36                                     ; error -->

        getarg si, _labelPtr

_goto_06:
        cmp byte ptr [ si ], ':'
        jnz _goto_08
        inc si
        jmp _goto_06

_goto_08:
        storarg _labelPtr, si

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  running batch file ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cmp word ptr [ RxDOS_BatchFile. batchFileHandle ], 0000
        jz _Goto_36                                     ; if not running a batch file -->

        call getBatchPosition                           ; save current batch file position
        mov word ptr [ _filePosition. _low  ][ bp ], ax
        mov word ptr [ _filePosition. _high ][ bp ], dx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  read lines 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor cx, cx
        xor dx, dx
        mov bx, word ptr [ RxDOS_BatchFile. batchFileHandle ]
        Int21 MoveFilePointer, SEEK_BEG                 ; start at beg of file        

_goto_16:
        xor ax, ax                                      ; no echo searching lines
        mov bx, word ptr [ RxDOS_BatchFile. batchFileHandle ]
        lea si, offset [ _buffer ][ bp ]
        mov byte ptr [ bufMaxLength ][ si ], sizeCmdLine
        call _ReadBatch                                 ; read batch line
        jz _goto_32                                     ; set position -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compare against search argument
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor bh, bh
        mov bl, byte ptr [ _buffer. bufActualLength ][ bp ]
        lea di, offset [ _buffer. bufData ][ bp ]
        mov byte ptr [ di + bx ], 00
        mov si, word ptr [ _labelPtr ][ bp ]

_goto_18:
        cmp byte ptr [ di ], ' '
        jz _goto_22
        cmp byte ptr [ di ], ':'
        jnz _goto_26

_goto_22:
        inc di
        jmp _goto_18

_goto_26:
        call _compareSubString                          ; label located ?
        jnz _goto_16                                    ; no -->
        jmp short _goto_36                              ; line located !

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  can't find, reset line location
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_goto_32:
        mov dx, word ptr [ _filePosition. _low  ][ bp ]
        mov cx, word ptr [ _filePosition. _high ][ bp ]
        mov bx, word ptr [ RxDOS_BatchFile. batchFileHandle ]
        Int21 MoveFilePointer, SEEK_BEG                 ; restore pointer

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_goto_36:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  If [not] ERRORLEVEL number command                           ;
        ;  If [not] EXIST filename command                              ;
        ;  If [not] string1==string2 command                            ;
        ;...............................................................;

_If:

        Entry 
        def __argarray, di
        defwords _argtypeArray, 20                      ; 5 args (type and text pointers)
        def _notflag, 0000
        def _usedportion, 0000

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  type first few arguments
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        lea di, offset [ _argtypeArray ][ bp ]          ; pointer to arg store value
        push di

        xor ax, ax
        mov cx, 5 * 4                                   ; words to clear
        rep stosw        

        pop bx
        mov cx, 5                                       ; # args to scan
        getarg di, __argarray

_If_08: mov si, word ptr [ di ]                         ; get argument passed
        or si, si                                       ; no more args ?
        jz _If_16                                       ; quit typing -->

        mov word ptr [ bx. _argtext ], si               ; pointer to text

        push di
        push bx
        push cx
        mov di, offset RxDOS_IfOptions
        call CmndLookup                                 ; lookup command
        mov ax, bx                                      ; save type

        pop cx
        pop bx
        pop di
        jc _If_12
        mov word ptr [ bx. _argtype ], ax               ; value returned from lookup
        
_If_12: add bx, 4                                       ; lookup first three args
        add di, 2                                       ; lookup first three args
        loop _If_08             

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if == special case
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_If_16: lea bx, offset [ _argtypeArray ][ bp ]          ; pointer to arg 
        cmp word ptr [ bx. _argtype ], IF_NOT           ; not argument ?
        jnz _If_20                                      ; no -->
        add bx, 4                                       ; then we'll use next as base
        storarg _notflag, -1                            ; if not

_If_20: cmp word ptr [ bx. _argtype ], IF_ERRORLEVEL
        ifz _ifErrorLevel
        cmp word ptr [ bx. _argtype ], IF_EXIST
        ifz _ifExist

        mov si, word ptr [ bx + 4 ][ _argtext ]         ; next arg
        or si, si                                       ; no text ?
        jz _IfSyntaxError                               ; syntax error -->

        cmp word ptr [ si ], "=="                       ; equals case ?
        jz _ifEquals

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  syntax error
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_IfSyntaxError:
        mov dx, offset CmndError_SyntaxError
        call DisplayErrorMessage                        ; display message
        Return
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if equals
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ifEquals:
        mov si, word ptr [ bx     ][ _argtext ]
        or si, si                                       ; arg before =='s 
        jz _IfSyntaxError                               ; if syntax error -->

        mov di, word ptr [ bx + 12][ _argtext ]
        storarg _usedportion, di
        or di, di                                       ; arg after =='s 
        jz _IfSyntaxError                               ; if syntax error -->

        push si
        call _ifStringlength                            ; get length of source string
        mov cx, ax

        push di
        call _ifStringlength                            ; get length of dest string
        cmp cx, ax                                      ; compare lengths
        jnz _ifNotEquals                                ; if syntax error -->

        rep cmpsb                                       ; strings compare equal ?
        lahf                                            ; zer/not zero to ah
        xor ah, byte ptr [ _notflag ][ bp ]             ; toggle Not Equal Bit
        and ah, 01000000b                               ; not zero if logical continue
        jnz _ifTrue

_ifNotEquals:
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if errorlevel
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ifErrorLevel:
        mov si, word ptr [ bx + 4 ][ _argtext ]         ; get text pointer to next arg
        storarg _usedportion, si
        or si, si
        jz _IfSyntaxError                               ; if syntax error -->

        call _GetNumber                                 ; get expected number
        jc _IfSyntaxError                               ; if syntax error -->
        
        dec ax                                          ; need to force carry
        cmp ax, word ptr [ RxDOS_ExecReturnCode ]       ; current >= return value ?
        lahf                                            ; zer/not zero to ah
        xor ah, byte ptr [ _notflag ][ bp ]             ; toggle Not Equal Bit
        and ah, 00000001b                               ; not zero if logical continue
        jnz _ifTrue                                     ; do rest of line -->
        
        Return
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if exist
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ifExist:
        mov cx, ATTR_DIRECTORY
        mov dx, word ptr [ bx + 4 ][ _argtext ]         ; get text pointer to next arg
        storarg _usedportion, dx
        Int21 FindFirstFile                             ; does this file exist ?

        lahf                                            ; zer/not zero to ah
        xor ah, byte ptr [ _notflag ][ bp ]             ; toggle Not Equal Bit
        and ah, 00000001b                               ; not zero means not True
        jz _ifTrue                                      ; do rest of line -->

        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if cond is true. execute command that follows
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_ifTrue:
        getarg di, __argarray
        getarg dx, _usedportion                         ; used args
        sub di, 2

_ifTrue_08:
        inc di
        inc di
        cmp word ptr [ di ], 0000
        ifz _IfSyntaxError
        cmp word ptr [ di ], dx                         ; located used part of If ?
        jc _ifTrue_08                                   ; not yet -->

        inc di
        inc di
        call _executeCommandArray
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute string lengths
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ifStringlength:

        Entry 1
        arg   _str

        push si
        push di
        getarg si, _str

_ifStringlength_08:
        cmp byte ptr [ si ], ' '
        jz _ifStringlength_12
        cmp byte ptr [ si ], '='
        jz _ifStringlength_12
        cmp byte ptr [ si ], 0
        jz _ifStringlength_12

        inc si
        jmp _ifStringlength_08

_ifStringlength_12:
        mov ax, si
        sub ax, word ptr _str [ bp ]

        pop di
        pop si
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Make Directory                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  Arg Array                                            ;
        ;   ax     Number of arguments in array                         ;
        ;...............................................................;

_makeDir:
        
        call CheckOneArg                                ; see if 1 arg 
        jc _makeDir_32                                  ; if error -->

_makeDir_22:
        mov dx, word ptr [ di ]                         ; point to filename arg
        Int21 CreateSubdirectory                        ; try changing dir
        jnc _makeDir_32                                 ; if valid -->

        cmp ax, errAccessDenied                         ; access denined ?
        jnz _makeDir_26                                 ; show other errors -->

        mov dx, offset CmndError_SubDirAlreadyExists
        call DisplayErrorMessage
        jmp short _makeDir_32

_makeDir_26:
        call DisplayErrorCode                           ; if error

_makeDir_32:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Path                                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  Arg Array                                            ;
        ;   ax     Number of arguments in array                         ;
        ;...............................................................;

_Path:

        Entry
        def __argarray, di
        def _pathArgBeg
        def _pathEndPtr, 0000
        def _foundflag, TRUE

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  locate current path arg, if any.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov si, offset RxDOS_PathSpec                   ; locate PATH=
        call searchEnvVariable                          ; env variable located ?
        jz _Path_08                                     ; if arg located -->
        storarg _foundflag, FALSE                       ; if not found
        storarg _pathEndPtr, di                         ; else this points to end
        xor di, di                                      ; say not found

_Path_08:
        storarg _pathArgBeg, di

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if no command given, type out current path value
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getarg di, __argarray
        mov si, word ptr [ di ]
        or si, si                                       ; any args passed ?
        jnz _Path_16                                    ; yes, go update path -->

        cmp word ptr [ _foundflag ][ bp ], TRUE         ; path found ?
        jnz _Path_14                                    ; no arg found -->

        push es
        getarg dx, _pathArgBeg                          ; restore arg to path
        mov es, word ptr [ _EnvSegment ]
        call DisplayLine                                ; show current path
        call CRLF                                       ; cr/lf
        pop es
        Return

_Path_14:
        mov dx, offset CmndError_NoPath
        call DisplayLine                                ; show current path
        Return
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  else, set path
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Path_16:
        cmp byte ptr [ si ], '='                        ; are we on = sign ?
        jnz _Path_20                                    ; no -->
        mov si, word ptr [ di + 2 ]                     ; get next arg

_Path_20:
        push si                                         ; env string to add
        getarg di, _pathEndPtr                          ; get pointer to end
        getarg dx, _pathArgBeg                          ; restore arg to path
        cmp word ptr [ _foundflag ][ bp ], TRUE         ; path arg found ?
        jnz _Path_24                                    ; no, no need to delete -->

        mov di, dx
        call deleteEnvVariable

_Path_24:
        mov si, offset RxDOS_PathSpec                   ; locate PATH=
        call insertEnvVariable
        call DisplayOutEnvSpace                         ; if error, display message
        pop si                                          ; path string to insert
        jc _Path_Return

        call insertEnvVariable
        call DisplayOutEnvSpace                         ; if error, display message

_Path_Return:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Pause                                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  Arg Array                                            ;
        ;   ax     Number of arguments in array                         ;
        ;...............................................................;

_Pause:
        mov dx, offset _PressAnyKeyToContinue
        Int21 DisplayString
        Int21 ClearBufferedKeyboardInput, KeyboardInput

        call CRLF
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Remark (Rem )                                                ;
        ;...............................................................;

_Rem:
        ret                                             ; do nothing

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Remove Directory                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  Arg Array                                            ;
        ;   ax     Number of arguments in array                         ;
        ;...............................................................;

_RemDir:
        
        call CheckOneArg                                ; see if 1 arg 
        jc _removeDir_32                                ; if error -->

        mov dx, word ptr [ di ]                         ; point to filename arg
        Int21 RemoveSubdirectory                        ; try command
        jnc _removeDir_32                               ; if valid -->

        call DisplayErrorCode                           ; if error

_removeDir_32:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  SET                                                          ;
        ;  SET variable=                                                ;
        ;  SET variable=string                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Display, set or remove environment variable.                 ;
        ;...............................................................;

_Set:

        Entry
        def __argarray, di
        def __deleteFlag

        call CountArgs
        or ax, ax                                       ; if no args 
        jnz _SetEnvVariable                             ; set/ clear env variable -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  display environment variables
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SetDisplay:
        xor si, si
        mov es, word ptr [ _EnvSegment ]

_SetDisplay_08:
        cmp byte ptr es:[ si ], 00
        jz _SetDisplayReturn
        cmp byte ptr es:[ si ], ';'                     ; if comment,
        jz _SetDisplay_12                               ; skip -->

        mov dx, si
        call DisplayLine
        call CRLF

_SetDisplay_12:
        inc si                                          ; scan to end of env string
        cmp byte ptr es:[ si - 1 ], 00
        jnz _SetDisplay_12
        jmp _SetDisplay_08 

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  upper case up to '='
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SetEnvVariable:

        mov di, word ptr [ __argarray ][ bp ]
        mov si, word ptr [ di ]                         ; get lead arg

_SetEnvVariable_08:
        mov al, byte ptr [ si ]                         ; get character
        call _upperCase                                 ; upper case
        mov byte ptr [ si ], al                         ; save it 

        inc si
        cmp al, '='                                     ; line contains a break ?
        jz _SetEnvVariable_12                           ; yes -->
        or al, al                                       ; end of string ?
        jnz _SetEnvVariable_08                          ; not yet -->

        mov dx, offset CmndError_SyntaxError
        call DisplayErrorMessage                        ; display message
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  search/ delete
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SetEnvVariable_12:
        xor ax, ax
        mov al, byte ptr [ si ]                         ; if char that follows is not
        storarg __deleteFlag, ax                        ; non-zero means insert

        mov si, word ptr [ di ]                         ; get lead arg
        call searchEnvVariable                          ; env variable located ?
        jnz _SetEnvVariable_18                          ; not found -->

        call deleteEnvVariable                          ; delete env variable
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  insert
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SetEnvVariable_18:
        mov si, word ptr [ __argarray ][ bp ]
        mov si, word ptr [ si ]                         ; get lead arg
        cmp word ptr [ __deleteFlag ][ bp ], 0000       ; non-zero means insert
        jz _SetDisplayReturn                            ; delete only -->

        call insertEnvVariable                          ; insert at end
        call DisplayOutEnvSpace                         ; if error, display message

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SetDisplayReturn:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Shift                                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  No parameters expected                                       ;
        ;...............................................................;

_Shift:

        mov si, offset ( RxDOS_BatchFile. batchArgPtrs ); copy args down
        mov cx, word ptr [ RxDOS_BatchFile. batchNumArgs ]
        or cx, cx
        jz _shift_32                                    ; if no arguments -->

_shift_08:
        mov ax, word ptr [ si + 2 ]
        mov word ptr [ si ], ax                         ; shift arg down
        add si, 2                                       ; point to next arg
        loop _shift_08                                  ; loop through all args

        dec word ptr [ RxDOS_BatchFile. batchNumArgs ]  ; decr # args

_shift_32:
        mov word ptr [ si ], 0000                       ; null out last arg
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Truename [ anypath ]                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Displays the DOS expanded filename.                          ;
        ;                                                               ;
        ;...............................................................;

_Truename:

        Entry
        defbytes _buffer, 128

        call CheckOptOneArg                             ; should have an arg
        jc _truename_36                                 ; error -->

        mov si, word ptr [ di ]                         ; get argument passed
        or si, si                                       ; if not actually null
        jnz _truename_24                                ; then fine -->
        mov si, offset RxDOS_TruenameCurrDir            ; get argument passed

_truename_24:
        lea di, offset [ _buffer ][ bp ]
        Int21 GetActualFileName

        lea dx, offset [ _buffer ][ bp ]
        call DisplayLine                                ; print line

_truename_36:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Type filename                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Unlike MSDOS, TYPE will permit both the use of the pause /p  ;
        ;  switch and wild card characters in a name.                   ;
        ;                                                               ;
        ;...............................................................;

_Type:

        Entry
        def __argarray, di
        def __pathPointer
        def __wildchars, FALSE
        def _abortFlag, FALSE
        def _handle

        defbytes _expandedPathName, 130
        defbytes _buffer, DYNAMIC_BUFFERSIZE + 2

        mov cx, 0001                                    ; at least one arg
        mov dx, 0999                                    ; unlimitd number of args
        mov bx, offset _TypeSwitches
        call PreProcessCmndLine                         ; process switches
        ifc _TypeReturn                                 ; if error -->

        mov ax, word ptr [ _TypePauseSwitch. swFlags ]
        call setPagingMode

        mov di, word ptr [ __argarray ][ bp ]
        call nullTerminateArgs                          ; null terminate args

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get next argument
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_TypeNext:
        mov di, word ptr [ __argarray ][ bp ]
        mov dx, word ptr [ di ]
        or dx, dx                                       ; any more args ?
        ifz _TypeReturn                                 ; if no more -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get path for argument
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov si, word ptr [ di ]                         ; get argument passed
        lea di, offset [ _expandedPathName ][ bp ]      ; 
        Int21 GetActualFileName

        xor ax, ax
        mov cx, -1                                      ; length (unsigned )
        lea di, offset [ _expandedPathName ][ bp ]      ; 
        repnz scasb                                     ; search for end character
        neg cx
        sub cx, 2                                       ; actual length

_TypeNext_08:
        cmp byte ptr ss:[ di - 1 ], '\'
        jz _TypeNext_14                                 ; if prec is separator -->
        cmp byte ptr ss:[ di ], '?'
        jnz _TypeNext_12                                ; if no wild characters
        storarg __wildchars, TRUE

_TypeNext_12:
        dec di                                          ; else ok to backup
        loop _TypeNext_08

_TypeNext_14:
        storarg __pathPointer, di
        
        xor cx, cx
        mov di, word ptr [ __argarray ][ bp ]
        mov dx, word ptr [ di ]
        Int21 findFirstFile                             ; filename found for arg ?
        jnc _TypeOpenFile                               ; yes, open and display -->

        push dx
        mov dx, offset _TypeCannotFind          
        call DisplayLine

        pop dx
        call DisplayLine                                ; show arg
        jmp _TypeReturn

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  open and print
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_TypeOpenFile:
        mov si, offset [ RxDOS_DTA. findFileName ]
        getarg di, __pathPointer

_TypeOpenFile_04:
        lodsb
        stosb
        or al, al
        jnz _TypeOpenFile_04

      ; mov dx, word ptr [ di ]
        lea dx, offset [ _expandedPathName ][ bp ]      ; 
        Int21 OpenFile, 0000                            ; open
        storarg _handle, ax                             ; save handle or error
        jc _TypeReturn                                  ; if something went wrong -->

        cmp word ptr [ __wildchars ][ bp ], TRUE        ; wild characters in name ?
        jnz _TypeReadFile                               ; go read, display file -->

        call CRLF

        mov dx, offset [ RxDOS_DTA. findFileName ]
        call DisplayLine                                ; show arg
        call CRLF

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  read and list
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_TypeReadFile:
        mov cx, DYNAMIC_BUFFERSIZE                      ; how much to read
        getarg bx, _handle                              ; get handle
        lea dx, offset [ _buffer ][ bp ]                ; where to read
        Int21 ReadFile                                  ; read buffer
        jc _TypeCloseFile
        
        or ax, ax                                       ; at end of file ?
        jz _TypeCloseFile                               ; yes, go to next -->

        mov cx, ax                                      ; how much read
        lea dx, offset [ _buffer ][ bp ]                ; where to read
        call DisplayLine_ByCount                        ; display buffer
        jnc _TypeReadFile                               ; keep reading file if no carry -->
        
        storarg _abortFlag, TRUE                        ; else abort

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  close file
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_TypeCloseFile:
        getarg bx, _handle                              ; get handle
        Int21 CloseFile
        call CRLF

        cmp word ptr [ _abortFlag ][ bp ], TRUE         ; abort ?
        jz _TypeReturn                                  ; exit ->

        Int21 findNextFile                              ; more files ?
        jnc _TypeOpenFile                               ; yes, open file -->

        add word ptr [ __argarray ][ bp ], 2
        jmp _TypeNext                                   ; else go to next -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_TypeReturn:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Verify [ ON | OFF ]                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Sets, clears or reports on RxDOS disk verify parameters.     ;
        ;                                                               ;
        ;...............................................................;

_Verify:

        Entry
        ddef __argarray, ss, di
        def _args, ax

        call CheckOptOneArg                             ; see if 1 arg 
        jc _VerifyExit                                  ; if error -->
        
        or ax, ax                                       ; any arguments ?
        jz _VerifyPrintcurrent                          ; none, print current status -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  test for On/ Off
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov si, word ptr [ di ]                         ; get single argument
        mov di, offset RxDOS_OnOff
        call CmndLookup                                 ; lookup command
        jc _VerifyError                                 ; if not on/off, print line -->

        mov al, bl
        Int21 SetVerifySwitch                           ; set or clear verify switch
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  must specify on or off
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_VerifyError:
        mov dx, offset CmndError_MustSpecifyOnOff
        call DisplayErrorMessage
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  print verify on/off value
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_VerifyPrintcurrent:
        Int21 GetVerify

        mov bl, al
        xor bh, bh
        add bx, bx
        mov dx, word ptr [ _VerifyOptions ][ bx ]
        call DisplayLine

_VerifyExit:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Version                                                      ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  No parameters expected                                       ;
        ;...............................................................;

_Ver:
        call CheckNoArgs                                ; check for no arguments 
        jc _ver_32                                      ; if error -->

        call CRLF

        mov dx, offset RxDOS_Version
        call DisplayLine

        mov dx, offset RxDOS_VersionCopyright
        call DisplayLine

_ver_32:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Volume                                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Displays Volume Information                                  ;
        ;                                                               ;
        ;...............................................................;

_Vol:

        Entry
        def  _currdisk, 0000
        defbytes _printbuffer, 128

        Int21 CurrentDisk
        add al, 'A'                                     ; get drive
        mov byte ptr [ _currdisk ][ bp ], al            ; save current disk

        call checkOptOneArg
        jc _Vol_60                                      ; parameter is wrong -->
        jz _Vol_30                                      ; if no parameters -->

        mov si, word ptr [ di ]                         ; get pointer to first arg
        mov ax, word ptr [ si ]
        cmp ah, ':'                                     ; not a drive specification ?
        jnz _Vol_62                                     ; no -->

        call _lowerCase                                 ; disk id
        and ax, not 20h                                 ; upper case

        cmp al, 'Z'+1
        jnc _Vol_62
        cmp al, 'A'
        jc _Vol_62

        storarg _currdisk, ax                           ; save disk

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get disk volume
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Vol_30:
        mov bx, offset _Dir_NoVolumeLabel               ; assume no volume label
        call returnVolumeName
        jc _Vol_62                                      ; cannot open drive -->
        jnz _Vol_34                                     ; if no volume name -->

        push di                                         ; save vol label pointer
        mov bx, offset _Dir_VolumeLabel                 ; print statement format

_Vol_34:
        lea di, offset [ _currdisk ][ bp ]              ; pointer to current disk
        push di                                         ; current disk
        push bx                                         ; format
        lea di, offset [ _printbuffer ][ bp ]
        push di
        call _sprintf
        add sp, ax                                      ; # args passed
        call DisplayLine

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Vol_60:
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  drive specification error
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Vol_62:
        mov dx, offset CmndError_InvalidDrive
        call DisplayErrorMessage
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Commands Not Supported                                       ;
        ;...............................................................;

_Chcp:
_Ctty:
        mov dx, offset CmndError_NotSupportedInRxDOS
        call DisplayErrorMessage
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Other Commands                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;                                                               ;
        ;                                                               ;
        ;...............................................................;

_Help:
_History:
_Move:
        mov dx, offset CmndError_NotSupportedYet
        call DisplayErrorMessage
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Not A Valid Command                                          ;
        ;...............................................................;

_NotValidCommand:
        push di
        mov dx, offset CmndError_BadCommandOrFileName
        call DisplayLine                                ; display error message

        pop di
        mov bx, word ptr ss:[ di + 2 ]                  ; get second argument
        or bx, bx                                       ; second argument ?
        jz _NotValidCommand_08                          ; if no second argument -->
        mov word ptr ss:[ bx ], 0                       ; set at null terminator

_NotValidCommand_08:
        mov dx, word ptr ss:[ di ]                      ; get command name
        call DisplayLine                                ; display error message

        mov dx, offset CmndError_EndOfBadCommand
        call DisplayLine                                ; display error message
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Exit                                                         ;
        ;...............................................................;

_Exit:
        push es
        Int21 GetPSPAddress

        mov es, bx
        cmp word ptr es:[ pspParentId ], 0000           ; no parent ?
        jz _Exit_12                                     ; can't exit -->

        lds bx, dword ptr [ _Int23_Original ]
        Int21 SetIntVector, 23h                         ; restore int 23
        Int21 TerminateProgram, 00h                     ; else ok

_Exit_12:
        pop es
        ret 

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Compute Length                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;  cx   length of string                                        ;
        ;  si   starting address of string.                             ;
        ;...............................................................;

_computeLength:
        push si

_computeLength_06:
        cmp byte ptr [ si ], ' '+ 1
        jc _computeLength_08
        cmp byte ptr [ si ], ')'
        jz _computeLength_08
        cmp byte ptr [ si ], ','
        jz _computeLength_08
        cmp byte ptr [ si ], ';'
        jz _computeLength_08

        inc si
        jmp _computeLength_06

_computeLength_08:
        pop cx
        xchg cx, si
        sub cx, si
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Compute Length                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  di   starting address of NULL terminated string.             ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;  ZR   if lead character is @ sign                             ;
        ;  all registers preserved except flags.                        ;
        ;...............................................................;

deleteLeadAtSign:
        cmp byte ptr [ di ], '@'                        ; starts with @ sign ?
        jnz deleteLeadAtSign_12                         ; no -->

        pushf
        SaveSegments di, si, ax

        setDS es
        mov si, di                                      ; copy buffer ptr
        inc si                                          ; source

deleteLeadAtSign_08:
        lodsb                                           ; copy character
        stosb                                           ; copy character
        or al, al                                       ; until zero byte
        jnz deleteLeadAtSign_08

        RestoreSegments ax, si, di
        popf

deleteLeadAtSign_12:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  See if Wild Characters Are Used.                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;  cx   length of string                                        ;
        ;  si   starting address of string.                             ;
        ;                                                               ;
        ;...............................................................;

_Seeif_WildCharacter:

        push si
        push cx

_WildCharacter_08:
        lodsb
        cmp al, '?'
        jz _WildCharacter_12
        cmp al, '*'
        jz _WildCharacter_12

        loop _WildCharacter_08
        stc                                     

_WildCharacter_12:
        pop cx
        pop si
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Create Default Master Environment, if none                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   bx     pointer to an environment                            ;
        ;   es     pointer to PSP                                       ;
        ;...............................................................;

_DefaultCreateEnvironment:

        Entry
        def  _CurrentPSP, es
        ddef _pgmName, 0000, 0000                       ; save ptr to orig pgm name

        or bx, bx                                       ; env block passed ?
        jz _crEnvironment_24                            ; if no name passed, create -->

        mov es, bx                                      ; current segment
        xor di, di                                      ; byte offset
        cmp word ptr es:[ di ], 0000                    ; empty env block ?
        jnz _crEnvironment_36                           ; no, leave alone -->

        add di, 4                                       ; skip over # strings word
        stordarg _pgmName, es, di                       ; save ptr to orig pgm name

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; allocate env segment
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_crEnvironment_24:
        mov bx, word ptr [ _EnvSize ]                   ; from /E or default size
        shr bx, 1
        shr bx, 1
        shr bx, 1
        shr bx, 1                                       ; # paragraphs
        Int21 AllocateMemory                            ; try to allocate
        jc _crEnvironment_36                            ; if couldn't -->
        
        mov es, ax
        mov word ptr [ _EnvSegment ], ax                ; updated env segment address
        
        xor di, di
        mov cx, word ptr [ _EnvSize ]
        clearMemory 

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; create COMSPEC
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cmp word ptr [ _pgmName. _segment ][ bp ], 0000 ; source name ?
        jz _crEnvironment_36                            ; no -->
        
        mov si, offset RxDOS_CommandSpec
        call insertEnvVariable                          ; insert COMSPEC=
        jc _crEnvironment_36                            ; if no room -->

        push ds
        getdarg ds, si, _pgmName                        ; com spec name
        call insertEnvVariable                          ; insert spec name
        pop ds                                          ; restore stack
     ;  jc _crEnvironment_36                            ; if no room -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_crEnvironment_36:
        mov bx, word ptr [ _EnvSegment ]
        mov es, word ptr [ _CurrentPSP ][ bp ]
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Save Batch File Position                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   bx     batch file handle                                    ;
        ;   dx:ax  position in batch file                               ;
        ;...............................................................;

getBatchPosition:

        xor cx, cx
        xor dx, dx
        mov bx, word ptr [ RxDOS_BatchFile. batchFileHandle ]
        Int21 MoveFilePointer, SEEK_CUR
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Manage Stdin Input                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:dx  buffer (pointer to max length)                       ;
        ;...............................................................;

_getStdinLine:

        Entry
        def _buffer, dx

        push di
        push bx
        push dx

        mov di, dx
        mov byte ptr [ bufActualLength ][ di ], 00

        mov bx, STDIN
        Int21 IoControl, 00h                            ; get stdin device data
        test dx, sftIsDevice                            ; file or device ?
        jnz _getStdinLine_32                            ; device, just read directly -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  read by character if file
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_getStdinLine_08:
        mov di, word ptr [ _buffer ][ bp ]
        mov bl, byte ptr [ bufActualLength ][ di ]      ; store character into buffer
        xor bh, bh
        lea dx, offset [ bufData ][ di + bx ]           ; data address

        mov cx, 1
        mov bx, STDIN
        Int21 ReadFile                                  ; read character
        jc _getStdinLine_28

        or ax, ax                                       ; read anything ?
        jz _getStdinLine_28                             ; no, see if return -->

        mov di, word ptr [ _buffer ][ bp ]
        mov bl, byte ptr [ bufActualLength ][ di ]      ; store character into buffer
        xor bh, bh

        mov dl, byte ptr [ bufData ][ di + bx ]         ; get character 
        cmp dl, 'J'-40h                                 ; character lf ?
        jz _getStdinLine_08                             ; ignore lf -->

        push dx
        Int21 DisplayOutput                             ; echo character

        pop dx
        inc byte ptr [ bufActualLength ][ di ]          ; store character into buffer
        cmp dl, 'M'-40h                                 ; character cr ?
        jz _getStdinLine_34                             ; yes, end of input -->

        mov al, byte ptr [ bufActualLength ][ di ]      ; actual less than max ?
        cmp al, byte ptr [ bufMaxLength ][ di ]         ; get max
        jc _getStdinLine_08
        jmp short _getStdinLine_34

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  end of file
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_getStdinLine_28:
        mov di, word ptr [ _buffer ][ bp ]
        mov dl, byte ptr [ bufActualLength ][ di ]      ; store character into buffer
        or dl, dl                                       ; any characters ?
        jnz _getStdinLine_34                            ; yes, return -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  cancel stdin
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov ax, 1                                       ; this may have to change !
        mov bx, STDIN
        call _CloseRedirectedDevice

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  read directly if device
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_getStdinLine_32:
        pop dx                                          ; buffer address
        Int21 GetLine                                   ; read direct from device

_getStdinLine_34:
        getarg dx, _buffer
        pop bx
        pop di
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Read Line                                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:si  buffer (pointer to max length)                       ;
        ;   bx     file handle (null value means stdin )                ;
        ;   al     echo status                                          ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   ax     actual length not incl cr.                           ;
        ;                                                               ;
        ;...............................................................;

_ReadLine:

        Entry
        def _Handle, bx
        ddef _buffer, ss, si

        or bx, bx                                       ; process from batch file ?
        jz _ReadLine_08                                 ; no, read from console -->
        call _ReadBatch                                 ; else read batch file
        jmp short _ReadLine_26

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  read from keyboard
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ReadLine_08:
        call DisplayPrompt                              ; display prompt

        mov dx, word ptr [ _buffer. _pointer ][ bp ]
        call _getStdinLine                              ; read buffer
        call CRLF

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ReadLine_26:
        mov si, word ptr [ _buffer. _pointer ][ bp ]
        mov al, byte ptr [ bufActualLength ][ si ]
        and ax, 255                                     ; actual length
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Read Batch Line                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:si  buffer (pointer to max length)                       ;
        ;   bx     file handle (null value means stdin )                ;
        ;   al     echo status                                          ;
        ;...............................................................;

_ReadBatch:

        Entry
        def  _bytesRead
        def  _echoline, ax
        def  _Handle, bx
        ddef _buffer, ss, si

        add si, bufData                                 ; offset to data
        ddef _bufferData, ss, si

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  read line
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov cx, sizeCmdLine
        mov dx, word ptr [ _bufferData. _pointer ][ bp ]; where to read
        mov bx, word ptr [ RxDOS_BatchFile. batchFileHandle ]

        call ReadBatchLine
        mov word ptr [ _bytesRead ][ bp ], ax           ; save count
        jnz _ReadBatch_08                               ; if none -->

        call _EndCall                                   ; end batch file call

        xor ax, ax                                      ; return zero bytes
        mov di, word ptr [ _buffer. _pointer ][ bp ]    ; buffer address
        mov byte ptr [ bufActualLength ][ di ], al
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  trim line to carriage return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ReadBatch_08:
        mov di, word ptr [ _buffer. _pointer ][ bp ]    ; buffer address
        mov byte ptr [ bufActualLength ][ di ], al

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  perform variable replacement
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ReadBatch_20:
        mov bx, word ptr [ _bytesRead ][ bp ]
        mov si, word ptr [ _bufferData. _pointer ][ bp ]; buffer address
        mov byte ptr [ si+bx ], 00
        call ReplaceTempVariables                       ; replace %n variables

        mov si, word ptr [ _buffer. _pointer ][ bp ]    ; buffer address
        mov byte ptr [ bufActualLength ][ si ], cl

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  echo enabled ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cmp byte ptr [ _echoLine ][ bp ], 00
        jz _ReadBatch_32                                ; echo is off -->

        mov cx, ax                                      ; length of line
        mov di, word ptr [ _bufferData. _pointer ][ bp ]; buffer address
        call deleteLeadAtSign                           ; clean up if lead at sign
        jz _ReadBatch_32                                ; if at sign, no echo -->

_ReadBatch_24:
        cmp byte ptr [ di ], ' '                        ; still in white space ?
        jnz _ReadBatch_26                               ; no, echo line -->
        inc di
        loop  _ReadBatch_24

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  echo this line
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ReadBatch_26:
        call DisplayPrompt                              ; display prompt

        mov dx, di
        call DisplayLine                                ; echo line
        call CRLF

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ReadBatch_32:
        mov di, word ptr [ _buffer. _pointer ][ bp ]    ; buffer address
        mov ax, word ptr [ _bytesRead ][ bp ]
        or di, di                                       ; data always returned
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Read Batch Line                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   bx     batch file handle                                    ;
        ;   dx     line buffer address                                  ;
        ;...............................................................;


ReadBatchLine:

        Entry
        def  _Handle, bx
        def  _lineBuffer, dx
        def  _bytesRead, 0000
        ddef _filePosition
        defbytes _tempbuffer, 2

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get current position in file
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor cx, cx
        xor dx, dx
        Int21 MoveFilePointer, SEEK_CUR
        mov word ptr [ _FilePosition. _low  ][ bp ], ax
        mov word ptr [ _FilePosition. _high ][ bp ], dx ; save current position

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get current position in file
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getarg si, _lineBuffer
        mov byte ptr [ si ], 00                         ; set null terminator at beg

        getarg bx, _Handle
        mov cx, sizeCmdLine
        getarg dx, _lineBuffer                          ; where to read batch file
        Int21 ReadFile                                  ; read
        jc ReadBatchLine_36                             ; if error -->
        mov word ptr [ _bytesRead ][ bp ], ax

        or ax, ax                                       ; end of batch file ?
        jz ReadBatchLine_36                             ; yes -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  trim line to carriage return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov cx, ax                                      ; scan line 
        getarg di, _lineBuffer                          ; where to read batch file

        mov al, ControlM
        repnz scasb                                     ; scan for cr
        jz ReadBatchLine_20                             ; carriage return found -->

ReadBatchLine_12:
        mov cx, 1
        lea dx, offset [ _tempbuffer ][ bp ]            ; scan char by character till cr
        Int21 ReadFile                                  ; read
        jc ReadBatchLine_36                             ; if error -->
        or ax, ax                                       ; end of file ?
        jz ReadBatchLine_36                             ; yes -->

        cmp byte ptr [ _tempbuffer ][ bp ], ControlM    ; carriage return ?
        jnz ReadBatchLine_12                            ; no -->
        jmp short ReadBatchLine_30                      ; process line a bit -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  position line immediately after cr
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

ReadBatchLine_20:
        mov ax, word ptr [ _bytesRead ][ bp ]
        sub ax, cx
        push ax

        cmp byte ptr [ di ], ControlJ
        jnz ReadBatchLine_22                            ; if line feed doesn't follow -->
        inc ax                                          ; account for lf

ReadBatchLine_22:
        mov dx, word ptr [ _FilePosition. _low  ][ bp ]
        mov cx, word ptr [ _FilePosition. _high ][ bp ] ; orig position
        add dx, ax
        adc cx, 0000

        getarg bx, _Handle
        Int21 MoveFilePointer, SEEK_BEG                 ; get current position

        pop ax
        dec ax                                          ; don't include cr
        mov word ptr [ _bytesRead ][ bp ], ax           ; bytes read

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  does line begin with a line feed ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

ReadBatchLine_30:
        getarg di, _lineBuffer
        cmp byte ptr [ di ], ControlJ
        jnz ReadBatchLine_32                            ; no -->

        getarg cx, _bytesRead
        mov si, di
        inc si
        rep movsb                                       ; copy

        dec word ptr [ _bytesRead ][ bp ]

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  place null terminator at end
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

ReadBatchLine_32:
        getarg bx, _bytesRead
        getarg si, _lineBuffer
        mov byte ptr [ si+bx ], 00                      ; set null terminator at end 

        mov ax, bx
        or si, si                                       ; nz means valid return
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  end of file or error
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

ReadBatchLine_36:
        xor ax, ax
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Command Shell Start                                          ;
        ;...............................................................;

CommandBegin:

        cli
        mov ax, cs
        mov ds, ax
        mov es, ax
        mov ss, ax
        mov sp, offset RxDOS_CmdStack

        sti
        cld

        mov byte ptr [ _EchoStatus ], Yes                     ; echo is ON
        mov word ptr [ RxDOS_BatchFile. batchNumArgs ], 0000  ; no args

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  process startup command switches
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        push ds
        mov ax, ds
        sub ax, sizePSP / sizeParagraph
        mov ds, ax                                      ; segment 

        xor cx, cx
        mov si, offset 81h                              ; where command line is loc
        mov cl, byte ptr [ si - 1 ]
        call ProcessStartupSwitches                     ; get options
        pop ds

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  initialize stack frame
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        Entry
        defbytes _cmdline, sizeCmdLineStruct
        defbytes _SaveBatchInfo, sizeBATCH_ARGS         ;  previous batch file 

        mov word ptr [ RxDOS_PrevStackFrame ], sp       ; current stack frame

        mov si, offset RxDOS_BatchFile
        mov di, word ptr [ RxDOS_PrevStackFrame ]       ; copy args to stack frame
        mov cx, sizeBATCH_ARGS
        rep movsb                                       ; copy

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  release unwanted memory (may be removed later)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        Int21 GetPSPAddress
        mov word ptr [ _PSPSegment ], bx
        
        mov es, bx                                      ; block to be changed
        mov bx, offset RXDOSLASTADDRESS + sizeParagraph + 100h
        shr bx, 1
        shr bx, 1
        shr bx, 1
        shr bx, 1
        Int21 ModifyAllocatedMemory, 00

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get env, psp addresses
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov es, word ptr [ _PSPSegment ]                ; get PSP address
        mov bx, word ptr es:[ pspEnvironment ]          ; get seg of Environment
        mov word ptr [ _EnvSegment ], bx
        call _DefaultCreateEnvironment

        dec bx                                          ; locate Mem Control Block
        mov es, bx                                      ; point to mem block
        mov bx, word ptr es:[ _memAlloc ]               ; allocation in paras of env block
        shl bx, 1
        shl bx, 1
        shl bx, 1
        shl bx, 1                                       ; conv paras to segs
        mov word ptr [ _EnvSize ], bx                   ; save env size

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  set DTA
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        Int21 GetDTA
        mov word ptr [ RxDOS_OrigDTA. _segment ], es
        mov word ptr [ RxDOS_OrigDTA. _pointer ], bx
        currSegment es

        mov dx, offset RxDOS_DTA
        Int21 SetDTA                                    ; set new disk transfer address

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  set interrupt vectors
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        Int21 GetIntVector, 23h
        mov word ptr [ _Int23_Original. _pointer ], bx
        mov word ptr [ _Int23_Original. _segment ], es
        
        mov dx, offset _ControlC_ISR
        Int21 SetIntVector, 23h                         ; int 23h

        mov dx, offset CommandInput
        Int21 SetIntVector, 22h                         ; int 22h

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init screen
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        setES ds                                        ; restore es
        call PhysClearScreen                            ; start with a clear screen

        mov dx, offset RxDOS_Version
        call DisplayLine                                ; print startup version

        mov dx, offset RxDOS_VersionCopyright
        call DisplayLine                                ; print startup version

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  execute Autoexec.Bat
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

         mov dx, offset AUTOEXEC_BAT
         Int21 OpenFile, OPEN_ACCESS_READONLY            ; try to open AUTOEXEC
         jc CommandInput                                 ; if not found -->

         mov word ptr [ RxDOS_BatchFile. batchFileHandle ], ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  basic command loop
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

CommandInput:
        mov sp, word ptr [ RxDOS_PrevStackFrame ]       ; current stack frame

        mov dx, offset RxDOS_DTA
        Int21 SetDTA                                    ; set new disk transfer address

        lea si, offset [ _cmdline ][ bp ]
        mov byte ptr ss:[ si ], sizeCmdLine
        mov bx, word ptr [ RxDOS_BatchFile. batchFileHandle ]
        mov al, byte ptr [ _EchoStatus ]
        call _ReadLine
        jz CommandInput                                 ; no, redisplay prompt -->

        mov bx, ax
        lea si, offset [ _cmdline. bufActualLength ][ bp ]
        call _CommandParser                             ; go parse / execute
        jmp CommandInput   

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Process Startup Switches                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Inputs:                                                      ;
        ;   ds:si  Pointer to Command Line                              ;
        ;   cx     Command line length                                  ;
        ;                                                               ;
        ;                                                               ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ; Warning: DS points to command line buffer                     ;
        ;...............................................................;

ProcessStartupSwitches:

        mov bx, cx
        mov byte ptr [ si + bx ], 0                     ; null terminate line

_startupSwitches_08:
        lodsb

_startupSwitches_12:
        or al, al                                       ; end of line ?
        jz _startupSwitches_Return                      ; yes -->

        cmp al, '/'                                     ; switch ?
        jnz _startupSwitches_08                         ; not yet -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get character past switch
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        lodsb
        call _upperCase                                 ; upper case char
        cmp al, 'E'                                     ; set environment ?
        jz _startupSwitches_EnvSize                     ; yes -->
        cmp al, 'C'                                     ; one command ?
        jz _startupSwitches_SingleCommand               ; yes -->
        jmp _startupSwitches_12                         ; continue processing -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  One time command (unsupported)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_startupSwitches_SingleCommand:
        jmp _startupSwitches_08                         ; continue processing -->
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Get Env Size
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_startupSwitches_EnvSize:
        xor dx, dx                                      ; accum value

        inc si                                          ; skip through ':' or other delim
      ; cmp al, ':'                                     ; expect separator
      ; jz _startupSwitches_20                          ; ok -->

_startupSwitches_20:
        lodsb                                           ; get character
        cmp al, '0'
        jc _startupSwitches_26                          ; if not a digit -->
        cmp al, '9'
        jg _startupSwitches_26                          ; if not a digit -->

        and ax, 15                                      ; strip down value
        mov cx, dx
        add dx, dx                                      ; 2
        add dx, dx                                      ; 4
        add dx, cx                                      ; 5
        add dx, dx                                      ; 10
        add dx, ax                                      ; add new value
        jmp _startupSwitches_20

_startupSwitches_26:
        cmp dx, DEFAULT_MINALLOWEDENVIRONMENT
        jc _startupSwitches_12
        cmp dx, DEFAULT_MAXALLOWEDENVIRONMENT
        jnc _startupSwitches_12

        mov word ptr ss:[ _EnvSize ], dx                ; env size
        jmp _startupSwitches_12                         ; continue processing -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_startupSwitches_Return:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Internal Commands                                            ;
        ;...............................................................;

        Even

RxDOS_InternalCommands:

        Cmnd _ChangeDir,        "cd"                    ; 
        Cmnd _ChangeDir,        "chdir"                 ; 
        Cmnd _MakeDir,          "md"                    ; 
        Cmnd _MakeDir,          "mkdir"                 ; 
        Cmnd _RemDir,           "rd"                    ; 
        Cmnd _RemDir,           "rmdir"                 ; 

        Cmnd _Break,            "break"                 ;
        Cmnd _Call,             "call"                  ; 
        Cmnd _Chcp,             "chcp"                  ; 
        Cmnd _Cls,              "cls"                   ; 
        Cmnd _Copy,             "copy"                  ;
        Cmnd _Ctty,             "ctty"                  ; 
        Cmnd _Date,             "date"                  ;
        Cmnd _Delete,           "del"                   ; 
        Cmnd _Dir,              "dir"                   ; 
        Cmnd _Echo,             "echo"                  ; 
        Cmnd _Delete,           "erase"                 ; 
        Cmnd _Exit,             "exit"                  ;
        Cmnd _For,              "for"                   ; 
        Cmnd _Goto,             "goto"                  ; 
        Cmnd _If,               "if"                    ; 
        Cmnd _Loadhigh,         "lh"                    ; 
        Cmnd _Loadhigh,         "loadhigh"              ; 
        Cmnd _Path,             "path"                  ;
        Cmnd _Pause,            "pause"                 ;
        Cmnd _Prompt,           "prompt"                ; 
        Cmnd _Rem,              "rem"                   ; 
        Cmnd _Rename,           "rename"                ;
        Cmnd _Rename,           "ren"                   ;
        Cmnd _Set,              "set"                   ;
        Cmnd _Shift,            "shift"                 ; 
        Cmnd _Time,             "time"                  ;
        Cmnd _Truename,         "truename"              ; 
        Cmnd _Type,             "type"                  ;
        Cmnd _Verify,           "verify"                ; 
        Cmnd _Ver,              "ver"                   ; 
        Cmnd _Vol,              "vol"                   ;
        dw -1

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Unix Style Commands                                          ;
        ;...............................................................;

        Even

RxDOS_UNIXStyleCommands:

        Cmnd _Type,             "cat"                   ; 
        Cmnd _Copy,             "cp"                    ;
        Cmnd _Delete,           "rm"                    ; 
        Cmnd _Dir,              "ls"                    ; 
        Cmnd _Move,             "mv"                    ; * future feature
        Cmnd _Set,              "setenv"                ;
        dw -1

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Environment Variables                                        ;
        ;...............................................................;

RxDOS_CommandSpec:              asciz "COMSPEC="
RxDOS_DirSpec:                  asciz "DIRCMD="
RxDOS_PathSpec:                 asciz "PATH="
RxDOS_PromptSpec:               asciz "PROMPT="
RxDOS_RxDOSSwitchSpec:          asciz "SWITCH="
RxDOS_RxDOSSpec:                asciz "RXDOS="
RxDOS_UNIXSpec:                 asciz "UNIX"

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Execution Order                                              ;
        ;...............................................................;

RxDOS_ExecOrder:                asciz ".com"
                                asciz ".exe"
                                asciz ".bat"
                                dw -1

RxDOS_ExecReturnCode:           dw 0

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  On/Off Options                                               ;
        ;...............................................................;

RxDOS_OnOff:                    Cmnd 1,                 "on"
                                Cmnd 0,                 "off"
                                dw -1

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  If Options                                                   ;
        ;...............................................................;

RxDOS_IfOptions:                Cmnd IF_ERRORLEVEL,     "errorlevel"
                                Cmnd IF_EXIST,          "exists"
                                Cmnd IF_EXIST,          "exist"
                                Cmnd IF_NOT,            "not"
                                dw -1

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  For Options                                                  ;
        ;...............................................................;

RxDOS_ForArgs:                  Cmnd _IN,               "in"
                                Cmnd _DO,               "do"
                                dw -1

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Version Prompt                                               ;
        ;...............................................................;

RxDOS_Version:                  db "RxDOS Version 6.00", 0

RxDOS_VersionCopyright:         db 13, 10
                                db "(c) Copyright 1990-1997 Api Software and Mike Podanoffsky", 13, 10
                                db "All rights reserved", 13, 10, 13, 10, 0

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Error Table                                                  ;
        ;...............................................................;

        Even

CmndError_TextReferenceTable:

        dw 0                                            ; 0000
        dw 0                                            ; 0001
        dw CmndError_FileNotFound                       ; 0002
        dw CmndError_InvalidDirectory                   ; 0003
        dw 0                                            ; 0004
        dw CmndError_AccessDenied                       ; 0005
        dw 0                                            ; 0006
        dw 0                                            ; 0007
        dw 0                                            ; 0008
        dw 0                                            ; 0009
        dw 0                                            ; 0010
        dw 0                                            ; 0011
        dw 0                                            ; 0012
        dw 0                                            ; 0013
        dw 0                                            ; 0014
        dw 0                                            ; 0015
        dw CmndError_CurrentDirectory                   ; 0016

CmndError_RefTableEntries       equ ($ - CmndError_TextReferenceTable)/2

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Error Messages                                               ;
        ;...............................................................;

CmndError_AccessDenied:           asciz "access denied"
CmndError_BadCommandOrFileName:   asciz "bad command: could not find or execute '"
CmndError_EndOfBadCommand:        asciz "'", 13, 10
CmndError_BadSwitch:              asciz "bad switch - "
CmndError_CannotCopyUntoSelf:     asciz "file cannot be copied unto itself"
CmndError_CannotCreateFile:       asciz "cannot create destination file"
CmndError_ContentsLostBeforeCopy: asciz "contents of file lost before copy"
CmndError_CurrentDirectory:       asciz "cannot remove current directory"
CmndError_FileAlreadyExists:      asciz "file not found or already exists"
CmndError_FileNotFound:           asciz "file not found"  
CmndError_NoFilesFound:           asciz " no files found", 13, 10
CmndError_InvalidDate:            asciz "invalid date"
CmndError_InvalidDirectory:       asciz "invalid directory"
CmndError_InvalidDrive:           asciz "invalid drive"
CmndError_InvalidTime:            asciz "invalid time"
CmndError_MustSpecifyOnOff:       asciz "must specify ON or OFF"
CmndError_NotSupportedYet:        asciz "command not supported yet !"
CmndError_NotSupportedInRxDOS:    asciz "command not supported in RxDOS"
CmndError_FileNameMissing:        asciz "file name missing"
CmndError_SubDirAlreadyExists:    asciz "subdirectory already exists"
CmndError_SyntaxError:            asciz "syntax error - use Help"
CmndError_TooManyParameters:      asciz "too many parameters - "
CmndError_ParametersMissing:      asciz "parameter(s) missing"
CmndError_NoPath:                 asciz "no path"
CmndError_InvalidNumberArguments: asciz "invalid number of arguments"
CmndError_OutOfEnvironmentSpace:  asciz "out of environment space"

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Other Variables                                              ;
        ;...............................................................;

RxDOS_NewLine:                  db 13, 10, 0

                                db "c:"
RxDOS_RootDirectory             db "\*.*", 0            ; root directory
RxDOS_AllFiles                  db "*"                  ; combines to for *.*
RxDOS_AllExtensions             db ".*", 0

RxDOS_TruenameCurrDir           db ".", 0               ; current directory

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Control C Handling                                           ;
        ;...............................................................;

                               even 
_Int23_Original                 dd 0

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Echo/ Paging                                                 ;
        ;...............................................................;

_EchoStatus                     db Yes                  ; echo is ON
PageLines                       db 00                   ; paging mode
LinesDisplayed                  dw 00                   ; # lines displayed, curr page

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Prompt                                                       ;
        ;...............................................................;

RxDOS_DefaultPrompt             db "$n$g", 0
RxDOS_Prompt                    db "$n$g", 0, 128 dup(0)

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Special Command Separators                                   ;
        ;...............................................................;

_UnixStyle                      db 0                    ; non-zero if Unix style commands    
_SwitchChar                     db "/"                  ; switch character
_CmndParse_Separators           db " <>|[],+=()%", doubleQuote, singleQuote, 0
                                db 20 dup(0)

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Environment Location/ Size                                   ;
        ;...............................................................;

_PSPSegment                     dw 0                    ; PSP Segment Address
_EnvSegment                     dw 0                    ; Env Segment Address
_EnvSize                        dw DEFAULT_MINENVIRONMENT ; Env Size (bytes)

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Batch Arguments                                              ;
        ;...............................................................;

        BATCH_ARGS struc

batchArgPtrs                    dw ?                    ; 0
                                dw ?                    ; 1 arg pointers
                                dw ?                    ; 2       .
                                dw ?                    ; 3       .
                                dw ?                    ; 4       .
                                dw ?                    ; 5       .
                                dw ?                    ; 6       .
                                dw ?                    ; 7       .
                                dw ?                    ; 8       .
                                dw ?                    ; 9       .

batchNumArgs                    dw ?                    ; number of batch arguments
batchFileHandle                 dw ?                    ; batch file handle
batchFilePosition               dd ?                    ; batch file position

batchArgStore                   db 128 dup(0)

        BATCH_ARGS ends

sizeBATCH_ARGS                  equ size BATCH_ARGS

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Batch File Arguments                                         ;
        ;...............................................................;

RxDOS_BatchFile                 db sizeBATCH_ARGS dup(0)
RxDOS_PrevStackFrame            dw 0                    ; end call stack frame
RxDOS_StackFrameNumEntries      dw 0                    ; number of entries in stack

 IFNDEF RxDOS_DEBUG
AUTOEXEC_BAT                    db "\autoexec.bat", 0

 ELSE
AUTOEXEC_BAT                    db "\autoexec.tst", 0

 ENDIF

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Restore Original Parameters                                  ;
        ;...............................................................;

RxDOS_OrigDTA                   dd ?                    ; original DTA value

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Type Switches                                                ;
        ;...............................................................;

_TypeSwitches:
_TypePauseSwitch:               Switch 'p', 0
                                db -1

_TypeCannotFind:                asciz 'cannot find - '

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Dir Switches                                                 ;
        ;...............................................................;

_DirSwitches:
_DirPauseSwitch:                Switch 'p', 0
_DirWideSwitch:                 Switch 'w', 0
_DirAttribSwitch:               Switch 'a', SW_LETTERCHOICE, SW_DIR_ATTRIB
_DirOrderSwitch:                Switch 'o', SW_LETTERCHOICE, SW_DIR_ORDER
_DirSubDirSwitch:               Switch 's', 0
_DirLowerCaseSwitch:            Switch 'l', 0
_DirBareSwitch:                 Switch 'b', 0
                                db -1

SW_DIR_ATTRIB:                  db '-dnsra', 0
SW_DIR_ORDER:                   db '-negsd', 0

_Dir_NoVolumeLabel:             asciz " Volume in drive %c has no label", 13, 10
_Dir_VolumeLabel:               asciz " Volume in drive %c is %s", 13, 10
_Dir_VolumeSerialNumber:        asciz " Volume Serial Number is %s", 13, 10
_Dir_DirectoryOf:               asciz " Directory of %s", 13, 10, 13, 10
_Dir_Files:                     db    "    %5d file(s) %11,ld bytes", 13, 10
                                db    "                  %11,ld bytes free", 13, 10, 0
_Dir_FileEntry:                 db    "%8s %3s %9ld %s  %s", 13, 10, 0
_Dir_DirEntry:                  db    "%8s %3s <DIR>     %s  %s", 13, 10, 0

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Copy Messages                                                ;
        ;...............................................................;

_Copy_FilesCopied:              db    ' %d file(s) copied', 13, 10, 0

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Pause/ Continue                                              ;
        ;...............................................................;

_PressAnyKeyToContinue:         asciz "Press any key to continue . . . $"
_Dir_Continuing:                asciz "(continuing %s)", 13, 10

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Break Display Options                                        ;
        ;...............................................................;

_BreakOptions:                  dw _BreakIsOFF, _BreakIsON

_BreakIsOFF:                    asciz "Break is OFF", 13, 10
_BreakIsON:                     asciz "Break is ON", 13, 10

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Echo Display Options                                         ;
        ;...............................................................;

_EchoOptions:                   dw _EchoIsOFF, _EchoIsON

_EchoIsOFF:                     asciz "Echo is OFF", 13, 10
_EchoIsON:                      asciz "Echo is ON", 13, 10

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Verify Display Options                                       ;
        ;...............................................................;

_VerifyOptions:                 dw _VerifyIsOFF, _VerifyIsON

_VerifyIsOFF:                   asciz "Verify is OFF", 13, 10
_VerifyIsON:                    asciz "Verify is ON", 13, 10

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Delete Yes/No                                                ;
        ;...............................................................;

_DeleteAllFiles:                db    "All files in directory will be deleted!", 13, 10
                                asciz "Are you sure (Y/N)?"

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Date Display Options                                         ;
        ;...............................................................;

_ShowCurrentDate:               asciz "Current date is %s"
_PleaseEnterDate:               asciz 13, 10, "Enter new date "

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Time Display Options                                         ;
        ;...............................................................;

_ShowCurrentTime:               asciz "Current time is %s"
_PleaseEnterTime:               asciz 13, 10, "Enter new time: "

RxDOSIntl_DateTimeTable:        intldate 001, "mm/dd/yyyy hh:mm:ss.00" ; USA
                                intldate 002, "yyyy-mm-dd HH:mm:ss,00" ; Canada-French
                                intldate 003, "dd/mm/yyyy hh:mm:ss.00" ; Latin America
                                intldate 031, "dd-mm-yyyy HH:mm:ss,00" ; Netherlands
                                intldate 032, "dd/mm/yyyy HH:mm:ss,00" ; Belgium
                                intldate 033, "dd.mm.yyyy HH:mm:ss,00" ; France
                                intldate 034, "dd/mm/yyyy HH:mm:ss,00" ; Spain
                                intldate 036, "yyyy-mm-dd HH:mm:ss,00" ; Hungary
                                intldate 038, "yyyy-mm-dd HH:mm:ss,00" ; Yugoslavia
                                intldate 039, "dd/mm/yyyy HH.mm.ss,00" ; Italy
                                intldate 041, "dd.mm.yyyy HH,mm,ss.00" ; Switzerland
                                intldate 042, "yyyy-mm-dd HH:mm:ss,00" ; Czechoslovakia
                                intldate 044, "dd/mm/yyyy HH:mm:ss.00" ; United Kingdom
                                intldate 045, "dd-mm-yyyy HH:mm:ss,00" ; Denmark
                                intldate 046, "yyyy-mm-dd HH.mm.ss,00" ; Sweden 
                                intldate 047, "dd.mm.yyyy HH:mm:ss,00" ; Norway
                                intldate 048, "yyyy-mm-dd HH:mm:ss,00" ; Hungary
                                intldate 049, "dd.mm.yyyy HH:mm:ss,00" ; Germany
                                intldate 055, "dd/mm/yyyy HH:mm:ss,00" ; Brazil
                                intldate 061, "dd/mm/yyyy HH:mm:ss.00" ; Intl English
                                intldate 351, "dd-mm-yyyy HH:mm:ss,00" ; Portugal
                                intldate 358, "dd.mm.yyyy HH.mm.ss,00" ; Finland
                                dw -1

RxDOSIntl_TimeTemplate          db "hh:mm:ss.00", 0
RxDOSIntl_DateTemplate          db "www mm/dd/yyyy", 0              ; USA

RxDOSIntl_DayOfWeek             db "SunMonTueWedThuFriSat", 0

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Disk Transfer Address                                        ;
        ;...............................................................;

                              even  
RxDOS_DTA:                      db 128 dup(' ')

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Command Shell Stack                                          ;
        ;...............................................................;

                              even  
                                dw 5E5Eh                        ; bottom signature

                                db ((DYNAMIC_BUFFERSIZE + 2 * 1024)/8) dup('RxDOSCMD')
                               ; db 512 dup('RxDOSCMD')          ; 4k
RxDOS_AltStack                  dw 0

                                db ((DYNAMIC_BUFFERSIZE + 2 * 1024)/8) dup('RxDOSCMD')
RxDOS_CmdStack                  dw 0
                                dw 5E5Eh                        ; top signature
RxDOSCMD_ProgSize               equ ((($ + PARAGRAPH - 1) / PARAGRAPH))

RxDOSCMD                        ENDS
                                END  CommandBegin
