www.pudn.com > CmdLine-src.zip > cmdline.h
//-------------------------------------------------------------------------------------------- // Copyright (c) 2001-2004 Klaus H. Probst [kprobst@vbbox.com] // // This software is provided 'as-is', without any express or implied warranty. In no event // will the authors be held liable for any damages arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, including // commercial applications, and to alter it and redistribute it freely, subject to the // following restrictions: // // ~ The origin of this software must not be misrepresented; you must not claim that you // wrote the original software. If you use this software in a product, an acknowledgment // in the product documentation would be appreciated but is not required. // ~ Altered source versions must be plainly marked as such, and must not be misrepresented // as being the original software. // ~ This notice may not be removed or altered from any source distribution. //-------------------------------------------------------------------------------------------- // // CmdLine.h // // A fairly complete command line parser* class for console or windowed applications. It // supports both straight command line parsing (as provided by the OS) or custom command // lines typed by the user, for example, from an interactive console. // // CommandLine assumes a few things about how your application's command line is // structured. Basically, it roughly expects something like the following format: // // myapp.exe arg1 /a /b:c /d @arg2 /g arg3 arg4 arg5 // // Where: // // myapp.exe - This is the path to the executable or a dummy placeholder. In the // class this is the 'path'. // arg1 - A positional argument (not an option). More than one OK. Spaces OK // as long as the argument is quoted. The max length is MAX_PATH. In // the class, this is considered a 'Command'. // /a, /d, /g - Option switches. The '/' is called a 'switch marker' and can be // whatever non-alphanumeric or whitespace character you want. For // example, '-' or '#' or whatever. Normally '-' or '/' are expected // by users. Max length of each one is 32 characters. // /b:c - A switch with a value. The ':' is called a 'value marker' and it has // to be different than the switch marker. Whatever comes after it is // considered the value. Spaces are OK as long as the whole thing is // enclosed in quotes. The max length is MAX_PATH. // @arg2 - A 'marked' argument that begins with a special character different // from both the switch and value markers. For example, a 'response file' // as used by some compilers and compression utilities. // arg3,arg4,arg5 - Trailing positional arguments that can be enumerated as long as they // come after a switch. For example, a list of files. // // This is just a sample. You can also use it like so: // // myapp.exe arg1 arg2 -a val1 -bc -d -E val2 // // which looks more like a *nix-style command line. Specifically, the 'val1' and 'val2' can be // considered switch values and retrieved even though the command line does not use a character // to delimit values. The class can also work in case-sensitive mode, so '-f' is different // from '-F'. // // These are just a few examples. You can use different switch markers and value delimiters // (or none at all), as well as marked arguments and multiple trailing positional arguments. // // Pleass refer to the README.HTML file and the online page (accessible from vbbox.com/cpp/) for // (fairly) complete documentation. The code itself is also well documented so if you can't // figure something out from the docs you should be able to divine it using the comments =) // // * It's really self-serving to call this a 'parser' since the parsing is really done by the // OS. It's really more of a 'structured accessor' or something like that. Sorta. // //-------------------------------------------------------------------------------------------- #pragma once #ifndef _CMDLINE_INC #define _CMDLINE_INC // A common problem - _UNICODE is used by the CRT #ifdef UNICODE #ifndef _UNICODE #define _UNICODE #endif #endif #include#define CMDLINE_MAX_SWITCH 32 // Max length for a switch #define CMDLINE_MAX_VALUE MAX_PATH // Max length for a switch value #define CMDLINE_MAX_ARGUMENT CMDLINE_MAX_VALUE // Max length for a positional argument // Value types. #define CMDLINE_VALUETYPE_NONE 0 #define CMDLINE_VALUETYPE_TCHAR 1 #define CMDLINE_VALUETYPE_INT 2 #define CMDLINE_VALUETYPE_FLOAT 3 class CommandLine { protected: // Intended for implementations. CommandLine() { } public: // // Initialize a new instance using whatever GetCommandLine() returns. // This is probably better for non-command line apps. When running on // an NT box and building with UNICODE #defined, the constructors will // use the GetCommandLine() API to initialize the argument array. // Otherwise they'll use the __argv and __argc globals exported from // // // These overloads should be used by windowed applications on Win9x and // ANSI builds on NT. // CommandLine(TCHAR switchMarker, TCHAR valueMarker) { #if defined (UNICODE) && defined (_WIN32_WINNT) InitializeFromCommandLine(GetCommandLine(), switchMarker, valueMarker, false, false); #else InitializeFromCommandLine(NULL, switchMarker, valueMarker, false, false); #endif } CommandLine(TCHAR switchMarker, TCHAR valueMarker, bool caseSensitive) { #if defined (UNICODE) && defined (_WIN32_WINNT) InitializeFromCommandLine(GetCommandLine(), switchMarker, valueMarker, false, caseSensitive); #else InitializeFromCommandLine(NULL, switchMarker, valueMarker, false, caseSensitive); #endif } // // Create a new instance using the argv and argc params passed to _tmain(). This // should be the default for most console apps. // CommandLine(int argc, TCHAR** argv, TCHAR switchMarker, TCHAR valueMarker) : _argvOwned(false), _caseSensitive(false) { _argc = argc; _argv = argv; _switchMarker = switchMarker; _valueMarker = valueMarker; } CommandLine(int argc, TCHAR** argv, TCHAR switchMarker, TCHAR valueMarker, bool caseSensitive) : _argvOwned(false) { _argc = argc; _argv = argv; _switchMarker = switchMarker; _valueMarker = valueMarker; _caseSensitive = caseSensitive; } #if defined (UNICODE) && defined (_WIN32_WINNT) // // These overloads can be used when passing a custom command line not // actually provided by the OS. For example, commands typed by a user // in an interactive way, or read from a file. Because the class // assumes that the first element in _argv is always *something* other // than a switch or a positional/marked argument, the overloads that // accept the 'custom' parameter will actually prepend a dummy placeholder // plus one space to the passed command line before running it through // CommandLineToArgvW() if the 'custom' argument is set to true. The // default is false. // // Obviously this also works for non-command line applications that don't // have access to argc/argv (though those are always exposed from the CRT). // In those cases simply take whatever GetCommandLine() returns and pass // it to the appropriate overload without the 'custom' parameter (because // it will already have the app's path in there). Or, use one of the first // two constructor overloads that automatically pull the command line. // // Custom command lines are not supported on 9x systems (because we use // a Unicode-only API to parse them). They are also not supported for // ANSI builds on NT boxes. Theoretically this could be implemented by // converting and managing the _argv array internally but I really don't // want to go there. // CommandLine(LPCTSTR commandLine, TCHAR switchMarker, TCHAR valueMarker) { InitializeFromCommandLine(commandLine, switchMarker, valueMarker, false, false); } CommandLine(bool custom, LPCTSTR commandLine, TCHAR switchMarker, TCHAR valueMarker) { InitializeFromCommandLine(commandLine, switchMarker, valueMarker, custom, false); } CommandLine(bool custom, LPCTSTR commandLine, TCHAR switchMarker, TCHAR valueMarker, bool caseSensitive) { InitializeFromCommandLine(commandLine, switchMarker, valueMarker, custom, caseSensitive); } CommandLine(LPCTSTR commandLine, TCHAR switchMarker, TCHAR valueMarker, bool caseSensitive) { InitializeFromCommandLine(commandLine, switchMarker, valueMarker, false, caseSensitive); } #endif // // Yah // virtual ~CommandLine() { if ((_argvOwned) && (_argv != NULL)) GlobalFree(static_cast (_argv)); } public: // // Return whether or not this instance // is case-sensitive. // bool CaseSensitive() const { return _caseSensitive; } // // Return the switch and value marker characters. // virtual TCHAR SwitchMarker() const { return _switchMarker; } virtual TCHAR ValueMarker() const { return _valueMarker; } // // Return the number of arguments. // // Note that this is not a count of switches - this // is the total number of elements in the parsed array // excluding the first one (the path or dummy placeholder). // virtual int Count() const { return (_argc - 1); } // // This should always be here, but it won't be valid // if it wasn't initialized with a real command line // from the OS. // virtual LPCTSTR Path() const { return _argv[0]; } // // This assumes that the second element in the // argument array is something other than a // switch. For example: // // C:\myapp.exe blah.txt /a /b // // ^------^ // // This is called a 'positional' argument, and // it is not an 'option'. Switches are options. // If your app requires more than one positional // argument, use the Argument() method to retrieve // them instead. // virtual LPCTSTR Command() const { if (_argv[1][0] == _switchMarker) return NULL; return _argv[1]; } // // Returns the positional argument at // index. This is the opposite of the // Switch() method, which checks for a // switch marker character. Use the Command() // method if you only have one positional // argument at the beginning of the sequence. // This of course assumes that you *know* where // your positional arguments are. That's why // they're called 'positional' =) // virtual LPCTSTR Argument(const int index) const { if ((index < 0) || (index >= _argc)) return NULL; if (_argv[index][0] != _switchMarker) return _argv[index]; return NULL; } // // Returns any positional argument that does // not begin with the specified exclusion // character. Pass '0' if your command line // does not include marked arguments. This method // helps when you only have one positional argument // but you allow the user to place it anywhere instead // of forcing it to be the first thing entered. // virtual LPCTSTR Argument(const TCHAR exclude) const { for (int i = 0; i < _argc; i++) { TCHAR ch = _argv[i][0]; if ((ch != _switchMarker) && (ch != exclude)) return _argv[i]; } return NULL; } // // Allows for enumeration of all positional // arguments. Note that this will enumerate // anything that is not a switch, including // marked arguments. The previous parameter // is normally zero, but you can pass a known // position (for example, returned from // LastSwitch()) to start enumeration there. // virtual LPCTSTR NextArgument(int* previous) const { for (int i = *previous + 1; i < _argc; i++) { LPCTSTR nextArg = Argument(i); if (nextArg) { *previous = i; return nextArg; } } return NULL; } // // Attempts to find a positional argument that // matches the string you passed. This method is // not very useful because it amounts to using // arguments as switches, which is kind of dumb. // But, you can do it if you want. The only check // performed by the class is to ensure that you're // not trying to match a switch. It is also rather // slow. The comparison is done based on the class // level case setting. // virtual bool ArgumentExists(LPCTSTR argument) const { for (int i = 1; i < _argc; i++) { LPCTSTR thisArgument = Argument(i); if (thisArgument != NULL) { if (_caseSensitive) { if (lstrcmp(argument, thisArgument) == 0) return true; } else { if (lstrcmpi(argument, thisArgument) == 0) return true; } } } return false; } // // Returns the number of times that a given switch appears // in the command line, and optionally the index of each // instance in the command array. // This method is useful when you have a switch that you // want to allow the user to repeat. For example, a utility // that processes folders recursively might accept a switch // called -exclude that has a name or wildcard that specifies // a given pattern to skip. Instead of telling the user to // enter the list of exclusions as a single switch delimited // with commas or something like that, the user enters each // option in a different instance of the same switch: // // myapp.exe -a -b -x one -x two -x three -cd // // So now you have three instances of the -x switch. If you // use the normal class methods to retrieve the value of // -x you'll simply get the first one. Instead, use this // method. // // Initially you'll want to call in here and pass NULL in the // indexes argument or call CountOf(). The method will return // the number of instances of the named switch it finds. Then // use that number to allocate an array of int and pass it // again. This time the method will fill it with the actual // indexes of the switch instances. // Once you have the indexes, use the ValueOf() method to get // the actual switch value at each index. // virtual int IndexesOf(LPCTSTR switchName, int indexes[]) const { int total = 0; for (int i = 1; i < _argc; i++) { TCHAR thisSwitch[CMDLINE_MAX_SWITCH]; if (GetSwitch(i, thisSwitch) != 0) { if (lstrcmpi(switchName, thisSwitch) == 0) { if (indexes != NULL) indexes[total] = i; total++; } } } return total; } // // Returns the value of the switch at the specified // index. This method should only be called with // indexes obtained by calling IndexesOf(). Set // checkArgument to true if your command line uses // positional arguments as values instead of character // markers. // virtual bool ValueOf(int index, LPTSTR buffer, bool checkArgument) const { LPCTSTR value = NULL; if (index > _argc) return false; if (!checkArgument) { TCHAR* valueMarker = _tcschr(_argv[index], _valueMarker); if (valueMarker) value = CharNext(valueMarker); } else { if ( (index + 1) >= _argc) return false; // Make sure it's not a switch if (_argv[index + 1][0] != _switchMarker) value = _argv[index + 1]; } if (value != NULL) { lstrcpyn(buffer, value, CMDLINE_MAX_VALUE); return true; } return false; } virtual bool ValueOf(int index, LPTSTR buffer) const { return ValueOf(index, buffer, false); } // // Same as calling IndexesOf() and passing NULL // instead of an array. // virtual int CountOf(LPCTSTR switchName) const { return IndexesOf(switchName, NULL); } // // Returns the position of the last // switch in the argument array. // This is useful for situations were a // command line might include multiple // trailing positional arguments. // For example: // // myapp.exe /k /l one /g two three four // // In this case, you could call LastSwitch() // to get the position of the '/g' switch // and use that to start the enumeration of // positional arguments using NextArgument() // because you're not interested in the 'one' // argument although it's also not a switch. // virtual int LastSwitch() const { for (int i = _argc - 1; i > 1; i--) { if (_argv[i][0] == _switchMarker) return i; } return -1; } // // Returns the position of the first switch // in the argument array. // virtual int FirstSwitch() const { for (int i = 0; i < _argc; i++) { if (_argv[i][0] == _switchMarker) return i; } return -1; } // // Returns whether or not a 'compound switch' // contains a given character. A compound switch // is something that looks like this: // // myapp.exe -abCdefg // // Some applications use switches like these. Compare // that to this: // // myapp.exe -a -b -C -d -e -f -g // // By definition, compound switches are case sensitive // and so the class-level setting that controls string // comparison has no effect when testing for them. // Also, a compound switch has to be the only switch in // the command line. Or at least the class assumes that // if you're asking for a compound switch it will be the // first one in the sequence array. // // The method returns the index of the character that // was matched to 'ch', or zero if no match was made. If // there is no switch or the switch had a value marker, // the return is -1. // virtual int CompoundSwitchContains(const TCHAR identifier) const { int index = FirstSwitch(); if (index != -1) { TCHAR* valueMarker = _tcschr(_argv[index], _valueMarker); if (valueMarker) return -1; int length = lstrlen(_argv[index]); if (!length) return -1; // Char at index 0 is the switch marker. for (int i = 1; i < length; i++) { if (_argv[index][i] == identifier) return i; } return 0; } return -1; } // // This is just a shorthand method to test for a single TCHAR in // a switch value. Basically frees you from writing a loop that // does the same thing. The 'value' parameter should be a switch // value previously obtained from the class. If the character is // found in the value, the return is true. // // A 'compound value' is similar to a compound switch in that it looks // like this: // // /switch:abcdefg // // This is sometimes easier when you want to limit the number of // switches and don't want to use duplicates that you'd obtain with // the IndexesOf() method. Or something. Hey, I just write this stuff. // virtual bool CompoundValueContains(LPCTSTR value, TCHAR identifier) const { for (int i = 0; i < lstrlen(value); i++) { if (value[i] == identifier) return true; } return false; } // // Returns a 'marked' positional argument. // An example of this are applications like command line // compression utilities or compilers that accept a // parameter prefixed with '@' which points to a // response file from which further commands are read. // Ideally there should be only one marked argument // in a command line, but you can have more than one // as long as you use different marker characters. And // of course the marker should be different from whatever // is being used as the switch delimiter. // virtual LPCTSTR MarkedArgument(const TCHAR identifier) const { LPCTSTR arg = NULL; for (int i = 1; i < _argc; i++) { if (_argv[i][0] == identifier) { // Don't include the marker itself. arg = CharNext(_argv[i]); break; } } return arg; } // // Returns the argument after the specified // switch. This method can be used to avoid // using a value marker (though *something* // has to be passed to the constructors) and // instead use spaces to separate values from // switches. For example: // // myapp.exe -a arg -b -c // // In this case, the switch delimiter was // set to '-'. Then do something like: // // LPCTSTR myValue = ArgumentAfter(_T("a")); // // to retrieve the 'arg' positional parameter. // // Basically this lets you use the class to // parse 'classic' command lines that don't use // a value delimiter. Not my thing, but hey. // // The second overload copies the value of the // argument (if found) to a buffer. Note that there // are no overloads for different types. // virtual LPCTSTR ArgumentAfter(LPCTSTR switchName) const { int index = FindSwitch(switchName); LPCTSTR arg = NULL; if (index != -1) arg = Argument(index + 1); return arg; } virtual bool ArgumentAfter(LPCTSTR switchName, LPTSTR buffer) const { LPCTSTR arg = ArgumentAfter(switchName); if (arg) { lstrcpyn(buffer, arg, CMDLINE_MAX_ARGUMENT); return true; } return false; } // // Return the name of the switch at index. // Before returning, a check is made to // ensure that the element at is indeed a switch, // as opposed to a positional or marked argument. // In general you should not retrieve swtiches // by index, but by name. If the switch has a // value it will not be returned along with the // switch itself. // virtual bool Switch(const int index, LPTSTR buffer) const { if ((index < 0) || (index >= _argc)) return NULL; return (GetSwitch(index, buffer) != 0); } // // Returns whatever is at _argv[n]. Unlike // the Switch() and Argument() methods, this // does no checking. Not useful since this // amounts to using the class directly as // a glorified array. // virtual LPCTSTR operator [] (const int index) const { if ((index < 0) || (index >= _argc)) return NULL; return _argv[index]; } // // Returns true if a given switch exists. Note // that switchName should be the _name_ of the // switch without the actual switch delimiter. // // The second override allows an application to // support short and long versions of switches. // For example, where '/v' and '/verb' should be // interpreted the same way. Simply pass an array // of possible names for a switch (and the count of // items in the array). The class will return the // index of the switches array that matched a switch // in the argument array. If you pass a valid buffer // then the matched switch name gets copied to it; // otherwise pass NULL if you're not interested in // having the class copy the name and you're going to // use the returned index into your own array. // // From then on you have the actual switch name, // which you can use to call the other functions, // like SwitchHasValue(). If there is no match, the // return is NULL and you know that neither version // of the argument was there. Although the method // signature lets you pass an array of any size, // obviously the ideal is to have only two forms // of the same switch. Anything else will probably // be confusing to people who use your app. // // Finally note that the 'switches' argument is const, // so either pass a const ref from your side or use a // const_cast: // // const TCHAR* args[] = {_T("v"), _T("verb")}; // virtual bool SwitchExists(LPCTSTR switchName) const { return (FindSwitch(switchName) != -1); } virtual int SwitchExists(const TCHAR** switches, const int count, LPTSTR buffer) const { for (int i = 0; i < count; i++) { int index = FindSwitch(switches[i], buffer); if (index != -1) return i; } return -1; } // // Returns true if the switch exists, and has a value. // The second override can be used when positional // arguments are being used instead of values delimited with // markers. So while the first override checks for something // like this: // // /f:blah // // the second one checks for: // // /f blah // // assuming the 'checkArgument' parameter is true. This is // the same as calling ArgumentAfter(switchName). // virtual bool SwitchHasValue(LPCTSTR switchName) const { int index = FindSwitch(switchName); if (index != -1) { return GetValue(index, NULL, true); } return false; } virtual bool SwitchHasValue(LPCTSTR switchName, bool checkArgument) const { if (!checkArgument) return SwitchHasValue(switchName); return (ArgumentAfter(switchName) != NULL); } // // These methods return the value of the specified switch. // There are three main overloads: TCHAR*, int and double. // Each of these have an additional overload that accepts a // default value that will be returned instead if the value // didn't exist. Keep in mind that when the default is returned // the methods will *still* return false. This lets you determine // if the default you passed is being returned or not. // // For the TCHAR* overload, value is assumed to be CMDLINE_MAX_VALUE // in length. // virtual bool SwitchValue(LPCTSTR switchName, LPTSTR value) const { int index = FindSwitch(switchName); if (index != -1) { return GetValue(index, value, false); } return false; } virtual bool SwitchValue(LPCTSTR switchName, LPTSTR value, LPCTSTR defaultValue) const { bool result = SwitchValue(switchName, value); if (!result) lstrcpy(value, defaultValue); return result; } virtual bool SwitchValue(LPCTSTR switchName, int* value) const { TCHAR buffer[32]; if (SwitchValue(switchName, buffer)) { *value = _ttoi(buffer); return true; } return false; } virtual bool SwitchValue(LPCTSTR switchName, int* value, const int defaultValue) const { bool result = SwitchValue(switchName, value); if (!result) *value = defaultValue; return result; } virtual bool SwitchValue(LPCTSTR switchName, double* value) const { TCHAR buffer[32]; if (SwitchValue(switchName, buffer)) { // There is no generic mapping for atof() in the CRT // released with VC++ 6 (as far as I know). I.e., _tstof() is // just not there AFAICT. Weird, eh. #if (_MSC_VER >= 1300) // VC.NET 7.x is release 13 of the compiler. *value = _tstof(buffer); #else *value = _tcstod(buffer, NULL); #endif //(_MSC_VER >= 1300) return true; } return false; } virtual bool SwitchValue(LPCTSTR switchName, double* value, const double defaultValue) const { bool result = SwitchValue(switchName, value); if (!result) *value = defaultValue; return result; } // // Determines if a switch exists and actually has a value, and // then compares it to the passed value. If they match, the // return is true. // // The string version has one overload that specifies if the // comparison is case-sensitive. This is different from the // class-level case setting. If the argument is omitted the // default is to compare the values without regard to case. // // There is an additional overload for int values, but not one // for double. Yes, there's an overload of SwitchValue() that // that accepts a double, but I don't want to get into // comparing floating point values. That's up to you =) Since // it's unlikely that heavy floating point support will be used // in a command line (i.e., two or three decimal positions should // be enough), perhaps the easiest way is to use *printf() to // compare them as strings instead. // virtual bool SwitchValueIs(LPCTSTR switchName, LPCTSTR value, const bool caseSensitive) const { TCHAR buffer[CMDLINE_MAX_VALUE]; int index = FindSwitch(switchName); if ((index != -1) && SwitchHasValue(switchName)) { if (GetValue(index, buffer, false)) { // Note that the class-level case setting does not apply here. if (caseSensitive) return (lstrcmp(buffer, value) == 0); else return (lstrcmpi(buffer, value) == 0); } } return false; } virtual bool SwitchValueIs(LPCTSTR switchName, LPCTSTR value) const { return SwitchValueIs(switchName, value, false); } virtual bool SwitchValueIs(LPCTSTR switchName, const int value) const { int actualValue = 0; int index = FindSwitch(switchName); if ((index != -1) && SwitchHasValue(switchName)) { if (SwitchValue(switchName, &actualValue)) { return (value == actualValue); } } return false; } // // Returns the number of positional arguments // (those that don't start with a switch marker) // in the command line. The count does not include // the argument at position 0, which is either the // path to the executable or a dummy placeholder. // Note that this will also include marked positional // arguments, if they are there. // virtual int ArgumentCount() const { int count = 0; // Again, loop starts at one. for (int i = 1; i < _argc; i++) { if (_argv[i][0] != _switchMarker) count++; } return count; } // // Returns the number of switches in the command // line. The count does not include the element // at position 0, which is either the path to the // executable or a dummy placeholder. // virtual int SwitchCount() const { int count = 0; // Again, loop starts at one. for (int i = 1; i < _argc; i++) { if (_argv[i][0] == _switchMarker) count++; } return count; } // // Shorthand method for parsing individual commands. // This method is handy when processing arguments read from // a response file, for example. It prevents you from having // to create an instance of the class every time you need to // parse another command. // // This method operates under the assumption that whatever // you are passing contains a single switch and, optionally, // a value. Multiple switches will not be parsed, and in fact // will probably cause weird behavior. If you need that then // just use an instance of the class. // // Pass the raw string in the 'argument' parameter and // properly allocated buffers in the in the 'switchName' and // 'value' parameters. If the string can be parsed as a // switch, the name is copied to 'switchName'. If the switch // has a value, it is copied to the 'value' parameter and the // 'hasValue' parameter is set to true. Note that this does not // differentiate between delimited and positional values, which // means that you _must_ initialize the class with a value marker // set to the space character (0x20) if you're passing // commands that have values otherwise retrieved using the // ArgumentAfter() method. IOW, if your commands look like this: // // -switch value // // then the switch marker must be set to 0x20. When using the // class to parse automatically using the OS this is not // necessary and you can just initialize the marker with something // else because, again, you get those with ArgumentAfter(). This is // due to how CommandLineToArgvW() works - it uses spaces as the // delimiter boundary to create the _argv array. I hope that makes // sense. // // If you're not interested in the switch value being returned // simply pass a NULL pointer. The test will still be performed // and the 'hasValue' argument will be set to true (if a value // is there) but the value won't be copied. // // The method will return false only if the 'argument' parameter // is NULL or is less than or equal to 1 character in length // (obviously a switch has to be at least two chars in length, // counting the marker itself), or if the first character is not // equal to the defined switch marker. // // If you're parsing a response file and you're expecting positional // arguments (non-switches) to be there you can pass them here and // then see if the return is false. If the string itself is longer // than 1 character that means said string is a valid positional // argument. // // Note that there is no overload for int or double values. It's up // to your code to convert them if necessary based on the name of // the switch and its expected value. Always check the 'hasValue' // parameter to see if something was actually copied to the buffer. // bool Parse(LPTSTR argument, LPTSTR switchName, bool& hasValue, LPTSTR value) const { if (!argument || lstrlen(argument) == 1) return false; if (argument[0] != _switchMarker) return false; TCHAR nameBuffer[CMDLINE_MAX_SWITCH]; int chars = GetSwitch(argument, nameBuffer); lstrcpyn(switchName, nameBuffer, chars + 1); hasValue = false; if (GetValue(argument, value, (value == NULL), true)) { hasValue = true; } return true; } protected: // // Finds a switch in the argument array and // returns its index. If the argument can't // be found, the return is -1. // This is pretty much the meat of the class. // virtual int FindSwitch(LPCTSTR switchName, LPTSTR buffer = NULL) const { // Note the loop starts at 1, not zero. for (int index = 1; index < _argc; index++) { // Does this have a switch marker to begin with? if (_argv[index][0] != _switchMarker) continue; // Obviously no single switch should exceed CMDLINE_MAX_SWITCH chars. // Actually anything over 8 chars is an abomination. TCHAR pzTemp[CMDLINE_MAX_SWITCH]; int charCount = 0; charCount = GetSwitch(index, pzTemp); // Not the same length, don't bother. if (charCount != lstrlen(switchName)) continue; bool found = false; if (_caseSensitive) { found = (_tcscmp(pzTemp, switchName) == 0); } else { found = (_tcsicmp(pzTemp, switchName) == 0); } if (found) { if (buffer != NULL) lstrcpy(buffer, pzTemp); return index; } } return -1; } // // Extracts the switch at index, removing the switch marker // itself and the value, if present. Returns the number of // characters copied to the buffer, which is assumed to // be CMDLINE_MAX_SWITCH chars in length. // virtual int GetSwitch(const int index, TCHAR* buffer) const { return GetSwitch(_argv[index], buffer); } int GetSwitch(/* const */ TCHAR* item, TCHAR* buffer) const { int charCount = 0; // Does this have a switch marker to begin with? if (item[0] != _switchMarker) return 0; TCHAR* valueMarker = NULL; // See if it has a value separator. // This should be enough to convince you not to use letters // as switch or value delimiters (if nothing else did). // We don't bother testing for the marker if it was initialized // to zero. if (_valueMarker != 0) valueMarker = _tcschr(item, _valueMarker); // In both cases below we substract one because we don't // want to compare the argument + the marker; only the // argument itself. // In the case of a switch with no value marker, we just use // the length of the argument (in the list, not the passed // one). // And in the case of a switch that has a value marker, // we simply substract the location of the marker from // the address of the argument. if (valueMarker != NULL) { charCount = (valueMarker - item) - 1; } else { charCount = lstrlen(item) - 1; } if (charCount > 0) lstrcpyn(buffer, CharNext(item), charCount + 1); return charCount; } // // Retrieves the value of the switch at index. If the switch has no value // or does not include a value marker, the return is false and buffer is undefined. // If test is true, the method returns true if there's a value, but does not // actually copy it to the buffer. The buffer is assumed to be big enough. // virtual bool GetValue(int index, LPTSTR buffer, const bool test) const { if (index != -1) return GetValue(_argv[index], buffer, test, false); return false; } // // If 'custom' is set to true the method will remove quotes from // the value. This is necessary because we want to support quotes // when the Parse() method is called, but we also want to support // double-quoted arguments when the OS is doing the parsing. // bool GetValue(/* const */ TCHAR* item, LPTSTR buffer, const bool test, const bool custom) const { if (item) { int length = lstrlen(item); int chars = 0; if (_valueMarker == 0x0) return false; TCHAR* valueMarker = _tcschr(item, _valueMarker); if (valueMarker) { if (!test) { TCHAR* ptr = CharNext(valueMarker); chars = lstrlen(ptr); if (custom) { TCHAR* szQuote = _tcschr(ptr, 0x22); if (szQuote) { ptr = CharNext(szQuote); chars -= 2; } } lstrcpyn(buffer, ptr, chars + 1); } return true; } } return false; } private: // // Called from the custom command line constructors // void InitializeFromCommandLine(LPCTSTR commandLine, TCHAR switchMarker, TCHAR valueMarker, bool custom, bool caseSensitive) { _caseSensitive = caseSensitive; _switchMarker = switchMarker; _valueMarker = valueMarker; #if defined (UNICODE) && defined (_WIN32_WINNT) _argvOwned = true; TCHAR* command = NULL; if (custom) { command = new TCHAR[10 + lstrlen(commandLine)]; lstrcpy(command, _T("_DUMMY_ ")); // Whatever lstrcat(command, commandLine); _argv = CommandLineToArgvW(command, &_argc); delete[] command; } else { _argv = CommandLineToArgvW(commandLine, &_argc); } #else _argvOwned = false; _argv = __argv; _argc = __argc; #endif } public: #ifdef DEBUG // // Helper. // virtual void _Dump() { for (int i = 0; i < _argc; i++) _tprintf(_T("%i - %s\n"), i, _argv[i]); } #endif protected: int _argc; // Number of arguments in the argument array TCHAR** _argv; // Argument array TCHAR _switchMarker; // TCHAR to use as switch delimiter TCHAR _valueMarker; // TCHAR to use as value delimiter bool _caseSensitive; // True if this instance is case sensitive, otherwise false. bool _argvOwned; // Flag that tells us we need to free _argv because we got it from CommandLineToArgvW }; #endif // #ifndef _CMDLINE_INC