'=========================================================================== ' Subject: READING COMMAND-LINE PARAMS Date: 05-23-99 (01:03) ' Author: Brian Marstella Code: QB, PDS ' Origin: brimars@mis.net Packet: DOS.ABC '=========================================================================== ' FileName: CMDDEMO.BAS ' FileDate: 05/23/1999 ' FileVer: 1.0 ' ' Purpose: Demonstrates how to read command-line parameters in and parse ' them neatly into an array. Reads the command-line case correctly and ' strip out command prefixes (- and / usually). Also retains quoted ' strings as 1 string, rather than splitting to multiple arguments. ' ' Status: Public Domain ' ' Language: QB45 (maybe others with some work; will not work with QBasic ' that comes with DOS unless you have someone's subroutines that will ' allow you to simulate DEF SEG and CALL INTERRUPT) ' ' Will not work properly from the QB45 programming environment, due to ' the calls it uses. It will find the command line entered when starting ' QB, rather than any other parms. ' ' O/S: Works with DOS 3.3+, WIN95, WIN98. I'm not sure if it will ' work with WINNT, OS/2, etc., due to the vastly different systems. ' ' Notes: Well, I actually wrote the subroutine for my own use. However, I ' know that I had a great deal of trouble figuring out how to use DEF SEG, ' call interrupt and some others, so hopefully I've documented this file ' well enough that it will help someone a little. ' ' Just so you know, this program is provided with absolutely no warranty ' of any kind. I don't guarantee that it will or won't do anything at all. ' ' If you would like to contact me for clarifications or whatever, you may ' e-mail me at brimars@yahoo.com. ' ' I'm sure that many of you who have programmed for years are disgusted ' by my formatting, extensive commenting, non-cryptic usages, child-like ' syntax, or whatever. Sorry. I wrote this for me and average people ' who might need to access the command-line. For C and assembly ' programmers, yes I know that there are many ways to do this much more ' cleanly, etc. Some people don't program in C and assembly, though, and ' might wish to have a way to do this in a somewhat clearer, more homey ' atmosphere than C (try to figure out how to get command line parms on ' your own in C; then try to use them). ' ' If you have constructive suggestions or find that I've missed an ' important method that a user can use to crash this, let me know. I'll ' try to make changes accordingly. ' ' ToDo: Add a little routine that will get the complete program path and ' name and store it to element 0 in the array. ' ' Oh, also this program does not reference QB.BI; however, you must have ' QB.LIB available to compile. If QB.LIB is not present, you must expand it ' off of your distribution disks. Since this file may not have installed ' properly when you installed QB45, make sure you install it in your library ' directory. This library is absolutely required to compile files using ' the CALL INTERRUPT, CALL ABSOLUTE, etc. '************************************** ' Precompile instructions '************************************** '************************************** ' dynamic simply means that arrays can ' be redimensioned on the fly and ' allows ERASE to destroy arrays and ' free their memory. '************************************** '$DYNAMIC '************************************** ' Type Declarations '************************************** '************************************** ' type reg is required for call ' interrupt '************************************** TYPE reg ax AS INTEGER bx AS INTEGER cx AS INTEGER dx AS INTEGER bp AS INTEGER di AS INTEGER si AS INTEGER flags AS INTEGER END TYPE '************************************** ' Function Declarations '************************************** '************************************** ' Subroutine Declarations '************************************** DECLARE SUB getcmdline (argc%, arg$()) DECLARE SUB interrupt (intnum%, regsin AS reg, regsout AS reg) DECLARE SUB main (argc%, arg$()) '************************************** ' Constant Declarations '************************************** CONST DOSINT = &H21 CONST FALSE = 0 CONST TRUE = NOT FALSE '************************************** ' dimension the array to hold command ' line parameters '************************************** DIM args$(0) '************************************** ' Begin Main Program '************************************** getcmdline argc%, args$() main argc%, args$() '************************************** ' End Main Program '************************************** END REM $STATIC SUB getcmdline (argc%, arg$()) 'This subroutine returns the command line as entered (case of characters IS 'retained). Strips normal terminators (/ and -) and single & double quotes. 'Text enclosed in single and double quotes is kept as a continous string. '************************************** ' dimension working variables as needed '************************************** DIM regs AS reg DIM temparg$(0) '************************************** ' call the routine to get the PSP ' offset address (stores the command ' line parms among other things...) '************************************** regs.ax = &H6200 CALL interrupt(DOSINT, regs, regs) '************************************** ' now change the working memory segment ' to whatever value was returned in BX ' by the above call. Offset 80 of ' this segment stores the entire length ' of the parameters entered (including ' the space between the program name ' and the parms!!!), so the command ' length is actually 1 less than what ' you are led to believe by good sense ' or experience on other systems '************************************** DEF SEG = regs.bx clen% = PEEK(&H80) - 1 cl$ = "" '************************************** ' if offset &h80 was greater than -1 ' then parms were entered; this section ' assembles them into a continuous ' string and notes that the argument ' counter (argc%) is at least 1 '************************************** IF clen% > -1 THEN FOR xloop% = 0 TO clen% cl$ = cl$ + CHR$(PEEK(&H80 + xloop% + 1)) NEXT xloop% argc% = 1 ELSE '************************************** ' if the value at offset 80 was 0 then ' no command line parms were entered ' and the argument counter is set to 0 ' and the sub is terminated '************************************** argc% = 0 EXIT SUB END IF '************************************** ' now change back to the default memory ' segment; otherwise, you'll get tons ' of errors regarding corrupt string ' space and stuff. Besides, it's good ' practice '************************************** DEF SEG '************************************** ' begin assembling the arguments by ' first setting up the input array for ' 1 length, initialize it to null, and ' initialize a couple of other vars as ' needed for the subroutine '************************************** REDIM arg$(argc%) arg$(argc%) = "" c$ = "" quotesopen% = FALSE '************************************** ' now start a loop to assemble the ' actual arguments entered into the ' input array area (arg$) '************************************** FOR xloop% = 1 TO clen% + 1 '************************************** ' set lc$ (last character) equal to the ' current character c$ then set c$ ' equal to the next character in the ' command line string (cl$) '************************************** lc$ = c$ c$ = MID$(cl$, xloop%, 1) '************************************** ' the select case determines what to do ' depending on what the next character ' c$ is '************************************** SELECT CASE c$ '************************************** ' if c$ is a space, then we first find ' out if quotes have started. if it's ' part of a quoted string, we simply ' add it to the current parameter and ' go about our business. however, if ' lc$ (last character) was not a space, ' it's not part of a quoted string, and ' it's not the first character in the ' parameter, then we assume that we ' have ended the current parameter. ' ' if the last character was a space, ' there's nothing that needs to be done ' ' if it's part of a quoted string, ' we've already taken care of it ' ' if it's the first character in the ' parms, then there's no sense making ' it a part of the current parm ' ' as an afterthought, we also check if ' the last character was a terminator. ' if so, this evaluation has no meaning '************************************** CASE " " IF quotesopen% = TRUE THEN arg$(argc%) = arg$(argc%) + c$ END IF IF (lc$ <> " " AND lc$ <> "-" AND lc$ <> "/" AND quotesopen% = FALSE AND xloop% > 1) THEN '************************************** ' if we made it this far, then we've ' reached the end of the current parm ' and need to prep for the next one ' first we redimension a temporary ' array to hold all the current parms, ' since we can't redimension the actual ' parm array (arg$) without destroying ' the parms we already collected '************************************** REDIM temparg$(argc%) '************************************** ' now we copy all the parms from the ' parm array arg$ to the temporary ' array temparg$ '************************************** FOR yloop% = 1 TO argc% temparg$(yloop%) = arg$(yloop%) NEXT yloop% '************************************** ' increment the argument counter (good ' a place as any to do it...) '************************************** argc% = argc% + 1 '************************************** ' now that all the parms are in a temp ' array, we'll redimension our actual ' array arg$ to the new number of args ' expected and then we'll copy the ' parms back using the same method as ' above '************************************** REDIM arg$(argc%) FOR yloop% = 1 TO argc% - 1 arg$(yloop%) = temparg$(yloop%) NEXT yloop% '************************************** ' next line initializes the current ' arg to NUL and the following lines ' dimension the temporary array back ' to zero to free memory '************************************** arg$(argc%) = "" ERASE temparg$ DIM temparg$(0) firstchar% = TRUE END IF '************************************** ' if the character in c$ is a single ' ' or double " quote, then we change the ' status of quotesopen%. this value is ' used simply to track whether we are ' in the middle of a text string (TRUE) ' or not (FALSE) '************************************** CASE CHR$(34), CHR$(39) IF quotesopen% = FALSE THEN quotesopen% = TRUE ELSE quotesopen% = FALSE END IF '************************************** ' the next case checks parm terminators ' which I simply strip unless they are ' part of a text string (quotesopen% is ' TRUE or the length of the parm is ' greater than zero). you can add more ' terminators or other characters to ' strip simply by adding them to the ' case statement separating each by a ' comma. make sure you also add them ' to the "space" evaluation case above '************************************** CASE "-", "/" IF quotesopen% = TRUE OR LEN(arg$(argc%)) > 0 THEN arg$(argc%) = arg$(argc%) + c$ END IF '************************************** ' if it's any other character, we just ' tack it onto the end of the command ' string and go on about our merry way '************************************** CASE ELSE arg$(argc%) = arg$(argc%) + c$ END SELECT NEXT xloop% '************************************** ' if the last argument is empty (which ' means that your user entered spaces ' or terminators at the end of the ' command line), then we kill it much ' the same way we added to the array. ' we also subtract 1 from the argument ' counter '************************************** IF LEN(arg$(argc%)) = 0 THEN argc% = argc% - 1 '************************************** ' if we no longer have any parms left ' then we can simply set the counter to ' zero, destroy the argument array and ' exit the sub '************************************** IF argc% = 0 THEN REDIM arg$(0) EXIT SUB END IF '************************************** ' continue copying the array '************************************** REDIM temparg$(argc%) FOR xloop% = 1 TO argc% temparg$(xloop%) = arg$(xloop%) NEXT xloop% REDIM arg$(argc%) FOR xloop% = 1 TO argc% arg$(xloop%) = temparg$(xloop%) NEXT xloop% ERASE temparg$ REDIM temparg$(0) END IF '************************************** ' set c$ and lc$ = NULL to kill the ' space they're taking up '************************************** c$ = "" lc$ = "" END SUB SUB main (argc%, args$()) ' Sample subroutine that uses the arguments found on the command line. PRINT "Total Arguments: "; argc% PRINT IF argc% > 0 THEN FOR xloop% = 1 TO argc% PRINT "Argument #"; xloop%; " = "; args$(xloop%) NEXT xloop% END IF END SUB