428 lines
13 KiB
NASM
428 lines
13 KiB
NASM
page ,132
|
||
;/*
|
||
; * Microsoft Confidential
|
||
; * Copyright (C) Microsoft Corporation 1991
|
||
; * All Rights Reserved.
|
||
; */
|
||
; SCCSID = @(#)parse.asm 1.1 85/05/14
|
||
; SCCSID = @(#)parse.asm 1.1 85/05/14
|
||
.sall
|
||
.xlist
|
||
.xcref
|
||
INCLUDE DOSSYM.INC
|
||
INCLUDE DEVSYM.INC
|
||
include comsw.asm
|
||
include comseg.asm
|
||
include comequ.asm
|
||
.list
|
||
.cref
|
||
|
||
|
||
break <Parse.Asm>
|
||
;----------------------------------------------------------------------------
|
||
; PARSE.ASM contains the routines to perform command line parsing.
|
||
; Parse and Path share a buffer and argv[] definitions.
|
||
; Invoking <Parseline> maps the unparsed command line in COMBUF into an
|
||
; array of pointers to the parsed tokens. The resulting array, argv[],
|
||
; also contains extra information provided by cparse about each token
|
||
; <Parseline> should be executed prior to <Path_Search>
|
||
;
|
||
; Alan L, OS/MSDOS August 15, 1983
|
||
;
|
||
;
|
||
; ENTRY:
|
||
; <Parseline>: command line in COMTAB.
|
||
; EXIT:
|
||
; <Parseline>: success flag, argcnt (number of args), argv[].
|
||
; NOTE(S):
|
||
; * <Argv_calc> handily turns an array index into an absolute pointer.
|
||
; The computation depends on the size of an argv[] element (arg_ele).
|
||
; * <Parseline> calls <cparse> for chunks of the command line. <Cparse>
|
||
; does not function as specified; see <Parseline> for more details.
|
||
; * <Parseline> now knows about the flags the internals of COMMAND.COM
|
||
; need to know about. This extra information is stored in a switch_flag
|
||
; word with each command-line argument; the switches themselves will not
|
||
; appear in the resulting arg structure.
|
||
; * With the exception of CARRY, flags are generally preserved across calls.
|
||
;---------------
|
||
; CONSTANTS:
|
||
;---------------
|
||
DEBUGx equ FALSE ; prints out debug info
|
||
;---------------
|
||
; DATA:
|
||
;---------------
|
||
|
||
DATARES SEGMENT PUBLIC BYTE
|
||
EXTRN FORFLAG:BYTE
|
||
DATARES ENDS
|
||
|
||
TRANSPACE SEGMENT PUBLIC BYTE ;AC000;
|
||
EXTRN combuf:byte
|
||
EXTRN cpyflag:byte
|
||
EXTRN expand_star:byte
|
||
EXTRN RESSEG:word
|
||
EXTRN STARTEL:word
|
||
TRANSPACE ENDS
|
||
|
||
TRANCODE SEGMENT PUBLIC BYTE ;AC000;
|
||
PUBLIC argv_calc ; convert array index into address
|
||
PUBLIC parseline
|
||
|
||
|
||
assume cs:trangroup, ds:trangroup, es:trangroup, ss:nothing
|
||
|
||
|
||
break <Parseline: Munch on the command line>
|
||
;----------------------------------------------------------------------------
|
||
; PARSELINE takes an MSDOS command line and maps it into a UNIX-style
|
||
; argv[argvcnt] array. The most important difference between this array and
|
||
; the tradition UNIX format is the extra cparse information included with
|
||
; each argument element.
|
||
;---------------
|
||
; ENTRY:
|
||
; (BL special delimiter for cparse -- not implemented)
|
||
;---------------
|
||
; EXIT:
|
||
; CF set if error
|
||
; AL error code (carry set). Note AH clobbered in any event.
|
||
; argv[] array of cparse flags and pointers to arguments
|
||
; argvcnt argument count
|
||
;---------------
|
||
; NOTE(S):
|
||
; * BL (special delimiter) is ignored, for now (set to space).
|
||
; * Parseflags record contains cparse flags, as follows:
|
||
; sw_flag -- was this arg a switch?
|
||
; wildcard -- whether or not it contained a * or ?
|
||
; path_sep -- maybe it was a pathname
|
||
; unused -- for future expansion
|
||
; special_delim -- was there an initial special delimiter?
|
||
; * argv[] and argvcnt are undefined if CF/AL indicates an error.
|
||
; * Relationship between input, cparse output, and comtail can be
|
||
; found in the following chart. Despite the claim of the cparse
|
||
; documentation that, "Token buffer always starts d: for non switch
|
||
; tokens", such is not the case (see column two, row two).
|
||
; Similarly, [STARTEL] is not null when the command line is one of
|
||
; the forms, "d:", "d:\", or "d:/". In fact, *STARTEL (i.e., what
|
||
; STARTEL addresses) will be null. This is clearly just a
|
||
; documentation error.
|
||
; * cparse also returns a switch code in BP for each switch it
|
||
; recognizes on the command line.
|
||
; * arglen for each token does NOT include the terminating null.
|
||
; * Finally, note that interesting constructions like 'foodir/*.exe'
|
||
; parse as three separate tokens, and the asterisk is NOT a wildcard.
|
||
; For example, 'for %i in (foodir/*.exe) do echo %i' will first
|
||
; echo 'foodir', then '*', then '.exe'. Using cparse for command-
|
||
; line parsing may result in slightly different behavior than
|
||
; previously observed with the old COMMAND.COM command-line parser.
|
||
;
|
||
; Input Cparse Command Line (80H)
|
||
; \alan\foo.bat c:\alan\foo.bat \alan\foo.bat
|
||
; alan\foo.bat alan\foo.bat alan\foo.bat
|
||
; foo.bat foo.bat foo.bat
|
||
; c:\alan\foo.bat c:\alan\foo.bat c:\alan\foo.bat
|
||
; c:alan\foo.bat c:alan\foo.bat c:alan\foo.bat
|
||
; c:foo.bat c:foo.bat c:foo.bat
|
||
;---------------
|
||
; CONSTANTS:
|
||
;---------------
|
||
;---------------
|
||
; DATA:
|
||
;---------------
|
||
|
||
TRANSPACE SEGMENT PUBLIC BYTE ;AC000;
|
||
EXTRN arg:byte
|
||
EXTRN argbufptr:word
|
||
EXTRN comptr:word
|
||
EXTRN last_arg:word
|
||
EXTRN tpbuf:byte
|
||
TRANSPACE ENDS
|
||
|
||
;---------------
|
||
parseline:
|
||
;---------------
|
||
|
||
push AX ; most of these are clobbered
|
||
push BX ; by cparse...
|
||
push CX
|
||
push DX
|
||
push DI
|
||
push SI
|
||
pushf
|
||
mov cpyflag,0 ; Turn "CPARSE called from COPY flag" off
|
||
|
||
mov [LAST_ARG], -1 ; last argument at which to accumulate
|
||
xor ax,ax
|
||
mov cx,SIZE arg_unit
|
||
mov di,offset trangroup:arg
|
||
rep stosb
|
||
mov argbufptr,offset trangroup:arg.argbuf
|
||
mov arg.argswinfo, 0 ; switch information, and info to date
|
||
mov arg.argvcnt, 0 ; initialize argvcnt/argv[]
|
||
mov SI, OFFSET TRANGROUP:combuf+2 ; prescan leaves cooked input in combuf
|
||
|
||
; This next section of code (up to pcont:) makes sure that si is set up for
|
||
; parsing. It should point at COMBUF if FORFLAG is set and arg.argforcombuf
|
||
; otherwise. This is done so that commands can get arg pointers into their
|
||
; original command line (or an exact copy of it) in arg_ocomptr.
|
||
; Arg.argforcombuf is used so that the for loop processor will always be able
|
||
; to get a hold of its original command line; even after COMBUF is blasted by
|
||
; the command to be repeated or the transient part of command has been
|
||
; reloaded.
|
||
|
||
push ds
|
||
mov ds,[RESSEG]
|
||
assume ds:resgroup
|
||
cmp FORFLAG,0
|
||
pop ds
|
||
assume ds:trangroup
|
||
jnz pcont
|
||
mov di,OFFSET TRANGROUP:arg.argforcombuf
|
||
xor ch,ch
|
||
mov cl,[COMBUF+1]
|
||
inc cl
|
||
rep movsb
|
||
mov si,OFFSET TRANGROUP:arg.argforcombuf
|
||
|
||
pcont:
|
||
mov DI, OFFSET TRANGROUP:tpbuf ; destination is temporary token buffer
|
||
mov BL, ' ' ; no special delimiter, for now
|
||
|
||
parseloop:
|
||
mov comptr,si ; save ptr into original command buffer
|
||
xor BP, BP ; switch information put here by cparse
|
||
mov byte ptr [expand_star],0 ; don't expand *'s to ?'s
|
||
invoke scanoff ; skip leading blanks...
|
||
invoke cparse ; byte off a token (args in SI, DI, BL)
|
||
jnc More_prse
|
||
or BP,BP ; Check for trailing switch character
|
||
jz parsedone
|
||
call newarg ; We hit CR but BP is non-zero. The
|
||
; typical cause of this is that a
|
||
; switch char IMMEDIATELY preceeds
|
||
; the CR. We have an argument, but it
|
||
; is sort of an error.
|
||
jmp short parsedone ; We're done (found the CR).
|
||
|
||
More_prse:
|
||
mov cpyflag,2 ; tell CPARSE that 1st token is done
|
||
call newarg ; add to argv array (CX has char count)
|
||
jnc parseloop ; was everything OK?
|
||
jmp short parse_error ; NO, it wasn't -- bug out (CF set)
|
||
|
||
parsedone: ; successful completion of parseline
|
||
popf
|
||
clc
|
||
jmp short parse_exit
|
||
|
||
parse_error: ; error entry (er, exit) point
|
||
popf
|
||
stc
|
||
parse_exit: ; depend on not changing CF
|
||
pop SI
|
||
pop DI
|
||
pop DX
|
||
pop CX
|
||
pop BX
|
||
pop AX
|
||
ret
|
||
|
||
;---------------
|
||
; parseline ends
|
||
;----------------------------------------------------------------------------
|
||
|
||
|
||
break <NewArg>
|
||
;----------------------------------------------------------------------------
|
||
; NEWARG adds the supplied argstring and cparse data to arg.argv[].
|
||
; ENTRY:
|
||
; BH argflags
|
||
; CX character count in argstring
|
||
; DI pointer to argstring
|
||
; comptr ptr to starting loc of current token in original command
|
||
; [STARTEL] cparse's answer to where the last element starts
|
||
; EXIT:
|
||
; argbufptr points to next free section of argbuffer
|
||
; arg.argbuf contains null-terminated argument strings
|
||
; arg.argvcnt argument count
|
||
; arg.argv[] array of flags and pointers
|
||
; arg.arg_ocomptr ptr to starting loc of current token in original command
|
||
; CF set if error
|
||
; AL carry set: error code; otherwise, zero
|
||
;---------------
|
||
newarg:
|
||
;---------------
|
||
|
||
push BX
|
||
push CX
|
||
push DX ; one never knows, do one?
|
||
push DI
|
||
push SI
|
||
pushf
|
||
call arg_switch ; if it's a switch, record switch info
|
||
; LEAVE SWITCH ON COMMAND LINE!!
|
||
;;; jc newarg_done ; previous arg's switches -- and leave
|
||
|
||
cmp arg.argvcnt, ARGMAX ; check to ensure we've not
|
||
jge too_many_args ; exceeded array limits
|
||
mov DH, BH ; save argflags
|
||
mov BX, arg.argvcnt ; argv[argvcnt++] = arg data
|
||
inc arg.argvcnt
|
||
mov AX, OFFSET TRANGROUP:arg.argv
|
||
call argv_calc ; convert offset to pointer
|
||
mov [BX].argsw_word, 0 ; no switch information, yet...
|
||
mov [BX].arglen, CX ; argv[argvcnt].arglen = arg length
|
||
mov [BX].argflags, DH ; argv[argvcnt].argflags = cparse flags
|
||
mov SI, argbufptr
|
||
mov [BX].argpointer, SI ; argv[argvcnt].argpointer = [argbufptr]
|
||
add SI, [STARTEL] ; save startel from new location
|
||
sub SI, DI ; form pointer into argbuf
|
||
mov [BX].argstartel, SI ; argv[argvcnt].argstartel = new [STARTEL]
|
||
mov si,[comptr]
|
||
mov [BX].arg_ocomptr,si ; arg_ocomptr=ptr into original com line
|
||
|
||
mov SI, DI ; now save argstring in argbuffer
|
||
mov DI, argbufptr ; load the argbuf pointer and make
|
||
add DI, CX ; sure we're not about to run off
|
||
cmp DI, OFFSET TRANGROUP:arg.argbuf+ARGBLEN-1
|
||
jge buf_ovflow ; the end of the buffer (plus null byte)
|
||
sub DI, CX ; adjust the pointer
|
||
cld
|
||
rep movsb ; and save the string in argbuffer
|
||
mov AL, ANULL ; tack a null byte on the end
|
||
stosb
|
||
mov argbufptr, DI ; update argbufptr after copy
|
||
|
||
newarg_done:
|
||
popf
|
||
clc
|
||
jmp short newarg_exit
|
||
|
||
too_many_args:
|
||
mov AX, arg_cnt_error
|
||
jmp short newarg_error
|
||
|
||
buf_ovflow:
|
||
mov AX, arg_buf_ovflow
|
||
|
||
newarg_error:
|
||
popf
|
||
stc
|
||
|
||
newarg_exit:
|
||
pop SI
|
||
pop DI
|
||
pop DX
|
||
pop CX
|
||
pop BX
|
||
ret
|
||
|
||
;---------------
|
||
; NewArg ends
|
||
;----------------------------------------------------------------------------
|
||
|
||
|
||
break <Arg_Switch>
|
||
;----------------------------------------------------------------------------
|
||
; ARG_SWITCH decides if an argument might really be a switch. In the
|
||
; event that it is, and we can recognize
|
||
; ENTRY:
|
||
; As in <newarg>.
|
||
; EXIT:
|
||
; CF -- clear (wasn't a switch); set (was a switch)
|
||
; NOTE(S):
|
||
; * The mechanism mapping a switch into a bit-value depends entirely
|
||
; on the order of definition in the <switch_list> variable and the
|
||
; values chosen to define the bits in CMDT:COMEQU.ASM. Change either
|
||
; <switch_list> or the definitions in CMDT:COMEQU.ASM -- and rewrite
|
||
; this mechanism. This code taken from CMDT:TCODE.ASM.
|
||
; * The <switch_list> declared below is redundant to one declared in
|
||
; TDATA.ASM, and used in TCODE.ASM.
|
||
; * An ugly routine.
|
||
;---------------
|
||
; CONSTANTS:
|
||
;---------------
|
||
; Constants come from the definitions in CMDT:COMEQU.ASM.
|
||
;---------------
|
||
; DATA:
|
||
;---------------
|
||
|
||
TRANSPACE SEGMENT PUBLIC BYTE ;AC000;
|
||
extrn switch_list:byte
|
||
switch_count EQU $-switch_list
|
||
transpace ends
|
||
|
||
;---------------
|
||
Arg_Switch:
|
||
;---------------
|
||
|
||
push AX
|
||
push BX
|
||
push CX
|
||
push DI
|
||
pushf
|
||
test BH, MASK sw_flag ; is it a switch? (preserve flag word)
|
||
jz arg_no_switch0
|
||
cmp [LAST_ARG], -1 ; have we encountered any REAL args yet?
|
||
je arg_no_switch1 ; no, so leading switches don't matter
|
||
mov BX, [LAST_ARG] ; yes, add switch info to last REAL arg
|
||
mov AX, OFFSET TRANGROUP:arg.argv
|
||
call argv_calc
|
||
or [BX].argsw_word, BP
|
||
or arg.argswinfo, BP
|
||
|
||
arg_yes_switch: ; ah, sweet success...
|
||
popf
|
||
stc
|
||
jmp short arg_switch_exit
|
||
|
||
arg_no_switch0:
|
||
mov AX, arg.argvcnt ; future switches should then affect
|
||
mov [LAST_ARG], AX ; this argument
|
||
|
||
arg_no_switch1: ; wasn't a switch, or we're pretending
|
||
popf
|
||
clc
|
||
|
||
arg_switch_exit:
|
||
pop DI
|
||
pop CX
|
||
pop BX
|
||
pop AX
|
||
ret
|
||
|
||
;---------------
|
||
; Arg_Switch ends
|
||
;----------------------------------------------------------------------------
|
||
|
||
|
||
break <Argv_calc>
|
||
;----------------------------------------------------------------------------
|
||
; ARGV_CALC maps an array index into a byte-offset from the base of
|
||
; the supplied array. Method used for computing the address is:
|
||
; Array Index * Array Elt Size + Base Addr = Elt Addr
|
||
; ENTRY:
|
||
; AX -- base of array
|
||
; BX -- array index
|
||
; EXIT:
|
||
; BX -- byte offset
|
||
;---------------
|
||
|
||
argv_calc:
|
||
push ax ; Save base
|
||
mov al,bl ; al = array index
|
||
mov bl,SIZE argv_ele ; bl = size of an argv element
|
||
mul bl ; ax = base offset
|
||
pop bx ; Get base
|
||
add ax,bx ; Add in base offset
|
||
xchg ax,bx ; Restore ax and put byte offset in bx
|
||
ret
|
||
|
||
;---------------
|
||
; argv_calc ends
|
||
;----------------------------------------------------------------------------
|
||
|
||
|
||
|
||
trancode ends
|
||
end
|
||
|