181 #ifndef INCLUDED_SimpleOpt
182 #define INCLUDED_SimpleOpt
188 #define SO_STATICBUF SO_MAX_ARGS
192 #define SO_STATICBUF 50
196 typedef enum _ESOError {
206 SO_OPT_MULTIPLE = -2,
214 SO_ARG_INVALID_TYPE = -4,
221 SO_ARG_INVALID_DATA = -6
231 SO_O_NOSLASH = 0x0002,
235 SO_O_SHORTARG = 0x0004,
243 SO_O_USEALL = 0x0010,
255 SO_O_PEDANTIC = 0x0040,
258 SO_O_ICASE_SHORT = 0x0100,
261 SO_O_ICASE_LONG = 0x0200,
265 SO_O_ICASE_WORD = 0x0400,
276 typedef enum _ESOArgType {
300 #define SO_END_OF_OPTIONS \
308 #define SO_ASSERT(b) _ASSERTE(b)
311 #define SO_ASSERT(b) assert(b)
322 template <
class SOCHAR>
323 class CSimpleOptTempl {
333 const SOCHAR* pszArg;
341 : m_rgShuffleBuf(NULL)
343 Init(0, NULL, NULL, 0);
350 const SOption* a_rgOptions,
352 : m_rgShuffleBuf(NULL)
354 Init(argc, argv, a_rgOptions, a_nFlags);
362 free(m_rgShuffleBuf);
390 const SOption* a_rgOptions,
397 inline void SetOptions(
const SOption* a_rgOptions)
399 m_rgOptions = a_rgOptions;
409 inline void SetFlags(
int a_nFlags) { m_nFlags = a_nFlags; }
412 inline bool HasFlag(
int a_nFlag)
const
414 return (m_nFlags & a_nFlag) == a_nFlag;
445 inline ESOError LastError()
const {
return m_nLastError; }
452 inline int OptionId()
const {
return m_nOptionId; }
459 inline const SOCHAR* OptionText()
const {
return m_pszOptionText; }
466 inline SOCHAR* OptionArg()
const {
return m_pszOptionArg; }
480 SOCHAR** MultiArg(
int n);
487 inline int FileCount()
const {
return m_argc - m_nLastArg; }
494 inline SOCHAR* File(
int n)
const
496 SO_ASSERT(n >= 0 && n < FileCount());
497 return m_argv[m_nLastArg + n];
501 inline SOCHAR** Files()
const {
return &m_argv[m_nLastArg]; }
504 CSimpleOptTempl(
const CSimpleOptTempl&);
505 CSimpleOptTempl& operator=(
const CSimpleOptTempl&);
507 SOCHAR PrepareArg(SOCHAR* a_pszString)
const;
509 void ShuffleArg(
int a_nStartIdx,
int a_nCount);
510 int LookupOption(
const SOCHAR* a_pszOption)
const;
511 int CalcMatch(
const SOCHAR* a_pszSource,
const SOCHAR* a_pszTest)
const;
514 inline SOCHAR* FindEquals(SOCHAR* s)
const
516 while (*s && *s != (SOCHAR)
'=')
518 return *s ? s : NULL;
520 bool IsEqual(SOCHAR a_cLeft, SOCHAR a_cRight,
int a_nArgType)
const;
522 inline void Copy(SOCHAR** ppDst, SOCHAR** ppSrc,
int nCount)
const
529 memmove(ppDst, ppSrc, nCount *
sizeof(SOCHAR*));
534 const SOption* m_rgOptions;
542 const SOCHAR* m_pszOptionText;
543 SOCHAR* m_pszOptionArg;
546 ESOError m_nLastError;
547 SOCHAR** m_rgShuffleBuf;
554 template <
class SOCHAR>
555 bool CSimpleOptTempl<SOCHAR>::Init(
558 const SOption* a_rgOptions,
564 m_rgOptions = a_rgOptions;
565 m_nLastError = SO_SUCCESS;
568 m_pszOptionText = NULL;
569 m_pszOptionArg = NULL;
570 m_nNextOption = (a_nFlags & SO_O_USEALL) ? 0 : 1;
571 m_szShort[0] = (SOCHAR)
'-';
572 m_szShort[2] = (SOCHAR)
'\0';
577 if (m_argc > SO_MAX_ARGS) {
578 m_nLastError = SO_ARG_INVALID_DATA;
583 if (m_rgShuffleBuf) {
584 free(m_rgShuffleBuf);
586 if (m_argc > SO_STATICBUF) {
587 m_rgShuffleBuf = (SOCHAR**)malloc(
sizeof(SOCHAR*) * m_argc);
588 if (!m_rgShuffleBuf) {
597 template <
class SOCHAR>
598 bool CSimpleOptTempl<SOCHAR>::Next()
601 if (m_argc > SO_MAX_ARGS) {
602 SO_ASSERT(!
"Too many args! Check the return value of Init()!");
608 if (m_pszClump && *m_pszClump) {
610 bool bIsValid = NextClumped();
611 while (*m_pszClump && !bIsValid && HasFlag(SO_O_NOERR)) {
612 bIsValid = NextClumped();
616 if (bIsValid || !HasFlag(SO_O_NOERR)) {
620 SO_ASSERT(!m_pszClump || !*m_pszClump);
624 m_nOptionIdx = m_nNextOption;
626 m_pszOptionText = NULL;
627 m_pszOptionArg = NULL;
628 m_nLastError = SO_SUCCESS;
633 int nOptIdx = m_nOptionIdx;
634 while (nTableIdx < 0 && nOptIdx < m_nLastArg) {
635 SOCHAR* pszArg = m_argv[nOptIdx];
636 m_pszOptionArg = NULL;
639 cFirst = PrepareArg(pszArg);
640 if (pszArg[0] == (SOCHAR)
'-') {
642 m_pszOptionArg = FindEquals(pszArg);
643 if (m_pszOptionArg) {
644 *m_pszOptionArg++ = (SOCHAR)
'\0';
647 nTableIdx = LookupOption(pszArg);
653 && pszArg[0] == (SOCHAR)
'-'
655 && pszArg[1] != (SOCHAR)
'-'
658 if (HasFlag(SO_O_SHORTARG)) {
659 m_szShort[1] = pszArg[1];
660 int nIdx = LookupOption(m_szShort);
662 && (m_rgOptions[nIdx].nArgType == SO_REQ_CMB
663 || m_rgOptions[nIdx].nArgType == SO_OPT)) {
664 m_pszOptionArg = &pszArg[2];
672 if (nTableIdx < 0 && HasFlag(SO_O_CLUMP)) {
673 m_pszClump = &pszArg[1];
675 if (nOptIdx > m_nOptionIdx) {
676 ShuffleArg(m_nOptionIdx, nOptIdx - m_nOptionIdx);
686 if (!HasFlag(SO_O_NOERR) && pszArg[0] == (SOCHAR)
'-') {
687 m_pszOptionText = pszArg;
693 if (m_pszOptionArg) {
694 *(--m_pszOptionArg) = (SOCHAR)
'=';
700 if (nOptIdx >= m_nLastArg) {
701 if (nOptIdx > m_nOptionIdx) {
702 ShuffleArg(m_nOptionIdx, nOptIdx - m_nOptionIdx);
709 ESOArgType nArgType = SO_NONE;
711 m_nLastError = (ESOError)nTableIdx;
713 m_nOptionId = m_rgOptions[nTableIdx].nId;
714 m_pszOptionText = m_rgOptions[nTableIdx].pszArg;
717 nArgType = m_rgOptions[nTableIdx].nArgType;
720 if (m_pszOptionArg) {
721 m_nLastError = SO_ARG_INVALID;
726 if (m_pszOptionArg) {
729 if (HasFlag(SO_O_PEDANTIC)) {
730 m_nLastError = SO_ARG_INVALID_TYPE;
737 if (!m_pszOptionArg) {
738 m_nLastError = SO_ARG_MISSING;
754 if (nOptIdx > m_nOptionIdx) {
755 ShuffleArg(m_nOptionIdx, nOptIdx - m_nOptionIdx);
760 if (nArgType == SO_REQ_SEP
762 && m_nLastError == SO_SUCCESS) {
763 SOCHAR** ppArgs = MultiArg(1);
765 m_pszOptionArg = *ppArgs;
772 template <
class SOCHAR>
773 void CSimpleOptTempl<SOCHAR>::Stop()
775 if (m_nNextOption < m_nLastArg) {
776 ShuffleArg(m_nNextOption, m_nLastArg - m_nNextOption);
780 template <
class SOCHAR>
782 CSimpleOptTempl<SOCHAR>::PrepareArg(
783 SOCHAR* a_pszString)
const
790 if (!HasFlag(SO_O_NOSLASH)
791 && a_pszString[0] == (SOCHAR)
'/'
793 && a_pszString[1] != (SOCHAR)
'-') {
794 a_pszString[0] = (SOCHAR)
'-';
798 return a_pszString[0];
801 template <
class SOCHAR>
802 bool CSimpleOptTempl<SOCHAR>::NextClumped()
805 m_szShort[1] = *m_pszClump++;
807 m_pszOptionText = NULL;
808 m_pszOptionArg = NULL;
809 m_nLastError = SO_SUCCESS;
812 int nSavedFlags = m_nFlags;
813 m_nFlags = SO_O_EXACT;
814 int nTableIdx = LookupOption(m_szShort);
815 m_nFlags = nSavedFlags;
819 m_pszOptionText = m_szShort;
820 m_nLastError = (ESOError)nTableIdx;
825 m_pszOptionText = m_rgOptions[nTableIdx].pszArg;
826 ESOArgType nArgType = m_rgOptions[nTableIdx].nArgType;
827 if (nArgType == SO_NONE) {
828 m_nOptionId = m_rgOptions[nTableIdx].nId;
832 if (nArgType == SO_REQ_CMB && *m_pszClump) {
833 m_nOptionId = m_rgOptions[nTableIdx].nId;
834 m_pszOptionArg = m_pszClump;
841 m_nLastError = SO_ARG_MISSING;
853 template <
class SOCHAR>
854 void CSimpleOptTempl<SOCHAR>::ShuffleArg(
858 SOCHAR* staticBuf[SO_STATICBUF];
859 SOCHAR** buf = m_rgShuffleBuf ? m_rgShuffleBuf : staticBuf;
860 int nTail = m_argc - a_nStartIdx - a_nCount;
863 Copy(buf, m_argv + a_nStartIdx, a_nCount);
866 Copy(m_argv + a_nStartIdx, m_argv + a_nStartIdx + a_nCount, nTail);
869 Copy(m_argv + a_nStartIdx + nTail, buf, a_nCount);
872 m_nLastArg -= a_nCount;
877 template <
class SOCHAR>
878 int CSimpleOptTempl<SOCHAR>::LookupOption(
879 const SOCHAR* a_pszOption)
const
882 int nBestMatchLen = 0;
883 int nLastMatchLen = 0;
885 for (
int n = 0; m_rgOptions[n].nId >= 0; ++n) {
888 SO_ASSERT(m_rgOptions[n].pszArg[0] != (SOCHAR)
'/');
890 int nMatchLen = CalcMatch(m_rgOptions[n].pszArg, a_pszOption);
891 if (nMatchLen == -1) {
894 if (nMatchLen > 0 && nMatchLen >= nBestMatchLen) {
895 nLastMatchLen = nBestMatchLen;
896 nBestMatchLen = nMatchLen;
903 if (HasFlag(SO_O_EXACT) || nBestMatch == -1) {
904 return SO_OPT_INVALID;
906 return (nBestMatchLen > nLastMatchLen) ? nBestMatch : SO_OPT_MULTIPLE;
911 template <
class SOCHAR>
912 int CSimpleOptTempl<SOCHAR>::CalcMatch(
913 const SOCHAR* a_pszSource,
914 const SOCHAR* a_pszTest)
const
916 if (!a_pszSource || !a_pszTest) {
921 int nArgType = SO_O_ICASE_LONG;
922 if (a_pszSource[0] !=
'-') {
923 nArgType = SO_O_ICASE_WORD;
924 }
else if (a_pszSource[1] !=
'-' && !a_pszSource[2]) {
925 nArgType = SO_O_ICASE_SHORT;
929 while (*a_pszSource == (SOCHAR)
'-' && *a_pszSource == *a_pszTest) {
933 if (*a_pszSource == (SOCHAR)
'-' || *a_pszTest == (SOCHAR)
'-') {
939 while (*a_pszSource && IsEqual(*a_pszSource, *a_pszTest, nArgType)) {
967 template <
class SOCHAR>
968 bool CSimpleOptTempl<SOCHAR>::IsEqual(
971 int a_nArgType)
const
974 if (m_nFlags & a_nArgType) {
975 if (a_cLeft >=
'A' && a_cLeft <=
'Z')
976 a_cLeft +=
'a' -
'A';
977 if (a_cRight >=
'A' && a_cRight <=
'Z')
978 a_cRight +=
'a' -
'A';
980 return a_cLeft == a_cRight;
985 template <
class SOCHAR>
987 CSimpleOptTempl<SOCHAR>::MultiArg(
991 if (m_nNextOption + a_nCount > m_nLastArg) {
992 m_nLastError = SO_ARG_MISSING;
997 SOCHAR** rgpszArg = &m_argv[m_nNextOption];
1001 if (!HasFlag(SO_O_NOERR)) {
1002 for (
int n = 0; n < a_nCount; ++n) {
1003 SOCHAR ch = PrepareArg(rgpszArg[n]);
1004 if (rgpszArg[n][0] == (SOCHAR)
'-') {
1005 rgpszArg[n][0] = ch;
1006 m_nLastError = SO_ARG_INVALID_DATA;
1009 rgpszArg[n][0] = ch;
1014 m_nNextOption += a_nCount;
1023 typedef CSimpleOptTempl<char> CSimpleOptA;
1026 typedef CSimpleOptTempl<wchar_t> CSimpleOptW;
1028 #if defined(_UNICODE)
1030 #define CSimpleOpt CSimpleOptW
1033 #define CSimpleOpt CSimpleOptA
1036 #endif // INCLUDED_SimpleOpt