00001
00196 #ifndef INCLUDED_SimpleOpt
00197 #define INCLUDED_SimpleOpt
00198
00199
00200
00201
00202 #ifdef SO_MAX_ARGS
00203 # define SO_STATICBUF SO_MAX_ARGS
00204 #else
00205 # include <stdlib.h>
00206 # include <string.h>
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)
00329 #endif
00330
00331
00332
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
00451 inline ESOError LastError() const { return m_nLastError; }
00452
00458 inline int OptionId() const { return m_nOptionId; }
00459
00465 inline const SOCHAR * OptionText() const { return m_pszOptionText; }
00466
00472 inline SOCHAR * OptionArg() const { return m_pszOptionArg; }
00473
00486 SOCHAR ** MultiArg(int n);
00487
00493 inline int FileCount() const { return m_argc - m_nLastArg; }
00494
00500 inline SOCHAR * File(int n) const {
00501 SO_ASSERT(n >= 0 && n < FileCount());
00502 return m_argv[m_nLastArg + n];
00503 }
00504
00506 inline SOCHAR ** Files() const { return &m_argv[m_nLastArg]; }
00507
00508 private:
00509 SOCHAR PrepareArg(SOCHAR * a_pszString) const;
00510 bool NextClumped();
00511 void ShuffleArg(int a_nStartIdx, int a_nCount);
00512 int LookupOption(const SOCHAR * a_pszOption) const;
00513 int CalcMatch(const SOCHAR *a_pszSource, const SOCHAR *a_pszTest) const;
00514
00515
00516 inline SOCHAR * FindEquals(SOCHAR *s) const {
00517 while (*s && *s != (SOCHAR)'=') ++s;
00518 return *s ? s : NULL;
00519 }
00520 bool IsEqual(SOCHAR a_cLeft, SOCHAR a_cRight, int a_nArgType) const;
00521
00522 inline void Copy(SOCHAR ** ppDst, SOCHAR ** ppSrc, int nCount) const {
00523 #ifdef SO_MAX_ARGS
00524
00525 while (nCount-- > 0) *ppDst++ = *ppSrc++;
00526 #else
00527 memcpy(ppDst, ppSrc, nCount * sizeof(SOCHAR*));
00528 #endif
00529 }
00530
00531 private:
00532 const SOption * m_rgOptions;
00533 int m_nFlags;
00534 int m_nOptionIdx;
00535 int m_nOptionId;
00536 int m_nNextOption;
00537 int m_nLastArg;
00538 int m_argc;
00539 SOCHAR ** m_argv;
00540 const SOCHAR * m_pszOptionText;
00541 SOCHAR * m_pszOptionArg;
00542 SOCHAR * m_pszClump;
00543 SOCHAR m_szShort[3];
00544 ESOError m_nLastError;
00545 SOCHAR ** m_rgShuffleBuf;
00546 };
00547
00548
00549
00550
00551
00552 template<class SOCHAR>
00553 bool
00554 CSimpleOptTempl<SOCHAR>::Init(
00555 int a_argc,
00556 SOCHAR * a_argv[],
00557 const SOption * a_rgOptions,
00558 int a_nFlags
00559 )
00560 {
00561 m_argc = a_argc;
00562 m_nLastArg = a_argc;
00563 m_argv = a_argv;
00564 m_rgOptions = a_rgOptions;
00565 m_nLastError = SO_SUCCESS;
00566 m_nOptionIdx = 0;
00567 m_nOptionId = -1;
00568 m_pszOptionText = NULL;
00569 m_pszOptionArg = NULL;
00570 m_nNextOption = (a_nFlags & SO_O_USEALL) ? 0 : 1;
00571 m_szShort[0] = (SOCHAR)'-';
00572 m_szShort[2] = (SOCHAR)'\0';
00573 m_nFlags = a_nFlags;
00574 m_pszClump = NULL;
00575
00576 #ifdef SO_MAX_ARGS
00577 if (m_argc > SO_MAX_ARGS) {
00578 m_nLastError = SO_ARG_INVALID_DATA;
00579 m_nLastArg = 0;
00580 return false;
00581 }
00582 #else
00583 if (m_rgShuffleBuf) {
00584 free(m_rgShuffleBuf);
00585 }
00586 if (m_argc > SO_STATICBUF) {
00587 m_rgShuffleBuf = (SOCHAR**) malloc(sizeof(SOCHAR*) * m_argc);
00588 if (!m_rgShuffleBuf) {
00589 return false;
00590 }
00591 }
00592 #endif
00593
00594 return true;
00595 }
00596
00597 template<class SOCHAR>
00598 bool
00599 CSimpleOptTempl<SOCHAR>::Next()
00600 {
00601 #ifdef SO_MAX_ARGS
00602 if (m_argc > SO_MAX_ARGS) {
00603 SO_ASSERT(!"Too many args! Check the return value of Init()!");
00604 return false;
00605 }
00606 #endif
00607
00608
00609 if (m_pszClump && *m_pszClump) {
00610
00611 bool bIsValid = NextClumped();
00612 while (*m_pszClump && !bIsValid && HasFlag(SO_O_NOERR)) {
00613 bIsValid = NextClumped();
00614 }
00615
00616
00617 if (bIsValid || !HasFlag(SO_O_NOERR)) {
00618 return true;
00619 }
00620 }
00621 SO_ASSERT(!m_pszClump || !*m_pszClump);
00622 m_pszClump = NULL;
00623
00624
00625 m_nOptionIdx = m_nNextOption;
00626 m_nOptionId = -1;
00627 m_pszOptionText = NULL;
00628 m_pszOptionArg = NULL;
00629 m_nLastError = SO_SUCCESS;
00630
00631
00632 SOCHAR cFirst;
00633 int nTableIdx = -1;
00634 int nOptIdx = m_nOptionIdx;
00635 while (nTableIdx < 0 && nOptIdx < m_nLastArg) {
00636 SOCHAR * pszArg = m_argv[nOptIdx];
00637 m_pszOptionArg = NULL;
00638
00639
00640 cFirst = PrepareArg(pszArg);
00641 if (pszArg[0] == (SOCHAR)'-') {
00642
00643 m_pszOptionArg = FindEquals(pszArg);
00644 if (m_pszOptionArg) {
00645 *m_pszOptionArg++ = (SOCHAR)'\0';
00646 }
00647 }
00648 nTableIdx = LookupOption(pszArg);
00649
00650
00651
00652 if (nTableIdx < 0
00653 && !m_pszOptionArg
00654 && pszArg[0] == (SOCHAR)'-'
00655 && pszArg[1]
00656 && pszArg[1] != (SOCHAR)'-'
00657 && pszArg[2])
00658 {
00659
00660 if (HasFlag(SO_O_SHORTARG)) {
00661 m_szShort[1] = pszArg[1];
00662 int nIdx = LookupOption(m_szShort);
00663 if (nIdx >= 0
00664 && (m_rgOptions[nIdx].nArgType == SO_REQ_CMB
00665 || m_rgOptions[nIdx].nArgType == SO_OPT))
00666 {
00667 m_pszOptionArg = &pszArg[2];
00668 pszArg = m_szShort;
00669 nTableIdx = nIdx;
00670 }
00671 }
00672
00673
00674
00675 if (nTableIdx < 0 && HasFlag(SO_O_CLUMP)) {
00676 m_pszClump = &pszArg[1];
00677 ++m_nNextOption;
00678 if (nOptIdx > m_nOptionIdx) {
00679 ShuffleArg(m_nOptionIdx, nOptIdx - m_nOptionIdx);
00680 }
00681 return Next();
00682 }
00683 }
00684
00685
00686
00687
00688 if (nTableIdx < 0) {
00689 if (!HasFlag(SO_O_NOERR) && pszArg[0] == (SOCHAR)'-') {
00690 m_pszOptionText = pszArg;
00691 break;
00692 }
00693
00694 pszArg[0] = cFirst;
00695 ++nOptIdx;
00696 if (m_pszOptionArg) {
00697 *(--m_pszOptionArg) = (SOCHAR)'=';
00698 }
00699 }
00700 }
00701
00702
00703 if (nOptIdx >= m_nLastArg) {
00704 if (nOptIdx > m_nOptionIdx) {
00705 ShuffleArg(m_nOptionIdx, nOptIdx - m_nOptionIdx);
00706 }
00707 return false;
00708 }
00709 ++m_nNextOption;
00710
00711
00712 ESOArgType nArgType = SO_NONE;
00713 if (nTableIdx < 0) {
00714 m_nLastError = (ESOError) nTableIdx;
00715 }
00716 else {
00717 m_nOptionId = m_rgOptions[nTableIdx].nId;
00718 m_pszOptionText = m_rgOptions[nTableIdx].pszArg;
00719
00720
00721 nArgType = m_rgOptions[nTableIdx].nArgType;
00722 switch (nArgType) {
00723 case SO_NONE:
00724 if (m_pszOptionArg) {
00725 m_nLastError = SO_ARG_INVALID;
00726 }
00727 break;
00728
00729 case SO_REQ_SEP:
00730 if (m_pszOptionArg) {
00731
00732
00733 if (HasFlag(SO_O_PEDANTIC)) {
00734 m_nLastError = SO_ARG_INVALID_TYPE;
00735 }
00736 }
00737
00738 break;
00739
00740 case SO_REQ_CMB:
00741 if (!m_pszOptionArg) {
00742 m_nLastError = SO_ARG_MISSING;
00743 }
00744 break;
00745
00746 case SO_OPT:
00747
00748 break;
00749
00750 case SO_MULTI:
00751
00752
00753 break;
00754 }
00755 }
00756
00757
00758 if (nOptIdx > m_nOptionIdx) {
00759 ShuffleArg(m_nOptionIdx, nOptIdx - m_nOptionIdx);
00760 }
00761
00762
00763
00764 if ( nArgType == SO_REQ_SEP
00765 && !m_pszOptionArg
00766 && m_nLastError == SO_SUCCESS)
00767 {
00768 SOCHAR ** ppArgs = MultiArg(1);
00769 if (ppArgs) {
00770 m_pszOptionArg = *ppArgs;
00771 }
00772 }
00773
00774 return true;
00775 }
00776
00777 template<class SOCHAR>
00778 SOCHAR
00779 CSimpleOptTempl<SOCHAR>::PrepareArg(
00780 SOCHAR * a_pszString
00781 ) const
00782 {
00783 #ifdef _WIN32
00784
00785
00786
00787
00788 if (!HasFlag(SO_O_NOSLASH)
00789 && a_pszString[0] == (SOCHAR)'/'
00790 && a_pszString[1]
00791 && a_pszString[1] != (SOCHAR)'-')
00792 {
00793 a_pszString[0] = (SOCHAR)'-';
00794 return (SOCHAR)'/';
00795 }
00796 #endif
00797 return a_pszString[0];
00798 }
00799
00800 template<class SOCHAR>
00801 bool
00802 CSimpleOptTempl<SOCHAR>::NextClumped()
00803 {
00804
00805 m_szShort[1] = *m_pszClump++;
00806 m_nOptionId = -1;
00807 m_pszOptionText = NULL;
00808 m_pszOptionArg = NULL;
00809 m_nLastError = SO_SUCCESS;
00810
00811
00812 int nSavedFlags = m_nFlags;
00813 m_nFlags = SO_O_EXACT;
00814 int nTableIdx = LookupOption(m_szShort);
00815 m_nFlags = nSavedFlags;
00816
00817
00818 if (nTableIdx < 0) {
00819 m_nLastError = (ESOError) nTableIdx;
00820 return false;
00821 }
00822
00823
00824 m_pszOptionText = m_rgOptions[nTableIdx].pszArg;
00825 ESOArgType nArgType = m_rgOptions[nTableIdx].nArgType;
00826 if (nArgType == SO_NONE) {
00827 m_nOptionId = m_rgOptions[nTableIdx].nId;
00828 return true;
00829 }
00830
00831 if (nArgType == SO_REQ_CMB && *m_pszClump) {
00832 m_nOptionId = m_rgOptions[nTableIdx].nId;
00833 m_pszOptionArg = m_pszClump;
00834 while (*m_pszClump) ++m_pszClump;
00835 return true;
00836 }
00837
00838
00839 m_nLastError = SO_ARG_MISSING;
00840 return true;
00841 }
00842
00843
00844
00845
00846
00847
00848
00849
00850
00851 template<class SOCHAR>
00852 void
00853 CSimpleOptTempl<SOCHAR>::ShuffleArg(
00854 int a_nStartIdx,
00855 int a_nCount
00856 )
00857 {
00858 SOCHAR * staticBuf[SO_STATICBUF];
00859 SOCHAR ** buf = m_rgShuffleBuf ? m_rgShuffleBuf : staticBuf;
00860 int nTail = m_argc - a_nStartIdx - a_nCount;
00861
00862
00863 Copy(buf, m_argv + a_nStartIdx, a_nCount);
00864
00865
00866 Copy(m_argv + a_nStartIdx, m_argv + a_nStartIdx + a_nCount, nTail);
00867
00868
00869 Copy(m_argv + a_nStartIdx + nTail, buf, a_nCount);
00870
00871
00872 m_nLastArg -= a_nCount;
00873 }
00874
00875
00876
00877 template<class SOCHAR>
00878 int
00879 CSimpleOptTempl<SOCHAR>::LookupOption(
00880 const SOCHAR * a_pszOption
00881 ) const
00882 {
00883 int nBestMatch = -1;
00884 int nBestMatchLen = 0;
00885 int nLastMatchLen = 0;
00886
00887 for (int n = 0; m_rgOptions[n].nId >= 0; ++n) {
00888
00889
00890 SO_ASSERT(m_rgOptions[n].pszArg[0] != (SOCHAR)'/');
00891
00892 int nMatchLen = CalcMatch(m_rgOptions[n].pszArg, a_pszOption);
00893 if (nMatchLen == -1) {
00894 return n;
00895 }
00896 if (nMatchLen > 0 && nMatchLen >= nBestMatchLen) {
00897 nLastMatchLen = nBestMatchLen;
00898 nBestMatchLen = nMatchLen;
00899 nBestMatch = n;
00900 }
00901 }
00902
00903
00904
00905 if (HasFlag(SO_O_EXACT) || nBestMatch == -1) {
00906 return SO_OPT_INVALID;
00907 }
00908 return (nBestMatchLen > nLastMatchLen) ? nBestMatch : SO_OPT_MULTIPLE;
00909 }
00910
00911
00912
00913 template<class SOCHAR>
00914 int
00915 CSimpleOptTempl<SOCHAR>::CalcMatch(
00916 const SOCHAR * a_pszSource,
00917 const SOCHAR * a_pszTest
00918 ) const
00919 {
00920 if (!a_pszSource || !a_pszTest) {
00921 return 0;
00922 }
00923
00924
00925 int nArgType = SO_O_ICASE_LONG;
00926 if (a_pszSource[0] != '-') {
00927 nArgType = SO_O_ICASE_WORD;
00928 }
00929 else if (a_pszSource[1] != '-' && !a_pszSource[2]) {
00930 nArgType = SO_O_ICASE_SHORT;
00931 }
00932
00933
00934 while (*a_pszSource == (SOCHAR)'-' && *a_pszSource == *a_pszTest) {
00935 ++a_pszSource;
00936 ++a_pszTest;
00937 }
00938 if (*a_pszSource == (SOCHAR)'-' || *a_pszTest == (SOCHAR)'-') {
00939 return 0;
00940 }
00941
00942
00943 int nLen = 0;
00944 while (*a_pszSource && IsEqual(*a_pszSource, *a_pszTest, nArgType)) {
00945 ++a_pszSource;
00946 ++a_pszTest;
00947 ++nLen;
00948 }
00949
00950
00951 if (!*a_pszSource) {
00952
00953 if (!*a_pszTest) {
00954 return -1;
00955 }
00956
00957
00958
00959 return 0;
00960 }
00961
00962
00963
00964 if (*a_pszTest) {
00965 return 0;
00966 }
00967
00968
00969 return nLen;
00970 }
00971
00972 template<class SOCHAR>
00973 bool
00974 CSimpleOptTempl<SOCHAR>::IsEqual(
00975 SOCHAR a_cLeft,
00976 SOCHAR a_cRight,
00977 int a_nArgType
00978 ) const
00979 {
00980
00981 if (m_nFlags & a_nArgType) {
00982 if (a_cLeft >= 'A' && a_cLeft <= 'Z') a_cLeft += 'a' - 'A';
00983 if (a_cRight >= 'A' && a_cRight <= 'Z') a_cRight += 'a' - 'A';
00984 }
00985 return a_cLeft == a_cRight;
00986 }
00987
00988
00989
00990 template<class SOCHAR>
00991 SOCHAR **
00992 CSimpleOptTempl<SOCHAR>::MultiArg(
00993 int a_nCount
00994 )
00995 {
00996
00997 if (m_nNextOption + a_nCount > m_nLastArg) {
00998 m_nLastError = SO_ARG_MISSING;
00999 return NULL;
01000 }
01001
01002
01003 SOCHAR ** rgpszArg = &m_argv[m_nNextOption];
01004
01005
01006
01007 if (!HasFlag(SO_O_NOERR)) {
01008 for (int n = 0; n < a_nCount; ++n) {
01009 SOCHAR ch = PrepareArg(rgpszArg[n]);
01010 if (rgpszArg[n][0] == (SOCHAR)'-') {
01011 rgpszArg[n][0] = ch;
01012 m_nLastError = SO_ARG_INVALID_DATA;
01013 return NULL;
01014 }
01015 rgpszArg[n][0] = ch;
01016 }
01017 }
01018
01019
01020 m_nNextOption += a_nCount;
01021 return rgpszArg;
01022 }
01023
01024
01025
01026
01027
01028
01030 typedef CSimpleOptTempl<char> CSimpleOptA;
01031
01033 typedef CSimpleOptTempl<wchar_t> CSimpleOptW;
01034
01035 #if defined(_UNICODE)
01036
01037 # define CSimpleOpt CSimpleOptW
01038 #else
01039
01040 # define CSimpleOpt CSimpleOptA
01041 #endif
01042
01043 #endif // INCLUDED_SimpleOpt