; FILENAME: IPARAM.ASM ; ; Copyright (c) 1988, 1992 by Borland International, Inc. ; ; DESCRIPTION: This module implements two routines that manage the ; command line parameters passed to the program. The routine ParamCount ; returns the number of parameters passed to the program while the ; routine ParamString returns a pointer to the referenced parameter ; string. This module uses ideal mode syntax. ; ; ASSEMBLY INSTRUCTIONS: To assemble this module use the following ; TASM command line. ; ; TASM /m /dMDL=memorymodel iparam ; ; /m in the above command line allows TASM to resolve jumps and other ; operations to their shortest form and get rid of extra NOPs. ; 'memorymodel' in the above command line may be replaced by TINY, SMALL, ; MEDIUM, COMPACT, LARGE or HUGE. If assembling this module to run on ; a 286/386 machine, turn on the P286 directive in order to take advantage of ; 286/386 specific instructions. I.E. ; ; TASM /m /dMDL=memorymodel /jP286 iparam ; ; NOTE: This module requires that the main program declare and initialize ; the global variable PspAddress. %tabsize 4 ifndef MDL display "Error: This module requires that you provide a memory model" display " definition on the command line. I.E. /dMDL=SMALL." err ; Force a fatal error else ideal ; Use TASM's Ideal mode % model MDL,pascal ; Define the memory model include "dos.inc" include "idos.inc" include "kbd.inc" include "iwhglobl.inc" NotCalled equ 0FFh ; ParamCount was already called once dataseg ; Declare variables to store the parsed parameters and parameter ; count. ParmCount DB NotCalled ; ParamCount initializes this variable ; the first time it is called. ParmList DB 7Fh DUP (0) ; Allocate enough space for a 127 ; character command line ; global PspAddress:word ; Define extrn variable codeseg ; global ParamCount:proc ; Public procs ; global ParamString:proc ; global LocateString:proc ; Declare external procs ; global FindBytePos:proc ; global FindAndReplace:proc ; global ByteCopy:proc ; global SkipSpaces:proc proc IsDelimiter ; This routine checks the character in AL to see if it is a delimiter. ; Valid delimiters are '' "" [] ; ; Input ; al - Character to check ; Output ; al contained a delimiter: ; al - matching delimiter ; Carry set ; al did not contain a delimiter ; Carry not set ; Calling conventions ; NA ; Registers modified ; al cmp al,'"' je ItIs cmp al,"'" je ItIs cmp al,"]" je ItIs cmp al,"[" je ItIsSquare jmp IsNot ItIsSquare: mov al,"]" ItIs: stc ret IsNot: clc ret endp IsDelimiter ; Use the following pattern to look for trailing spaces. label TrailingSpace word db " ",0dh proc ParamCount ; This routine returns the number of command line parameters passed to ; the program. Parameters are delimited by spaces or tabs. Double or ; single quotes can enclose parameters that include spaces or tabs ; inside them. ; ; While the function is parsing the command line it stores copies of ; each of the parameters in the modules data segment. The strings are ; stored in Turbo Pascal format. That is they are stored with a ; preceeding length byte. The first time the routine is called it also ; stores the result in the variable ParmCount in the modules data ; segment. Any subsequent calls simply return the contents of ; ParmCount. ; ; Input ; none ; Output ; al - Number of parameters ; Calling conventions ; NA ; Registers modified ; ax, bx, cx, DX, di, si, es, Flags local SearchChar:byte=LocalSymbolSize ; Declare local variables ; Point es:di to location where the parsed parameters will be stored mov ax, @data ; Using near data model mov es, ax ; Check if the function was called previously. If it was we ; don't want to parse the command line again. cmp [es:ParmCount], NotCalled je FirstCall jmp AlreadyCalled FirstCall: mov di, offset ParmList ; es:di now points to modules storage push ds ; Note that we don't actually allocate any memory to store the ; DOS Psp. We simply use the STRUC declaration to determine ; the offsets of the fields in the memory allocated by DOS. mov ds, [PspAddress] ; Load the segment address of the Psp mov si, offset (Psp).CommandTail + 1 xor cx, cx ; Initialize cx xor bx, bx ; Store # of parameters in bx ; Get the length of the DOS command line from the Psp. mov cl, [ds:Psp.CommandTail.LengthByte] ; Check if the command line is empty and leave if it is. cmp cl, 0 jne ReplaceTabs jmp InitParmCount ReplaceTabs: ; Convert any tabs in the command line to spaces in order to ; make the parsing simpler. push cx ; Store value of cx & es:di because call to push es ; FindAndReplace modifies them. push di ; Push the address of the DOS command line push ds if (@Cpu and 100b) eq 100b push offset (Psp).CommandTail + 1 else mov ax, offset (Psp).CommandTail + 1 push ax endif ; Define the bytes to search/replace mov ax, (SPACE shl 8) + TAB call FindAndReplace ; Replace all tabs with spaces pop di es ; Restore previous values of cx & es:di pop cx ; Now we need to trim the end of the string mov ax,[TrailingSpace] mov bx,cx CheckForTrailing: cmp ax,[si+bx-1] jne StringTrimmed ; String trimmed, but some still left ; Space found at end, move the carriage return forward a byte mov [byte ptr si+bx-1],0dh dec cx dec bx cmp cl, 0 jne CheckForTrailing StringTrimmed: mov bx,0 ; Set it back to zero for counting ; parameters jz InitParmCount ; If CL=0, then Z set. ; If end of string, Z not set! ; Skip any spaces at the beginning of the parameter list. push es di ; Save registers that will be modified push ds si ; Pass the address of the string call SkipSpaces pop si ds ; Update ds:si with start of next ; parameter pop di es ; Restore es:di to point to location ; to store the next parameter ; Now parse the command line. Note that after each iteration of ; this loop ds:si points to the beginning of the next parameter ; and es:di points to location in the modules data segment where ; the next parameter will be stored. ParseCmdLine: inc bx ; Increment the parameter count mov al,[si] call IsDelimiter jc HandleDelimiter jmp WhiteSpace HandleDelimiter: mov [SearchChar], al inc si ; Point to the next character in the parameter list dec cx ; Adjust number of bytes left to check jmp FindDelimiter WhiteSpace: mov al, SPACE mov [SearchChar], al ; Use space, tab or eoln as delimiter FindDelimiter: push bx es di ; Store bx, es:di ; Push the address of the start of the next parameter for the call ; to FindBytePos push ds push si ; Find the end of the parameter. After the call to FindBytePos ; ax = the number of bytes searched and es:di is pointing to ; the byte after the last one checked. cx=# of bytes left to ; check in the command line string. call FindBytePos push es ; Move the pointer returned by pop ds ; FindBytePos into ds:si mov si, di pop di es bx ; Restore es:di, bx ; Now copy the parameter into its storage location CopyParameter: mov dx, si ; Calculate the offset of the source sub dx, ax ; string dec dx ; If there was a delimiter other than a space, we want to make ; sure that we get the trailing delimiter too. cmp [SearchChar],SPACE jne BumpCopyCount or cx,cx ; Check if CX=0 (If at end of arguments) jnz StoreLength BumpCopyCount: inc al ; We need to copy and extra character StoreLength: push cx bx es si di ; Save the values of registers ; modified by the call to ByteCopy mov [byte es:di], al ; Store length of parameter in inc di ; the length byte ; Copy the array of characters call ByteCopy,ds,dx,\ ; Source string es,di ; Destination string pop di si es bx cx ; Restore the previous values of the ; registers modified by ByteCopy add di, ax ; Move pointer past end of parameter inc di cmp [byte ds:si], SPACE jne NoWhiteSpace ; Now find the first character of the next parameter. push es di ; Save registers that will be modified push ds si ; Pass the address of the string call SkipSpaces pop si ds ; Update ds:si with start of next ; parameter pop di es ; Restore es:di to point to location ; to store the next parameter NoWhiteSpace: jcxz InitParmCount jmp ParseCmdLine ; Get the next parameter on the ; command line. InitParmCount: ; Initialize ParmCount so the routine doesn't have to parse the ; command line more than once. mov [byte es:ParmCount], bl pop ds ; Restore the programs data segment AlreadyCalled: mov al, [byte es:ParmCount] ; Return the previously determined ret endp ParamCount proc ParamString ; This routine returns a far pointer to the parameter referenced in ; al. Before looking for the parameter, the function calls ParamCount ; to assure that the parameter exists. This has the side-affect of ; assuring that ParamCount parses and copies the parameters into the ; modules data segment. ; ; Input ; al - Parameter to return ; Output ; al = Desired Parameter ; es:di - Far pointer to parameter ; al = 0 - Parameter doesn't exist ; Calling conventions ; NA ; Registers modified ; ax, bx, cx, di, si, es, Flags ; First check if the parameter exists push ax ; Save index to desired parameter call ParamCount pop bx ; Restore index to desired parameter cmp bl, al ; Check if the parameter exists jg InvalidParameter mov al, bl ; Pass parameter index in al ; Point to modules data segment mov bx, @data ; Using near data model mov es, bx ; Make es:si point to the data area mov di, offset ParmList call LocateString ; Determine the address of the string jmp Exit InvalidParameter: xor al, al Exit: ret endp ParamString endif ; ifndef MDL end