GIDNetwork > Command Line Arguments, Part 3
Register
« Command Line Arguments, Part 2 Things to Avoid in C/C++ -- gets() , Part 1 »

Command Line Arguments, Part 3

by: WaltP - Sep 08, 2005

Command Line Parameters as Switches

Design Considerations

There are two main styles of switches:

  1. /X:value or /Xvalue

  2. /X value and the switch designator can be either '/' or '-'.

I generally add a subroutine to do the parameter parsing. The first style allows the parameter parser to handle a single string. In the second form, you may have to grab the next parameter which means you have to have the entire parameter array available. So if you write a parameter function you'll have to pass the entire argv array as well as the current index. I will concentrate on the first switch style for ease of programming.

Another thing to consider is how the parameters affect the program.

  1. Do all the parameters have to be read before processing?

  2. Do you parse a few parameters at a time, process, then parse more?

These two questions will define how you write the program itself. To parse all the params at one time, this program snippet shows one way:

C/CPP/C++ Code Example:

int main(int argc, char *argv[])
{
    ...
    
    parse_params(argc, argv);
    ...
}

void parse_params(int ac, char *av[])
{
    int par = 1;
    while (par < ac)
    {
        // process a parameter and set flags
        // and other necessary info
        
        par++;
    }
    return;
}

But if the processing is done as parameters are parsed, the subroutine will only parse a single parameter, and the loop is in the mainline.

C/CPP/C++ Code Example:

int main(int argc, char *argv[])
{
    ...
    int par = 1;
    while (par < ac)
    {
        parse_param(argv[par]);

        // process the parameter
        ...

        par++;  // Go back for the next param
    }
}

void parse_params(char *av)
{
    // process the parameter and set flags
    // and other necessary info
    return;
}

This is a way to process one file at a time from a list of files for example.

Let's Do One

So let's create a parse routine. This will handle the sort parameters for a sort program I wrote. The help page states:

Generic Code Example:

Syntax:
    WSORT  inputfile [outputfile] [switches]

  Input file must preceed output file, otherwise parameters can be
      specified in any order
  Output File defaults to display

  Switches (preceed with - or /):
      /Ks.lc  Sort key (the 'K' is optional)
                 s = starting column              (default:  1)
                 l = length of key                (default: 25)
                 c = 'C' for Case sensitive       (default: insensitive)
                     'B' for Binary value         (default: text)
                 First key specified is the primary key
                 Maximum number of keys: 20
      /B      Blank lines will be removed         (default: leave)
      /D      Descending sort                     (default: ascending)
      /Hnn    File Header bytes, don't sort       (default: no header)
      /F[nn]  Fixed-length Binary data file       (default: text)
      /Lnn    Length of Record or Line            (default: 2048)
      /?      This information

What the specific parameters do is not important, but how they are defined, defaulted, and modified is the crux of the situation. Here is a list of the switches, the variables they affect, and the variable type as they are processed:

Generic Code Example:

    /Ks.lc   s = starting column            int  StartCol
             l = length of key              int  LengthCol
             c = 'C' for Case sensitive     bit  FlagCol / SENSITIVE
                 'B' for Binary value       bit  FlagCol / BINARYVAL
    /B      Blank lines will be removed     bit  flgByte / BLANKIGNORE
    /D      Descending sort                 bit  flgByte / DESCENDSORT
    /Hnn    File Header bytes, don't sort   int  HeaderSize
    /F[nn]  Fixed-length Binary data file   int  RecordSize 
                                            bit  flgByte / BINARYFILE 
    /Lnn    Length of Record or Line        int  RecordSize
    /?      This information                Direct Output

In order to accomplish this, we'll define some values and variables:

C/CPP/C++ Code Example:

// The switch values:
#define  SW_KEY      'K'
#define  SW_BLANK    'B'
#define  SW_DESCEND  'D'
#define  SW_HEADER   'H'
#define  SW_FIXED    'F'
#define  SW_LENREC   'L'
#define  SW_HELP     '?'
#define BLANKIGNORE  0x01
#define DESCENDSORT  0x02
#define BINARYFILE   0x04
#define DEBUGFLAG    0x80

// Key Values
#define  SENSITIVE   'C'
#define  BINARYVAL   'B'
#define  BIT_CASE    0x01
#define  BIT_BINARY  0x02

// Variables
char    flgByte     = 0;
int     HeaderSize  = 0;
unsigned int RecordSize = MAXBUF;
char  inpFile[FILESIZE];
char  outFile[FILESIZE];

For ease in this explanation, I'm defining the variables as globals. You can define them in a class or structure -- basically anywhere you have access to them. Call the parsing routine with:

C/CPP/C++ Code Example:

param = 1;
while (param < argc)
{
    ParseCmdLine(argv[param++]);
}

Now define the skeleton of the routine:

C/CPP/C++ Code Example:

void ParseCmdLine(char *param)
{
    char   *p;                   // Pointer for parsing the parameter
    char    sw;                  // The switch value
 
     p = param;                  // Get the parameter into the pointer
     if (*p == '-' || *p == '/') // Is the first character a switch designator?
     {                           // Yes it is.. process the switch info
        p++;                     // Next character in the parameter
        sw = toupper(*p);        // Get the switch character as uppercase
        
            // Switch processing goes here
        
    }
    else                    // Not a switch designator.
    {                       // It must be a file name.
                            // Input file first, then output file
        if (strlen(inpFile) == 0)  strcpy(inpFile, param);
            else                   strcpy(outFile, param);
    }
}

Now in the space provided, we can start testing the switches. We can use either a switch statement or nested if/else statements. We will use nested ifs in this example. To process each switch:

C/CPP/C++ Code Example:

if (sw == SW_HELP)    // The question mark switch was seen so call the 
{                     // Help Display.  No further processing needed
    DisplayHelp();
}
else                  // The BLANK switch simply sets a bit in the
if (sw == SW_BLANK)   // flgByte variable
{
    flgByte |= BLANKIGNORE;
}
else
if (sw == SW_DESCEND) // Same with the Descend switch
{
    flgByte |= DESCENDSORT;
}
else                  // Both Header and LenRecord read a value
if (sw == SW_HEADER)  //     attached to the switch
{
    p++;              // Skip the switch value
    HeaderSize  = atoi(p); // Convert the value to an int
}
else
if (sw == SW_LENREC)  // Same as the Header switch
{
    p++;
    RecordSize  = atoi(p);
}
else                  // The Fixed length binary data file switch has
if (sw == SW_FIXED)   // an optional value
{
    flgByte |= BINARYFILE;      // Set the bit
    p++;                        // Point to the value
    if (isdigit(*p))            // If there is a digit...
        RecordSize  = atoi(p);  //   convert the value to int
}

        // Now the complicated switch. Because this switch can either
        // be the optional 'K' or a digit, the switch would have been
        // somewhat ugly, hence the use of the ifs.  
        // The function NextValue() is designed to 
        //     always skip the next character, then 
        //     skip all digits following, then 
        //     skip a dot if there is one.

else                        // test if the switch is a K or a digit
if (sw == SW_KEY || isdigit(*p))
{
    if (sw == SW_KEY) p++;  // If 'K', skip it.
    StartCol = atoi(p) - 1; // Get the 's' values
    p = NextValue(p);       // Skip the value and the trailing '.'
    LengthCol = atoi(p);    // Get the 'l' value
    if (LengthCol == 0)     // If it's zero, load the default value
    {
        LengthCol = KEYDEFLEN;
    }

    p = NextValue(p);       // Skips the digits 
    while (*p)              // loop while more characters are present
    {
        if (sw == SENSITIVE)    // Check for the 'S' subswitch
                FlagCol |= BIT_CASE;
        if (sw == BINARYVAL)    // Check for the 'B' subswitch
                FlagCol |= BIT_BINARY;
        p++;                    // Next character
    }
}

If you would like to check out the program, it's called WSort.exe and can be found at http://wildeware.com under Utilities.

Would you like to comment? This story has been viewed 18,878 times.
« Command Line Arguments, Part 2 Things to Avoid in C/C++ -- gets() , Part 1 »

__top__

Copyright © GIDNetwork™ 2001 - 2024

Another website by J de Silva

Page generated in : 0.01335 sec.