You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
428 lines
13 KiB
428 lines
13 KiB
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
|
|
|