diff options
| author | 2024-04-25 21:24:10 +0100 | |
|---|---|---|
| committer | 2024-04-25 22:32:27 +0000 | |
| commit | 2d04cacc5322951f187bb17e017c12920ac8ebe2 (patch) | |
| tree | 80ee017efa878dfd5344b44249e6a241f2a7f6e2 /v4.0/src/CMD/COMMAND/PARSE2.ASM | |
| parent | Merge pull request #430 from jpbaltazar/typoptbr (diff) | |
| download | ms-dos-main.tar.gz ms-dos-main.tar.xz ms-dos-main.zip | |
Diffstat (limited to 'v4.0/src/CMD/COMMAND/PARSE2.ASM')
| -rw-r--r-- | v4.0/src/CMD/COMMAND/PARSE2.ASM | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/v4.0/src/CMD/COMMAND/PARSE2.ASM b/v4.0/src/CMD/COMMAND/PARSE2.ASM new file mode 100644 index 0000000..2695927 --- /dev/null +++ b/v4.0/src/CMD/COMMAND/PARSE2.ASM | |||
| @@ -0,0 +1,422 @@ | |||
| 1 | page 80,132 | ||
| 2 | ; SCCSID = @(#)parse.asm 1.1 85/05/14 | ||
| 3 | ; SCCSID = @(#)parse.asm 1.1 85/05/14 | ||
| 4 | .sall | ||
| 5 | .xlist | ||
| 6 | .xcref | ||
| 7 | INCLUDE DOSSYM.INC | ||
| 8 | INCLUDE DEVSYM.INC | ||
| 9 | include comsw.asm | ||
| 10 | include comseg.asm | ||
| 11 | include comequ.asm | ||
| 12 | .list | ||
| 13 | .cref | ||
| 14 | |||
| 15 | |||
| 16 | break <Parse.Asm> | ||
| 17 | ;---------------------------------------------------------------------------- | ||
| 18 | ; PARSE.ASM contains the routines to perform command line parsing. | ||
| 19 | ; Parse and Path share a buffer and argv[] definitions. | ||
| 20 | ; Invoking <Parseline> maps the unparsed command line in COMBUF into an | ||
| 21 | ; array of pointers to the parsed tokens. The resulting array, argv[], | ||
| 22 | ; also contains extra information provided by cparse about each token | ||
| 23 | ; <Parseline> should be executed prior to <Path_Search> | ||
| 24 | ; | ||
| 25 | ; Alan L, OS/MSDOS August 15, 1983 | ||
| 26 | ; | ||
| 27 | ; | ||
| 28 | ; ENTRY: | ||
| 29 | ; <Parseline>: command line in COMTAB. | ||
| 30 | ; EXIT: | ||
| 31 | ; <Parseline>: success flag, argcnt (number of args), argv[]. | ||
| 32 | ; NOTE(S): | ||
| 33 | ; * <Argv_calc> handily turns an array index into an absolute pointer. | ||
| 34 | ; The computation depends on the size of an argv[] element (arg_ele). | ||
| 35 | ; * <Parseline> calls <cparse> for chunks of the command line. <Cparse> | ||
| 36 | ; does not function as specified; see <Parseline> for more details. | ||
| 37 | ; * <Parseline> now knows about the flags the internals of COMMAND.COM | ||
| 38 | ; need to know about. This extra information is stored in a switch_flag | ||
| 39 | ; word with each command-line argument; the switches themselves will not | ||
| 40 | ; appear in the resulting arg structure. | ||
| 41 | ; * With the exception of CARRY, flags are generally preserved across calls. | ||
| 42 | ;--------------- | ||
| 43 | ; CONSTANTS: | ||
| 44 | ;--------------- | ||
| 45 | DEBUGx equ FALSE ; prints out debug info | ||
| 46 | ;--------------- | ||
| 47 | ; DATA: | ||
| 48 | ;--------------- | ||
| 49 | |||
| 50 | DATARES SEGMENT PUBLIC BYTE | ||
| 51 | EXTRN FORFLAG:BYTE | ||
| 52 | DATARES ENDS | ||
| 53 | |||
| 54 | TRANSPACE SEGMENT PUBLIC BYTE ;AC000; | ||
| 55 | EXTRN combuf:byte | ||
| 56 | EXTRN cpyflag:byte | ||
| 57 | EXTRN expand_star:byte | ||
| 58 | EXTRN RESSEG:word | ||
| 59 | EXTRN STARTEL:word | ||
| 60 | TRANSPACE ENDS | ||
| 61 | |||
| 62 | TRANCODE SEGMENT PUBLIC BYTE ;AC000; | ||
| 63 | PUBLIC argv_calc ; convert array index into address | ||
| 64 | PUBLIC parseline | ||
| 65 | |||
| 66 | |||
| 67 | assume cs:trangroup, ds:trangroup, es:trangroup, ss:nothing | ||
| 68 | |||
| 69 | |||
| 70 | break <Parseline: Munch on the command line> | ||
| 71 | ;---------------------------------------------------------------------------- | ||
| 72 | ; PARSELINE takes an MSDOS command line and maps it into a UNIX-style | ||
| 73 | ; argv[argvcnt] array. The most important difference between this array and | ||
| 74 | ; the tradition UNIX format is the extra cparse information included with | ||
| 75 | ; each argument element. | ||
| 76 | ;--------------- | ||
| 77 | ; ENTRY: | ||
| 78 | ; (BL special delimiter for cparse -- not implemented) | ||
| 79 | ;--------------- | ||
| 80 | ; EXIT: | ||
| 81 | ; CF set if error | ||
| 82 | ; AL error code (carry set). Note AH clobbered in any event. | ||
| 83 | ; argv[] array of cparse flags and pointers to arguments | ||
| 84 | ; argvcnt argument count | ||
| 85 | ;--------------- | ||
| 86 | ; NOTE(S): | ||
| 87 | ; * BL (special delimiter) is ignored, for now (set to space). | ||
| 88 | ; * Parseflags record contains cparse flags, as follows: | ||
| 89 | ; sw_flag -- was this arg a switch? | ||
| 90 | ; wildcard -- whether or not it contained a * or ? | ||
| 91 | ; path_sep -- maybe it was a pathname | ||
| 92 | ; unused -- for future expansion | ||
| 93 | ; special_delim -- was there an initial special delimiter? | ||
| 94 | ; * argv[] and argvcnt are undefined if CF/AL indicates an error. | ||
| 95 | ; * Relationship between input, cparse output, and comtail can be | ||
| 96 | ; found in the following chart. Despite the claim of the cparse | ||
| 97 | ; documentation that, "Token buffer always starts d: for non switch | ||
| 98 | ; tokens", such is not the case (see column two, row two). | ||
| 99 | ; Similarly, [STARTEL] is not null when the command line is one of | ||
| 100 | ; the forms, "d:", "d:\", or "d:/". In fact, *STARTEL (i.e., what | ||
| 101 | ; STARTEL addresses) will be null. This is clearly just a | ||
| 102 | ; documentation error. | ||
| 103 | ; * cparse also returns a switch code in BP for each switch it | ||
| 104 | ; recognizes on the command line. | ||
| 105 | ; * arglen for each token does NOT include the terminating null. | ||
| 106 | ; * Finally, note that interesting constructions like 'foodir/*.exe' | ||
| 107 | ; parse as three separate tokens, and the asterisk is NOT a wildcard. | ||
| 108 | ; For example, 'for %i in (foodir/*.exe) do echo %i' will first | ||
| 109 | ; echo 'foodir', then '*', then '.exe'. Using cparse for command- | ||
| 110 | ; line parsing may result in slightly different behavior than | ||
| 111 | ; previously observed with the old COMMAND.COM command-line parser. | ||
| 112 | ; | ||
| 113 | ; Input Cparse Command Line (80H) | ||
| 114 | ; \alan\foo.bat c:\alan\foo.bat \alan\foo.bat | ||
| 115 | ; alan\foo.bat alan\foo.bat alan\foo.bat | ||
| 116 | ; foo.bat foo.bat foo.bat | ||
| 117 | ; c:\alan\foo.bat c:\alan\foo.bat c:\alan\foo.bat | ||
| 118 | ; c:alan\foo.bat c:alan\foo.bat c:alan\foo.bat | ||
| 119 | ; c:foo.bat c:foo.bat c:foo.bat | ||
| 120 | ;--------------- | ||
| 121 | ; CONSTANTS: | ||
| 122 | ;--------------- | ||
| 123 | ;--------------- | ||
| 124 | ; DATA: | ||
| 125 | ;--------------- | ||
| 126 | |||
| 127 | TRANSPACE SEGMENT PUBLIC BYTE ;AC000; | ||
| 128 | EXTRN arg:byte | ||
| 129 | EXTRN argbufptr:word | ||
| 130 | EXTRN comptr:word | ||
| 131 | EXTRN last_arg:word | ||
| 132 | EXTRN tpbuf:byte | ||
| 133 | TRANSPACE ENDS | ||
| 134 | |||
| 135 | ;--------------- | ||
| 136 | parseline: | ||
| 137 | ;--------------- | ||
| 138 | |||
| 139 | push AX ; most of these are clobbered | ||
| 140 | push BX ; by cparse... | ||
| 141 | push CX | ||
| 142 | push DX | ||
| 143 | push DI | ||
| 144 | push SI | ||
| 145 | pushf | ||
| 146 | mov cpyflag,0 ; Turn "CPARSE called from COPY flag" off | ||
| 147 | |||
| 148 | mov [LAST_ARG], -1 ; last argument at which to accumulate | ||
| 149 | xor ax,ax | ||
| 150 | mov cx,SIZE arg_unit | ||
| 151 | mov di,offset trangroup:arg | ||
| 152 | rep stosb | ||
| 153 | mov argbufptr,offset trangroup:arg.argbuf | ||
| 154 | mov arg.argswinfo, 0 ; switch information, and info to date | ||
| 155 | mov arg.argvcnt, 0 ; initialize argvcnt/argv[] | ||
| 156 | mov SI, OFFSET TRANGROUP:combuf+2 ; prescan leaves cooked input in combuf | ||
| 157 | |||
| 158 | ; This next section of code (up to pcont:) makes sure that si is set up for | ||
| 159 | ; parsing. It should point at COMBUF if FORFLAG is set and arg.argforcombuf | ||
| 160 | ; otherwise. This is done so that commands can get arg pointers into their | ||
| 161 | ; original command line (or an exact copy of it) in arg_ocomptr. | ||
| 162 | ; Arg.argforcombuf is used so that the for loop processor will always be able | ||
| 163 | ; to get a hold of its original command line; even after COMBUF is blasted by | ||
| 164 | ; the command to be repeated or the transient part of command has been | ||
| 165 | ; reloaded. | ||
| 166 | |||
| 167 | push ds | ||
| 168 | mov ds,[RESSEG] | ||
| 169 | assume ds:resgroup | ||
| 170 | cmp FORFLAG,0 | ||
| 171 | pop ds | ||
| 172 | assume ds:trangroup | ||
| 173 | jnz pcont | ||
| 174 | mov di,OFFSET TRANGROUP:arg.argforcombuf | ||
| 175 | xor ch,ch | ||
| 176 | mov cl,[COMBUF+1] | ||
| 177 | inc cl | ||
| 178 | rep movsb | ||
| 179 | mov si,OFFSET TRANGROUP:arg.argforcombuf | ||
| 180 | |||
| 181 | pcont: | ||
| 182 | mov DI, OFFSET TRANGROUP:tpbuf ; destination is temporary token buffer | ||
| 183 | mov BL, ' ' ; no special delimiter, for now | ||
| 184 | |||
| 185 | parseloop: | ||
| 186 | mov comptr,si ; save ptr into original command buffer | ||
| 187 | xor BP, BP ; switch information put here by cparse | ||
| 188 | mov byte ptr [expand_star],0 ; don't expand *'s to ?'s | ||
| 189 | invoke scanoff ; skip leading blanks... | ||
| 190 | invoke cparse ; byte off a token (args in SI, DI, BL) | ||
| 191 | jnc More_prse | ||
| 192 | or BP,BP ; Check for trailing switch character | ||
| 193 | jz parsedone | ||
| 194 | call newarg ; We hit CR but BP is non-zero. The | ||
| 195 | ; typical cause of this is that a | ||
| 196 | ; switch char IMMEDIATELY preceeds | ||
| 197 | ; the CR. We have an argument, but it | ||
| 198 | ; is sort of an error. | ||
| 199 | jmp short parsedone ; We're done (found the CR). | ||
| 200 | |||
| 201 | More_prse: | ||
| 202 | mov cpyflag,2 ; tell CPARSE that 1st token is done | ||
| 203 | call newarg ; add to argv array (CX has char count) | ||
| 204 | jnc parseloop ; was everything OK? | ||
| 205 | jmp short parse_error ; NO, it wasn't -- bug out (CF set) | ||
| 206 | |||
| 207 | parsedone: ; successful completion of parseline | ||
| 208 | popf | ||
| 209 | clc | ||
| 210 | jmp short parse_exit | ||
| 211 | |||
| 212 | parse_error: ; error entry (er, exit) point | ||
| 213 | popf | ||
| 214 | stc | ||
| 215 | parse_exit: ; depend on not changing CF | ||
| 216 | pop SI | ||
| 217 | pop DI | ||
| 218 | pop DX | ||
| 219 | pop CX | ||
| 220 | pop BX | ||
| 221 | pop AX | ||
| 222 | ret | ||
| 223 | |||
| 224 | ;--------------- | ||
| 225 | ; parseline ends | ||
| 226 | ;---------------------------------------------------------------------------- | ||
| 227 | |||
| 228 | |||
| 229 | break <NewArg> | ||
| 230 | ;---------------------------------------------------------------------------- | ||
| 231 | ; NEWARG adds the supplied argstring and cparse data to arg.argv[]. | ||
| 232 | ; ENTRY: | ||
| 233 | ; BH argflags | ||
| 234 | ; CX character count in argstring | ||
| 235 | ; DI pointer to argstring | ||
| 236 | ; comptr ptr to starting loc of current token in original command | ||
| 237 | ; [STARTEL] cparse's answer to where the last element starts | ||
| 238 | ; EXIT: | ||
| 239 | ; argbufptr points to next free section of argbuffer | ||
| 240 | ; arg.argbuf contains null-terminated argument strings | ||
| 241 | ; arg.argvcnt argument count | ||
| 242 | ; arg.argv[] array of flags and pointers | ||
| 243 | ; arg.arg_ocomptr ptr to starting loc of current token in original command | ||
| 244 | ; CF set if error | ||
| 245 | ; AL carry set: error code; otherwise, zero | ||
| 246 | ;--------------- | ||
| 247 | newarg: | ||
| 248 | ;--------------- | ||
| 249 | |||
| 250 | push BX | ||
| 251 | push CX | ||
| 252 | push DX ; one never knows, do one? | ||
| 253 | push DI | ||
| 254 | push SI | ||
| 255 | pushf | ||
| 256 | call arg_switch ; if it's a switch, record switch info | ||
| 257 | ; LEAVE SWITCH ON COMMAND LINE!! | ||
| 258 | ;;; jc newarg_done ; previous arg's switches -- and leave | ||
| 259 | |||
| 260 | cmp arg.argvcnt, ARGMAX ; check to ensure we've not | ||
| 261 | jge too_many_args ; exceeded array limits | ||
| 262 | mov DH, BH ; save argflags | ||
| 263 | mov BX, arg.argvcnt ; argv[argvcnt++] = arg data | ||
| 264 | inc arg.argvcnt | ||
| 265 | mov AX, OFFSET TRANGROUP:arg.argv | ||
| 266 | call argv_calc ; convert offset to pointer | ||
| 267 | mov [BX].argsw_word, 0 ; no switch information, yet... | ||
| 268 | mov [BX].arglen, CX ; argv[argvcnt].arglen = arg length | ||
| 269 | mov [BX].argflags, DH ; argv[argvcnt].argflags = cparse flags | ||
| 270 | mov SI, argbufptr | ||
| 271 | mov [BX].argpointer, SI ; argv[argvcnt].argpointer = [argbufptr] | ||
| 272 | add SI, [STARTEL] ; save startel from new location | ||
| 273 | sub SI, DI ; form pointer into argbuf | ||
| 274 | mov [BX].argstartel, SI ; argv[argvcnt].argstartel = new [STARTEL] | ||
| 275 | mov si,[comptr] | ||
| 276 | mov [BX].arg_ocomptr,si ; arg_ocomptr=ptr into original com line | ||
| 277 | |||
| 278 | mov SI, DI ; now save argstring in argbuffer | ||
| 279 | mov DI, argbufptr ; load the argbuf pointer and make | ||
| 280 | add DI, CX ; sure we're not about to run off | ||
| 281 | cmp DI, OFFSET TRANGROUP:arg.argbuf+ARGBLEN-1 | ||
| 282 | jge buf_ovflow ; the end of the buffer (plus null byte) | ||
| 283 | sub DI, CX ; adjust the pointer | ||
| 284 | cld | ||
| 285 | rep movsb ; and save the string in argbuffer | ||
| 286 | mov AL, ANULL ; tack a null byte on the end | ||
| 287 | stosb | ||
| 288 | mov argbufptr, DI ; update argbufptr after copy | ||
| 289 | |||
| 290 | newarg_done: | ||
| 291 | popf | ||
| 292 | clc | ||
| 293 | jmp short newarg_exit | ||
| 294 | |||
| 295 | too_many_args: | ||
| 296 | mov AX, arg_cnt_error | ||
| 297 | jmp short newarg_error | ||
| 298 | |||
| 299 | buf_ovflow: | ||
| 300 | mov AX, arg_buf_ovflow | ||
| 301 | |||
| 302 | newarg_error: | ||
| 303 | popf | ||
| 304 | stc | ||
| 305 | |||
| 306 | newarg_exit: | ||
| 307 | pop SI | ||
| 308 | pop DI | ||
| 309 | pop DX | ||
| 310 | pop CX | ||
| 311 | pop BX | ||
| 312 | ret | ||
| 313 | |||
| 314 | ;--------------- | ||
| 315 | ; NewArg ends | ||
| 316 | ;---------------------------------------------------------------------------- | ||
| 317 | |||
| 318 | |||
| 319 | break <Arg_Switch> | ||
| 320 | ;---------------------------------------------------------------------------- | ||
| 321 | ; ARG_SWITCH decides if an argument might really be a switch. In the | ||
| 322 | ; event that it is, and we can recognize | ||
| 323 | ; ENTRY: | ||
| 324 | ; As in <newarg>. | ||
| 325 | ; EXIT: | ||
| 326 | ; CF -- clear (wasn't a switch); set (was a switch) | ||
| 327 | ; NOTE(S): | ||
| 328 | ; * The mechanism mapping a switch into a bit-value depends entirely | ||
| 329 | ; on the order of definition in the <switch_list> variable and the | ||
| 330 | ; values chosen to define the bits in CMDT:COMEQU.ASM. Change either | ||
| 331 | ; <switch_list> or the definitions in CMDT:COMEQU.ASM -- and rewrite | ||
| 332 | ; this mechanism. This code taken from CMDT:TCODE.ASM. | ||
| 333 | ; * The <switch_list> declared below is redundant to one declared in | ||
| 334 | ; TDATA.ASM, and used in TCODE.ASM. | ||
| 335 | ; * An ugly routine. | ||
| 336 | ;--------------- | ||
| 337 | ; CONSTANTS: | ||
| 338 | ;--------------- | ||
| 339 | ; Constants come from the definitions in CMDT:COMEQU.ASM. | ||
| 340 | ;--------------- | ||
| 341 | ; DATA: | ||
| 342 | ;--------------- | ||
| 343 | |||
| 344 | TRANSPACE SEGMENT PUBLIC BYTE ;AC000; | ||
| 345 | extrn switch_list:byte | ||
| 346 | switch_count EQU $-switch_list | ||
| 347 | transpace ends | ||
| 348 | |||
| 349 | ;--------------- | ||
| 350 | Arg_Switch: | ||
| 351 | ;--------------- | ||
| 352 | |||
| 353 | push AX | ||
| 354 | push BX | ||
| 355 | push CX | ||
| 356 | push DI | ||
| 357 | pushf | ||
| 358 | test BH, MASK sw_flag ; is it a switch? (preserve flag word) | ||
| 359 | jz arg_no_switch0 | ||
| 360 | cmp [LAST_ARG], -1 ; have we encountered any REAL args yet? | ||
| 361 | je arg_no_switch1 ; no, so leading switches don't matter | ||
| 362 | mov BX, [LAST_ARG] ; yes, add switch info to last REAL arg | ||
| 363 | mov AX, OFFSET TRANGROUP:arg.argv | ||
| 364 | call argv_calc | ||
| 365 | or [BX].argsw_word, BP | ||
| 366 | or arg.argswinfo, BP | ||
| 367 | |||
| 368 | arg_yes_switch: ; ah, sweet success... | ||
| 369 | popf | ||
| 370 | stc | ||
| 371 | jmp short arg_switch_exit | ||
| 372 | |||
| 373 | arg_no_switch0: | ||
| 374 | mov AX, arg.argvcnt ; future switches should then affect | ||
| 375 | mov [LAST_ARG], AX ; this argument | ||
| 376 | |||
| 377 | arg_no_switch1: ; wasn't a switch, or we're pretending | ||
| 378 | popf | ||
| 379 | clc | ||
| 380 | |||
| 381 | arg_switch_exit: | ||
| 382 | pop DI | ||
| 383 | pop CX | ||
| 384 | pop BX | ||
| 385 | pop AX | ||
| 386 | ret | ||
| 387 | |||
| 388 | ;--------------- | ||
| 389 | ; Arg_Switch ends | ||
| 390 | ;---------------------------------------------------------------------------- | ||
| 391 | |||
| 392 | |||
| 393 | break <Argv_calc> | ||
| 394 | ;---------------------------------------------------------------------------- | ||
| 395 | ; ARGV_CALC maps an array index into a byte-offset from the base of | ||
| 396 | ; the supplied array. Method used for computing the address is: | ||
| 397 | ; Array Index * Array Elt Size + Base Addr = Elt Addr | ||
| 398 | ; ENTRY: | ||
| 399 | ; AX -- base of array | ||
| 400 | ; BX -- array index | ||
| 401 | ; EXIT: | ||
| 402 | ; BX -- byte offset | ||
| 403 | ;--------------- | ||
| 404 | |||
| 405 | argv_calc: | ||
| 406 | push ax ; Save base | ||
| 407 | mov al,bl ; al = array index | ||
| 408 | mov bl,SIZE argv_ele ; bl = size of an argv element | ||
| 409 | mul bl ; ax = base offset | ||
| 410 | pop bx ; Get base | ||
| 411 | add ax,bx ; Add in base offset | ||
| 412 | xchg ax,bx ; Restore ax and put byte offset in bx | ||
| 413 | ret | ||
| 414 | |||
| 415 | ;--------------- | ||
| 416 | ; argv_calc ends | ||
| 417 | ;---------------------------------------------------------------------------- | ||
| 418 | |||
| 419 | |||
| 420 | |||
| 421 | trancode ends | ||
| 422 | end | ||