SETARGV.ASM -- Parse Command Line

NAME SETARGV
PAGE 60,132
;[]------------------------------------------------------------[]
;| SETARGV.ASM -- Parse Command Line |
;| |
;| Turbo-C Run Time Library version 2.0 |
;| |
;| Copyright (c) 1988 by Borland International Inc. |
;| All Rights Reserved. |
;[]------------------------------------------------------------[]

INCLUDE RULES.ASI

; Segment and Group declarations

Header@

; External references

ExtSym@ _argc, WORD, __CDECL__
dPtrExt@ _argv, __CDECL__
ExtSym@ _psp, WORD, __CDECL__
ExtSym@ _envseg, WORD, __CDECL__
ExtSym@ _envLng, WORD, __CDECL__
ExtSym@ _osmajor, BYTE, __CDECL__
ExtProc@ abort, __CDECL__

ifdef WILD
ExtProc@ sbrk, __CDECL__
endif

SUBTTL Parse Command Line
PAGE
;/* */
;/*-----------------------------------------------------*/
;/* */
;/* Parse Command Line */
;/* ------------------ */
;/* */
;/*-----------------------------------------------------*/
;/* */
PSPCmd equ 00080h

CSeg@

IF LPROG
SavedReturn dd ?
ELSE
SavedReturn dw ?
ENDIF
SavedDS dw ?
SavedBP dw ?


ifdef WILD

;------------------------------------------------------------------------------
;
; Not enough space on stack for the program name.
;
BadProgName label near
jmp abort@

endif

;==============================================================================

ifdef WILD
PubProc@ _wildargv, __CDECL__
else
PubProc@ _setargv, __CDECL__
endif

; First, save caller context and Return Address

pop word ptr SavedReturn
IF LPROG
pop word ptr SavedReturn+2
ENDIF
mov SavedDS, ds
cld

; Compute Command Line size

mov es, _psp@
mov si, PSPCmd ; ES: SI = Command Line address
xor ah, ah
lods byte ptr es:[si]
inc ax ; AX = Command Line size including \r
mov bp, es
xchg dx, si ; BP:DX = Command Line address
xchg bx, ax ; BX = Command line size

; Compute Program Name size

mov si, _envLng@
add si, 2 ; SI = Program name offset
mov cx, 1 ; CX = Filename size (includes \0)
cmp _osmajor@, 3
jb NoProgramName
mov es, _envseg@
mov di, si ; SI = argv[0] address
mov cl, 07fh
xor al, al
repnz scasb
jcxz BadProgName
xor cl, 07fh ; CX = Filename size (includes \0)
NoProgramName label near

; Reserve space for the arguments

sub sp, 2 ; To be sure nothing in SS:FFFF
mov ax, 1
ifndef WILD
add ax, bx
endif
add ax, cx
and ax, not 1
mov di, sp
sub di, ax
jb BadProgName
mov sp, di ; SS:DI = Command Line storage address

; Copy ProgName to the stack

mov ax, es
mov ds, ax
mov ax, ss
mov es, ax
ifndef WILD
push cx
endif
dec cx
rep movsb
xor al, al
stosb ; ASCIIZ string

; Process Command Line.

;==============================================================================
ifdef WILD
;==============================================================================

;
; The value of "wild_attr" is used in the "findfirst" call as the file
; attribute.
;
; The default value is 0, which will only include "regular" files.
;
; Adding 10H to this value will include directories, 04h will include system
; files, and 02h will include hidden files.
;

wild_attr equ 0 ; include only regular files

;------------------------------------------------------------------------------

ffblk struc

ff_reserved db 21 dup (?)
ff_attrib db ?
ff_ftime dw ?
ff_fdate dw ?
ff_fsize dd ?
ff_name db 14 dup (?)

ffblk ends

wild_init_space equ 128 ; initial buffer allocation
wild_more_space equ 256 ; buffer size increment

;------------------------------------------------------------------------------

wild_buff_addr equ [bp]
wild_buff_size equ [bp+4]
wild_buff_max equ [bp+6]
wild_arg_src equ [bp+8]
wild_arg_dst equ [bp+10]

wild_argument equ [bp+12]
wild_destin equ [bp+16]
wild_path_len equ [bp+20]
wild_argc equ [bp+22]

wild_DTA_save equ [bp+24]
wild_ffblk equ [bp+28]

wild_frame_size equ 28 + TYPE ffblk

;------------------------------------------------------------------------------

mov cx, bp ; save segment of command line
dec bx ; don't need trailing \0

sub sp, wild_frame_size
mov bp, sp ; bp points at local variables

push dx ; save cmd line addr
push cx ; save cmd line seg
push bx ; save cmd line size
mov ax, wild_init_space
mov wild_buff_size, ax ; save initial size
ifndef __HUGE__
mov ds, savedDS
endif
push ax
call sbrk@
pop cx ; toss parameter
pop cx ; restore cmd line size
pop ds ; restore cmd line seg
pop si ; restore cmd line addr

mov wild_buff_addr, ax ; save offset
if LDATA
mov wild_buff_addr+2, dx ; save segment
and ax, dx
else
mov wild_buff_addr+2, ss ; seg = SS
endif
cmp ax, -1
je NoSbrkSpace ; abort if not enough space
add ax, wild_buff_size
mov wild_buff_max, ax ; save max offset

mov ah, 2fh
int 21h ; get current DTA
mov wild_DTA_save, bx
mov wild_DTA_save+2, es
push ds
push ss ; fflbk is on stack
pop ds
lea dx, wild_ffblk
mov ah, 1ah
int 21h ; switch DTA to ffblk
pop ds

les di, dword ptr wild_buff_addr
xor dx, dx ; dx = # of arguments
;
; Start new argument.
;
NewArg: mov wild_arg_dst, di
xor bh, bh ; bh = wildcard flag
;
; Skip leading whitespace.
;
ArgCopy: mov wild_arg_src, si ; save address of argument
call GetChar
jc ArgCopyDone ; jump if no more characters
jz ArgCopyLoop
cmp al, ' '
je ArgCopy ; skip whitespace
cmp al, 9
je ArgCopy
cmp al, 13
je ArgCopy
cmp al, '"'
je ArgQuote ; jump if quoted string
;
; Loop to copy unquoted argument.
;
ArgCopyLoop: call ArgPushChar ; store character in destination
call GetChar
jc ArgComplete ; jump if end of line
jz ArgCopyLoop ; jump if \"
cmp al, ' '
je ArgComplete ; whitespace terminates
cmp al, 9
je ArgComplete
cmp al, 13
je ArgComplete ; whitespace terminates
cmp al, '"'
jne ArgCopyLoop
ArgComplete: call ProcessArg ; copy or expand argument
jmp SHORT NewArg

NoSbrkSpace: jmp abort@ ; error jump

;
; Here if quoted argument.
;
ArgQuote: call GetChar
jc QuoteDone
jz QuoteNext
cmp al, '"' ; terminating quote ?
je QuoteDone
QuoteNext: call ArgPushChar ; store character in destination
jmp SHORT ArgQuote
;
; End of a quoted argument. Push terminating null, do not expand.
;
QuoteDone: xor al, al
call ArgPushChar ; push terminating null
inc dx ; bump arg count
jmp SHORT NewArg ; go get more

;------------------------------------------------------------------------------
;
; Here when done expanding command line. Go build the argv array.
;
ArgCopyDone: mov ax, di ; ax = unused space
sub ax, wild_buff_max
jz ArgNoWaste ; skip if all used
push dx
push di
ifndef __HUGE__
mov ds, savedDS
endif
push ax
call sbrk@ ; release unused memory
pop cx ; toss parameter
pop di
pop dx
ArgNoWaste: lds si, dword ptr wild_buff_addr
mov cx, di
sub cx, si ; cx = number of bytes in expanded line
inc dx ; count program name
jmp BuildArgv

;------------------------------------------------------------------------------
;
; Routine to retrieve the next character from the command line.
; Sets CF when end of line reached.
; Sets ZF when \ character found (i.e. \")
;
; bh.bit0 set if wildcard chars found (* or ?)
; bh.bit1 set if \ character found (\")
;
GetChar proc near

jcxz GchEnd ; jump if no more
lodsb
dec cx
cmp al, '\' ; escape ?
je GchEsc
cmp al, '?'
je GchWild
cmp al, '*'
je GchWild
GchRet: or ah, 1 ; clear CF and ZF
ret
GchWild: test bh, bh
jnz GchRet ; give up if \" has been found
or bh, 1
ret
GchEsc: jcxz GchRet ; check for \ at end of line
cmp byte ptr [si],'"'
jne GchRet ; only \" is special
lodsb
dec cx
mov bh, 2 ; set \ flag
xor ah, ah ; clear CF, set ZF
ret
GchEnd: stc
ret

GetChar endp

;------------------------------------------------------------------------------
;
; Routine to expand a wildcard parameter.
;
; DS:SI = argument address
; ES:DI = destination
; Returns:
; CX = number of expanded arguments (0 = no match)
;
WildExpand proc near

push ds
mov wild_argument, si
mov wild_argument+2, ds
mov wild_destin, di
mov wild_destin+2, es
mov word ptr wild_argc, 0
;
; Find the length of the path prefix, if any.
;
mov bx, si
WildFindPath: lodsb
and al, al
jz WildEndPath
cmp al, '\'
je WildDelimiter
cmp al, ':'
je WildDelimiter
cmp al, '\'
jne WildFindPath
WildDelimiter: mov bx, si ; save addr past last delimiter
jmp SHORT WildFindPath
WildEndPath: sub bx, wild_argument
mov wild_path_len, bx

mov ah, 4eh
mov cx, wild_attr ; file attribute
lds dx, dword ptr wild_argument
int 21h ; find first matching file ...
jc WildDone
;
; We have a matching file. Add it to the destination string (unless "." or "..")
;
WildAddArg:
;
; If directories are included (10h set in wild_attr), ignore "." and ".."
;
if wild_attr AND 10h
push ss
pop ds
lea si,wild_ffblk.ff_name
cmp byte ptr [si],'.' ; skip if doesn't start with "."
jne WildNoDir
cmp byte ptr [si+1],0 ; check for "."
je WildNameNext
cmp word ptr [si+1],'.' ; check for ".."
je WildNameNext
WildNoDir:
endif

inc word ptr wild_argc
les di, dword ptr wild_destin
mov cx, wild_path_len ; prefix filename with path
jcxz WildCopyName
lds si, dword ptr wild_argument
WildCopyPath: lodsb
call ArgPushChar
loop WildCopyPath
WildCopyName: lea si,wild_ffblk.ff_name ; copy filename from ffblk
WildNameLoop: lods byte ptr ss:[si]
push ax
call ArgPushChar ; store char in destination
pop ax
and al, al ; continue until \0
jnz WildNameLoop
mov wild_destin, di
WildNameNext: mov ah, 4fh
int 21h ; find next matching file
jnc WildAddArg
;
; Done with expansion. Restore ES:DI, set CX, and return.
;
WildDone: mov cx, wild_argc
les di, dword ptr wild_destin
pop ds
ret

WildExpand endp

;------------------------------------------------------------------------------
;
; Routine to store a character in the destination string.
;
ArgPushChar proc near

cmp di, wild_buff_max ; space available ?
jae ArgMoreSpace
stosb ; yes --> store character
ret
;
; No more argument space. Grab some more memory through sbrk.
;
ArgMoreSpace: push ds
push es
push si
push di
push ax
push bx
push cx
push dx

ifndef __HUGE__
mov ds, savedDS
endif
mov ax, wild_more_space
add wild_buff_size, ax ; bump allocated size
add wild_buff_max, ax ; bump end pointer
push ax
call sbrk@
pop cx
if LDATA
and ax, dx
endif
cmp ax, -1
je NoArgSpace ; abort if not enough space

pop dx
pop cx
pop bx
pop ax
pop di
pop si
pop es
pop ds
stosb ; store character
ret

ArgPushChar endp

;------------------------------------------------------------------------------
;
; Not enough space to process the command line .... abort.
;
NoArgSpace: jmp abort@

;------------------------------------------------------------------------------
;
; Routine to process an argument.
;
ProcessArg proc near

push bx
xor al, al
call ArgPushChar ; null-terminate
pop bx
test bh, 1 ; wildcards present ?
jnz ArgWild
inc dx ; bump arg count
ret
;
; We have a wildcard argument. Expand it.
;
ArgWild: push cx
push [si] ; save word following argument
mov byte ptr [si],0 ; null-terminate argument
xchg si, wild_arg_src ; si = argument address
push di
mov di, wild_arg_dst
push dx
call WildExpand
pop dx
pop bx
and cx, cx ; see if any matched
jnz ArgWildSome
mov di, bx ; none ---> use unexpanded argument
mov cx, 1 ; bump arg count by 1
ArgWildSome: add dx, cx
mov si, wild_arg_src
pop [si] ; restore word following argument
pop cx
ret

ProcessArg endp

;------------------------------------------------------------------------------
;
; Build the argv array. [DS:SI] is the expanded command line, CX its length.
; DX has the number of arguments (including the program name).
;
BuildArgv: push ds
push dx
lds dx, dword ptr wild_DTA_save
mov ah, 1ah
int 21h ; switch to original DTA
pop dx
pop ds

add sp, wild_frame_size ; remove local variables

mov es,savedDS
mov es:[_argc@], dx
inc dx ; argv ends with a NULL pointer
shl dx, 1 ;   argc * 2 (LDATA = 0)
IF LDATA
shl dx, 1 ;   argc * 4 (LDATA = 1)
ENDIF
mov bx, sp ; point to program name
mov bp, sp
sub bp, dx
jb NoArgSpace
mov sp, bp ; SS:BP = argv array address
mov word ptr es:[_argv@], bp
IF LDATA
mov word ptr es:[_argv@+2], ss
ENDIF
mov [bp], bx ; set argv[0] to program name
IF LDATA
mov [bp+2], ss ; program name is on the stack
ENDIF
add bp, dPtrSize

SetArgvX label near
jcxz SetLastArg
mov [bp], si ; Set argv[n]
IF LDATA
mov [bp+2], ds
ENDIF
add bp, dPtrSize
CopyArg label near
lodsb
or al, al
loopnz CopyArg
jz SetArgvX
SetLastArg label near
xor ax, ax
mov [bp], ax
IF LDATA
mov [bp+2], ax
ENDIF
mov ds, savedDS

;==============================================================================
else
;==============================================================================

mov ds, bp
xchg si, dx ; DS: SI = Command Line address
xchg bx, cx ; CX = Command Line size including \r
mov ax, bx
mov dx, ax ; AX = BX = DX = 0
inc bx ; BX = Nb of arguments (at least 1)
Processing label near
call NextChar
ja NotQuote ; Not a quote and there are more
InString label near
jb BuildArgv ; Command line is empty now
call NextChar
ja InString ; Not a quote and there are more
NotQuote label near
cmp al, ' '
je EndArgument ; Space is an argument separator
cmp al, 13
je EndArgument ; \r is an argument separator
cmp al, 9
jne Processing ; \t is an argument separator
EndArgument label near
xor al, al ; Space and TAB are argument separators
jmp short Processing

; Character test function used in SetArgs
; On entry AL holds the previous character
; On exit AL holds the next character
; ZF on if the next character is quote (") and AL = 0
; CF on if end of command line and AL = 0

NextChar PROC NEAR
or ax, ax
jz NextChar0
inc dx ; DX = Actual length of CmdLine
stosb
or al, al
jnz NextChar0
inc bx ; BX = Number of parameters
NextChar0 label near
xchg ah, al
xor al, al
stc
jcxz NextChar2 ; End of command line --> CF ON
lodsb
dec cx
sub al, '"'
jz NextChar2 ; Quote found --> AL = 0 and ZF ON
add al, '"'
cmp al,'\'
jne NextChar1 ; It is not a \
cmp byte ptr ds:[si], '"'
jne NextChar1 ; Only " is transparent after \
lodsb
dec cx
NextChar1 label near
or si, si ; Be sure both CF & ZF are OFF
NextChar2 label near
ret
NextChar ENDP

; Invalid program name

BadProgName label near
jmp abort@

; Now, build the argv array

BuildArgv label near
pop cx
add cx, dx ; CX = Argument area size
mov ds, SavedDS
mov _argc@, bx
inc bx ; argv ends with a NULL pointer
add bx, bx ; argc * 2 (LDATA = 0)
IF LDATA
add bx, bx ; argc * 4 (LDATA = 1)
ENDIF
mov si, sp
mov bp, sp
sub bp, bx
jb BadProgName
mov sp, bp ; SS:BP = argv array address
mov word ptr _argv@, bp
IF LDATA
mov word ptr _argv@+2, ss
ENDIF
SetArgvX label near
jcxz SetLastArg
mov [bp], si ; Set argv[n]
IF LDATA
mov [bp+2], ss
ENDIF
add bp, dPtrSize
CopyArg label near
lods byte ptr ss:[si]
or al, al
loopnz CopyArg
jz SetArgvX
SetLastArg label near
xor ax, ax
mov [bp], ax
IF LDATA
mov [bp+2], ax
ENDIF

;==============================================================================
endif ; ifdef WILD
;==============================================================================

; Restore caller context and exit

IF LPROG
jmp dword ptr SavedReturn
ELSE
jmp word ptr SavedReturn
ENDIF

ifdef WILD
EndProc@ _wildargv, __CDECL__
else
EndProc@ _setargv, __CDECL__
endif

CSegEnd@
END


Learn More :