|
||||
|
« 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 SwitchesDesign Considerations There are two main styles of switches:
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.
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.
|
GIDNetwork Sites
Archives
Recent GIDBlog Posts
Recent GIDForums Posts
Contact Us
|
« Command Line Arguments, Part 2 | Things to Avoid in C/C++ -- gets() , Part 1 » |