SimpleOpt

SimpleOpt.h

Go to the documentation of this file.
00001 
00196 #ifndef INCLUDED_SimpleOpt
00197 #define INCLUDED_SimpleOpt
00198 
00199 // Default the max arguments to a fixed value. If you want to be able to 
00200 // handle any number of arguments, then predefine this to 0 and it will 
00201 // use an internal dynamically allocated buffer instead.
00202 #ifdef SO_MAX_ARGS
00203 # define SO_STATICBUF   SO_MAX_ARGS
00204 #else
00205 # include <stdlib.h>    // malloc, free
00206 # include <string.h>    // memcpy
00207 # define SO_STATICBUF   50
00208 #endif
00209 
00211 typedef enum _ESOError
00212 {
00214     SO_SUCCESS          =  0,   
00215 
00218     SO_OPT_INVALID      = -1,   
00219 
00222     SO_OPT_MULTIPLE     = -2,   
00223 
00226     SO_ARG_INVALID      = -3,   
00227 
00230     SO_ARG_INVALID_TYPE = -4,   
00231 
00233     SO_ARG_MISSING      = -5,   
00234 
00237     SO_ARG_INVALID_DATA = -6    
00238 } ESOError;
00239 
00241 enum _ESOFlags
00242 {
00244     SO_O_EXACT       = 0x0001, 
00245 
00248     SO_O_NOSLASH     = 0x0002, 
00249 
00252     SO_O_SHORTARG    = 0x0004, 
00253 
00256     SO_O_CLUMP       = 0x0008, 
00257 
00260     SO_O_USEALL      = 0x0010, 
00261 
00266     SO_O_NOERR       = 0x0020, 
00267 
00272     SO_O_PEDANTIC    = 0x0040, 
00273 
00275     SO_O_ICASE_SHORT = 0x0100, 
00276 
00278     SO_O_ICASE_LONG  = 0x0200, 
00279 
00282     SO_O_ICASE_WORD  = 0x0400, 
00283 
00285     SO_O_ICASE       = 0x0700  
00286 };
00287 
00293 typedef enum _ESOArgType {
00296     SO_NONE,    
00297 
00300     SO_REQ_SEP, 
00301 
00304     SO_REQ_CMB, 
00305 
00308     SO_OPT, 
00309 
00313     SO_MULTI
00314 } ESOArgType;
00315 
00317 #define SO_END_OF_OPTIONS   { -1, NULL, SO_NONE }
00318 
00319 #ifdef _DEBUG
00320 # ifdef _MSC_VER
00321 #  include <crtdbg.h>
00322 #  define SO_ASSERT(b)  _ASSERTE(b)
00323 # else
00324 #  include <assert.h>
00325 #  define SO_ASSERT(b)  assert(b)
00326 # endif
00327 #else
00328 # define SO_ASSERT(b)   //!< assertion used to test input data
00329 #endif
00330 
00331 // ---------------------------------------------------------------------------
00332 //                              MAIN TEMPLATE CLASS
00333 // ---------------------------------------------------------------------------
00334 
00336 template<class SOCHAR>
00337 class CSimpleOptTempl
00338 {
00339 public:
00341     struct SOption {
00343         int nId;        
00344 
00348         const SOCHAR * pszArg;
00349 
00351         ESOArgType nArgType;   
00352     };
00353 
00355     CSimpleOptTempl() 
00356         : m_rgShuffleBuf(NULL) 
00357     { 
00358         Init(0, NULL, NULL, 0); 
00359     }
00360 
00362     CSimpleOptTempl(
00363         int             argc, 
00364         SOCHAR *        argv[], 
00365         const SOption * a_rgOptions, 
00366         int             a_nFlags = 0
00367         ) 
00368         : m_rgShuffleBuf(NULL) 
00369     { 
00370         Init(argc, argv, a_rgOptions, a_nFlags); 
00371     }
00372 
00373 #ifndef SO_MAX_ARGS
00374 
00375     ~CSimpleOptTempl() { if (m_rgShuffleBuf) free(m_rgShuffleBuf); }
00376 #endif
00377 
00399     bool Init(
00400         int             a_argc, 
00401         SOCHAR *        a_argv[], 
00402         const SOption * a_rgOptions, 
00403         int             a_nFlags = 0
00404         );
00405 
00410     inline void SetOptions(const SOption * a_rgOptions) { 
00411         m_rgOptions = a_rgOptions; 
00412     }
00413 
00421     inline void SetFlags(int a_nFlags) { m_nFlags = a_nFlags; }
00422 
00424     inline bool HasFlag(int a_nFlag) const { 
00425         return (m_nFlags & a_nFlag) == a_nFlag; 
00426     }
00427 
00444     bool Next();
00445 
00449     void Stop();
00450 
00456     inline ESOError LastError() const  { return m_nLastError; }
00457 
00463     inline int OptionId() const { return m_nOptionId; }
00464 
00470     inline const SOCHAR * OptionText() const { return m_pszOptionText; }
00471 
00477     inline SOCHAR * OptionArg() const { return m_pszOptionArg; }
00478 
00491     SOCHAR ** MultiArg(int n);
00492 
00498     inline int FileCount() const { return m_argc - m_nLastArg; }
00499 
00505     inline SOCHAR * File(int n) const {
00506         SO_ASSERT(n >= 0 && n < FileCount());
00507         return m_argv[m_nLastArg + n];
00508     }
00509 
00511     inline SOCHAR ** Files() const { return &m_argv[m_nLastArg]; }
00512 
00513 private:
00514     CSimpleOptTempl(const CSimpleOptTempl &); // disabled
00515     CSimpleOptTempl & operator=(const CSimpleOptTempl &); // disabled
00516 
00517     SOCHAR PrepareArg(SOCHAR * a_pszString) const;
00518     bool NextClumped();
00519     void ShuffleArg(int a_nStartIdx, int a_nCount);
00520     int LookupOption(const SOCHAR * a_pszOption) const;
00521     int CalcMatch(const SOCHAR *a_pszSource, const SOCHAR *a_pszTest) const;
00522 
00523     // Find the '=' character within a string.
00524     inline SOCHAR * FindEquals(SOCHAR *s) const {
00525         while (*s && *s != (SOCHAR)'=') ++s;
00526         return *s ? s : NULL;
00527     }
00528     bool IsEqual(SOCHAR a_cLeft, SOCHAR a_cRight, int a_nArgType) const;
00529 
00530     inline void Copy(SOCHAR ** ppDst, SOCHAR ** ppSrc, int nCount) const {
00531 #ifdef SO_MAX_ARGS
00532         // keep our promise of no CLIB usage
00533         while (nCount-- > 0) *ppDst++ = *ppSrc++;
00534 #else
00535         memcpy(ppDst, ppSrc, nCount * sizeof(SOCHAR*));
00536 #endif
00537     }
00538 
00539 private:
00540     const SOption * m_rgOptions;     
00541     int             m_nFlags;        
00542     int             m_nOptionIdx;    
00543     int             m_nOptionId;     
00544     int             m_nNextOption;   
00545     int             m_nLastArg;      
00546     int             m_argc;          
00547     SOCHAR **       m_argv;          
00548     const SOCHAR *  m_pszOptionText; 
00549     SOCHAR *        m_pszOptionArg;  
00550     SOCHAR *        m_pszClump;      
00551     SOCHAR          m_szShort[3];    
00552     ESOError        m_nLastError;    
00553     SOCHAR **       m_rgShuffleBuf;  
00554 };
00555 
00556 // ---------------------------------------------------------------------------
00557 //                                  IMPLEMENTATION
00558 // ---------------------------------------------------------------------------
00559 
00560 template<class SOCHAR>
00561 bool
00562 CSimpleOptTempl<SOCHAR>::Init(
00563     int             a_argc,
00564     SOCHAR *        a_argv[],
00565     const SOption * a_rgOptions,
00566     int             a_nFlags
00567     )
00568 {
00569     m_argc           = a_argc;
00570     m_nLastArg       = a_argc;
00571     m_argv           = a_argv;
00572     m_rgOptions      = a_rgOptions;
00573     m_nLastError     = SO_SUCCESS;
00574     m_nOptionIdx     = 0;
00575     m_nOptionId      = -1;
00576     m_pszOptionText  = NULL;
00577     m_pszOptionArg   = NULL;
00578     m_nNextOption    = (a_nFlags & SO_O_USEALL) ? 0 : 1;
00579     m_szShort[0]     = (SOCHAR)'-';
00580     m_szShort[2]     = (SOCHAR)'\0';
00581     m_nFlags         = a_nFlags;
00582     m_pszClump       = NULL;
00583 
00584 #ifdef SO_MAX_ARGS
00585     if (m_argc > SO_MAX_ARGS) {
00586         m_nLastError = SO_ARG_INVALID_DATA;
00587         m_nLastArg = 0;
00588         return false;
00589     }
00590 #else
00591     if (m_rgShuffleBuf) {
00592         free(m_rgShuffleBuf);
00593     }
00594     if (m_argc > SO_STATICBUF) {
00595         m_rgShuffleBuf = (SOCHAR**) malloc(sizeof(SOCHAR*) * m_argc);
00596         if (!m_rgShuffleBuf) {
00597             return false;
00598         }
00599     }
00600 #endif
00601 
00602     return true;
00603 }
00604 
00605 template<class SOCHAR>
00606 bool
00607 CSimpleOptTempl<SOCHAR>::Next()
00608 {
00609 #ifdef SO_MAX_ARGS
00610     if (m_argc > SO_MAX_ARGS) {
00611         SO_ASSERT(!"Too many args! Check the return value of Init()!");
00612         return false;
00613     }
00614 #endif
00615 
00616     // process a clumped option string if appropriate
00617     if (m_pszClump && *m_pszClump) {
00618         // silently discard invalid clumped option
00619         bool bIsValid = NextClumped();
00620         while (*m_pszClump && !bIsValid && HasFlag(SO_O_NOERR)) {
00621             bIsValid = NextClumped();
00622         }
00623 
00624         // return this option if valid or we are returning errors
00625         if (bIsValid || !HasFlag(SO_O_NOERR)) {
00626             return true;
00627         }
00628     }
00629     SO_ASSERT(!m_pszClump || !*m_pszClump);
00630     m_pszClump = NULL;
00631 
00632     // init for the next option
00633     m_nOptionIdx    = m_nNextOption;
00634     m_nOptionId     = -1;
00635     m_pszOptionText = NULL;
00636     m_pszOptionArg  = NULL;
00637     m_nLastError    = SO_SUCCESS;
00638 
00639     // find the next option
00640     SOCHAR cFirst;
00641     int nTableIdx = -1;
00642     int nOptIdx = m_nOptionIdx;
00643     while (nTableIdx < 0 && nOptIdx < m_nLastArg) {
00644         SOCHAR * pszArg = m_argv[nOptIdx];
00645         m_pszOptionArg  = NULL;
00646 
00647         // find this option in the options table
00648         cFirst = PrepareArg(pszArg);
00649         if (pszArg[0] == (SOCHAR)'-') {
00650             // find any combined argument string and remove equals sign
00651             m_pszOptionArg = FindEquals(pszArg);
00652             if (m_pszOptionArg) {
00653                 *m_pszOptionArg++ = (SOCHAR)'\0';
00654             }
00655         }
00656         nTableIdx = LookupOption(pszArg);
00657 
00658         // if we didn't find this option but if it is a short form
00659         // option then we try the alternative forms
00660         if (nTableIdx < 0
00661             && !m_pszOptionArg
00662             && pszArg[0] == (SOCHAR)'-'
00663             && pszArg[1]
00664             && pszArg[1] != (SOCHAR)'-'
00665             && pszArg[2])
00666         {
00667             // test for a short-form with argument if appropriate
00668             if (HasFlag(SO_O_SHORTARG)) {
00669                 m_szShort[1] = pszArg[1];
00670                 int nIdx = LookupOption(m_szShort);
00671                 if (nIdx >= 0
00672                     && (m_rgOptions[nIdx].nArgType == SO_REQ_CMB
00673                         || m_rgOptions[nIdx].nArgType == SO_OPT))
00674                 {
00675                     m_pszOptionArg = &pszArg[2];
00676                     pszArg         = m_szShort;
00677                     nTableIdx      = nIdx;
00678                 }
00679             }
00680 
00681             // test for a clumped short-form option string and we didn't
00682             // match on the short-form argument above
00683             if (nTableIdx < 0 && HasFlag(SO_O_CLUMP))  {
00684                 m_pszClump = &pszArg[1];
00685                 ++m_nNextOption;
00686                 if (nOptIdx > m_nOptionIdx) {
00687                     ShuffleArg(m_nOptionIdx, nOptIdx - m_nOptionIdx);
00688                 }
00689                 return Next();
00690             }
00691         }
00692 
00693         // The option wasn't found. If it starts with a switch character
00694         // and we are not suppressing errors for invalid options then it
00695         // is reported as an error, otherwise it is data.
00696         if (nTableIdx < 0) {
00697             if (!HasFlag(SO_O_NOERR) && pszArg[0] == (SOCHAR)'-') {
00698                 m_pszOptionText = pszArg;
00699                 break;
00700             }
00701             
00702             pszArg[0] = cFirst;
00703             ++nOptIdx;
00704             if (m_pszOptionArg) {
00705                 *(--m_pszOptionArg) = (SOCHAR)'=';
00706             }
00707         }
00708     }
00709 
00710     // end of options
00711     if (nOptIdx >= m_nLastArg) {
00712         if (nOptIdx > m_nOptionIdx) {
00713             ShuffleArg(m_nOptionIdx, nOptIdx - m_nOptionIdx);
00714         }
00715         return false;
00716     }
00717     ++m_nNextOption;
00718 
00719     // get the option id
00720     ESOArgType nArgType = SO_NONE;
00721     if (nTableIdx < 0) {
00722         m_nLastError    = (ESOError) nTableIdx; // error code
00723     }
00724     else {
00725         m_nOptionId     = m_rgOptions[nTableIdx].nId;
00726         m_pszOptionText = m_rgOptions[nTableIdx].pszArg;
00727 
00728         // ensure that the arg type is valid
00729         nArgType = m_rgOptions[nTableIdx].nArgType;
00730         switch (nArgType) {
00731         case SO_NONE:
00732             if (m_pszOptionArg) {
00733                 m_nLastError = SO_ARG_INVALID;
00734             }
00735             break;
00736 
00737         case SO_REQ_SEP:
00738             if (m_pszOptionArg) {
00739                 // they wanted separate args, but we got a combined one, 
00740                 // unless we are pedantic, just accept it.
00741                 if (HasFlag(SO_O_PEDANTIC)) {
00742                     m_nLastError = SO_ARG_INVALID_TYPE;
00743                 }
00744             }
00745             // more processing after we shuffle
00746             break;
00747 
00748         case SO_REQ_CMB:
00749             if (!m_pszOptionArg) {
00750                 m_nLastError = SO_ARG_MISSING;
00751             }
00752             break;
00753 
00754         case SO_OPT:
00755             // nothing to do
00756             break;
00757 
00758         case SO_MULTI:
00759             // nothing to do. Caller must now check for valid arguments
00760             // using GetMultiArg()
00761             break;
00762         }
00763     }
00764 
00765     // shuffle the files out of the way
00766     if (nOptIdx > m_nOptionIdx) {
00767         ShuffleArg(m_nOptionIdx, nOptIdx - m_nOptionIdx);
00768     }
00769 
00770     // we need to return the separate arg if required, just re-use the
00771     // multi-arg code because it all does the same thing
00772     if (   nArgType == SO_REQ_SEP 
00773         && !m_pszOptionArg 
00774         && m_nLastError == SO_SUCCESS) 
00775     {
00776         SOCHAR ** ppArgs = MultiArg(1);
00777         if (ppArgs) {
00778             m_pszOptionArg = *ppArgs;
00779         }
00780     }
00781 
00782     return true;
00783 }
00784 
00785 template<class SOCHAR>
00786 void
00787 CSimpleOptTempl<SOCHAR>::Stop()
00788 {
00789     if (m_nNextOption < m_nLastArg) {
00790         ShuffleArg(m_nNextOption, m_nLastArg - m_nNextOption);
00791     }
00792 }
00793 
00794 template<class SOCHAR>
00795 SOCHAR
00796 CSimpleOptTempl<SOCHAR>::PrepareArg(
00797     SOCHAR * a_pszString
00798     ) const
00799 {
00800 #ifdef _WIN32
00801     // On Windows we can accept the forward slash as a single character
00802     // option delimiter, but it cannot replace the '-' option used to
00803     // denote stdin. On Un*x paths may start with slash so it may not
00804     // be used to start an option.
00805     if (!HasFlag(SO_O_NOSLASH)
00806         && a_pszString[0] == (SOCHAR)'/'
00807         && a_pszString[1]
00808         && a_pszString[1] != (SOCHAR)'-')
00809     {
00810         a_pszString[0] = (SOCHAR)'-';
00811         return (SOCHAR)'/';
00812     }
00813 #endif
00814     return a_pszString[0];
00815 }
00816 
00817 template<class SOCHAR>
00818 bool
00819 CSimpleOptTempl<SOCHAR>::NextClumped()
00820 {
00821     // prepare for the next clumped option
00822     m_szShort[1]    = *m_pszClump++;
00823     m_nOptionId     = -1;
00824     m_pszOptionText = NULL;
00825     m_pszOptionArg  = NULL;
00826     m_nLastError    = SO_SUCCESS;
00827 
00828     // lookup this option, ensure that we are using exact matching
00829     int nSavedFlags = m_nFlags;
00830     m_nFlags = SO_O_EXACT;
00831     int nTableIdx = LookupOption(m_szShort);
00832     m_nFlags = nSavedFlags;
00833 
00834     // unknown option
00835     if (nTableIdx < 0) {
00836         m_nLastError = (ESOError) nTableIdx; // error code
00837         return false;
00838     }
00839 
00840     // valid option
00841     m_pszOptionText = m_rgOptions[nTableIdx].pszArg;
00842     ESOArgType nArgType = m_rgOptions[nTableIdx].nArgType;
00843     if (nArgType == SO_NONE) {
00844         m_nOptionId = m_rgOptions[nTableIdx].nId;
00845         return true;
00846     }
00847 
00848     if (nArgType == SO_REQ_CMB && *m_pszClump) {
00849         m_nOptionId = m_rgOptions[nTableIdx].nId;
00850         m_pszOptionArg = m_pszClump;
00851         while (*m_pszClump) ++m_pszClump; // must point to an empty string
00852         return true;
00853     }
00854 
00855     // invalid option as it requires an argument
00856     m_nLastError = SO_ARG_MISSING;
00857     return true;
00858 }
00859 
00860 // Shuffle arguments to the end of the argv array.
00861 //
00862 // For example:
00863 //      argv[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8" };
00864 //
00865 //  ShuffleArg(1, 1) = { "0", "2", "3", "4", "5", "6", "7", "8", "1" };
00866 //  ShuffleArg(5, 2) = { "0", "1", "2", "3", "4", "7", "8", "5", "6" };
00867 //  ShuffleArg(2, 4) = { "0", "1", "6", "7", "8", "2", "3", "4", "5" };
00868 template<class SOCHAR>
00869 void
00870 CSimpleOptTempl<SOCHAR>::ShuffleArg(
00871     int a_nStartIdx,
00872     int a_nCount
00873     )
00874 {
00875     SOCHAR * staticBuf[SO_STATICBUF];
00876     SOCHAR ** buf = m_rgShuffleBuf ? m_rgShuffleBuf : staticBuf;
00877     int nTail = m_argc - a_nStartIdx - a_nCount;
00878 
00879     // make a copy of the elements to be moved
00880     Copy(buf, m_argv + a_nStartIdx, a_nCount);
00881 
00882     // move the tail down
00883     Copy(m_argv + a_nStartIdx, m_argv + a_nStartIdx + a_nCount, nTail);
00884 
00885     // append the moved elements to the tail
00886     Copy(m_argv + a_nStartIdx + nTail, buf, a_nCount);
00887 
00888     // update the index of the last unshuffled arg
00889     m_nLastArg -= a_nCount;
00890 }
00891 
00892 // match on the long format strings. partial matches will be
00893 // accepted only if that feature is enabled.
00894 template<class SOCHAR>
00895 int
00896 CSimpleOptTempl<SOCHAR>::LookupOption(
00897     const SOCHAR * a_pszOption
00898     ) const
00899 {
00900     int nBestMatch = -1;    // index of best match so far
00901     int nBestMatchLen = 0;  // matching characters of best match
00902     int nLastMatchLen = 0;  // matching characters of last best match
00903 
00904     for (int n = 0; m_rgOptions[n].nId >= 0; ++n) {
00905         // the option table must use hyphens as the option character,
00906         // the slash character is converted to a hyphen for testing.
00907         SO_ASSERT(m_rgOptions[n].pszArg[0] != (SOCHAR)'/');
00908 
00909         int nMatchLen = CalcMatch(m_rgOptions[n].pszArg, a_pszOption);
00910         if (nMatchLen == -1) {
00911             return n;
00912         }
00913         if (nMatchLen > 0 && nMatchLen >= nBestMatchLen) {
00914             nLastMatchLen = nBestMatchLen;
00915             nBestMatchLen = nMatchLen;
00916             nBestMatch = n;
00917         }
00918     }
00919 
00920     // only partial matches or no match gets to here, ensure that we
00921     // don't return a partial match unless it is a clear winner
00922     if (HasFlag(SO_O_EXACT) || nBestMatch == -1) {
00923         return SO_OPT_INVALID;
00924     }
00925     return (nBestMatchLen > nLastMatchLen) ? nBestMatch : SO_OPT_MULTIPLE;
00926 }
00927 
00928 // calculate the number of characters that match (case-sensitive)
00929 // 0 = no match, > 0 == number of characters, -1 == perfect match
00930 template<class SOCHAR>
00931 int
00932 CSimpleOptTempl<SOCHAR>::CalcMatch(
00933     const SOCHAR *  a_pszSource,
00934     const SOCHAR *  a_pszTest
00935     ) const
00936 {
00937     if (!a_pszSource || !a_pszTest) {
00938         return 0;
00939     }
00940 
00941     // determine the argument type
00942     int nArgType = SO_O_ICASE_LONG;
00943     if (a_pszSource[0] != '-') {
00944         nArgType = SO_O_ICASE_WORD;
00945     }
00946     else if (a_pszSource[1] != '-' && !a_pszSource[2]) {
00947         nArgType = SO_O_ICASE_SHORT;
00948     }
00949 
00950     // match and skip leading hyphens
00951     while (*a_pszSource == (SOCHAR)'-' && *a_pszSource == *a_pszTest) {
00952         ++a_pszSource; 
00953         ++a_pszTest;
00954     }
00955     if (*a_pszSource == (SOCHAR)'-' || *a_pszTest == (SOCHAR)'-') {
00956         return 0;
00957     }
00958 
00959     // find matching number of characters in the strings
00960     int nLen = 0;
00961     while (*a_pszSource && IsEqual(*a_pszSource, *a_pszTest, nArgType)) {
00962         ++a_pszSource; 
00963         ++a_pszTest; 
00964         ++nLen;
00965     }
00966 
00967     // if we have exhausted the source...
00968     if (!*a_pszSource) {
00969         // and the test strings, then it's a perfect match
00970         if (!*a_pszTest) {
00971             return -1;
00972         }
00973 
00974         // otherwise the match failed as the test is longer than
00975         // the source. i.e. "--mant" will not match the option "--man".
00976         return 0;
00977     }
00978 
00979     // if we haven't exhausted the test string then it is not a match
00980     // i.e. "--mantle" will not best-fit match to "--mandate" at all.
00981     if (*a_pszTest) {
00982         return 0;
00983     }
00984 
00985     // partial match to the current length of the test string
00986     return nLen;
00987 }
00988 
00989 template<class SOCHAR>
00990 bool
00991 CSimpleOptTempl<SOCHAR>::IsEqual(
00992     SOCHAR  a_cLeft,
00993     SOCHAR  a_cRight,
00994     int     a_nArgType
00995     ) const
00996 {
00997     // if this matches then we are doing case-insensitive matching
00998     if (m_nFlags & a_nArgType) {
00999         if (a_cLeft  >= 'A' && a_cLeft  <= 'Z') a_cLeft  += 'a' - 'A';
01000         if (a_cRight >= 'A' && a_cRight <= 'Z') a_cRight += 'a' - 'A';
01001     }
01002     return a_cLeft == a_cRight;
01003 }
01004 
01005 // calculate the number of characters that match (case-sensitive)
01006 // 0 = no match, > 0 == number of characters, -1 == perfect match
01007 template<class SOCHAR>
01008 SOCHAR **
01009 CSimpleOptTempl<SOCHAR>::MultiArg(
01010     int a_nCount
01011     )
01012 {
01013     // ensure we have enough arguments
01014     if (m_nNextOption + a_nCount > m_nLastArg) {
01015         m_nLastError = SO_ARG_MISSING;
01016         return NULL;
01017     }
01018 
01019     // our argument array
01020     SOCHAR ** rgpszArg = &m_argv[m_nNextOption];
01021 
01022     // Ensure that each of the following don't start with an switch character.
01023     // Only make this check if we are returning errors for unknown arguments.
01024     if (!HasFlag(SO_O_NOERR)) {
01025         for (int n = 0; n < a_nCount; ++n) {
01026             SOCHAR ch = PrepareArg(rgpszArg[n]);
01027             if (rgpszArg[n][0] == (SOCHAR)'-') {
01028                 rgpszArg[n][0] = ch;
01029                 m_nLastError = SO_ARG_INVALID_DATA;
01030                 return NULL;
01031             }
01032             rgpszArg[n][0] = ch;
01033         }
01034     }
01035 
01036     // all good
01037     m_nNextOption += a_nCount;
01038     return rgpszArg;
01039 }
01040 
01041 
01042 // ---------------------------------------------------------------------------
01043 //                                  TYPE DEFINITIONS
01044 // ---------------------------------------------------------------------------
01045 
01047 typedef CSimpleOptTempl<char>    CSimpleOptA; 
01048 
01050 typedef CSimpleOptTempl<wchar_t> CSimpleOptW; 
01051 
01052 #if defined(_UNICODE)
01053 
01054 # define CSimpleOpt CSimpleOptW   
01055 #else
01056 
01057 # define CSimpleOpt CSimpleOptA   
01058 #endif
01059 
01060 #endif // INCLUDED_SimpleOpt