SimpleOpt

SimpleGlob.h

Go to the documentation of this file.
00001 
00085 #ifndef INCLUDED_SimpleGlob
00086 #define INCLUDED_SimpleGlob
00087 
00131 enum SG_Flags {
00132     SG_GLOB_ERR         = 1 << 0,
00133     SG_GLOB_MARK        = 1 << 1,
00134     SG_GLOB_NOSORT      = 1 << 2,
00135     SG_GLOB_NOCHECK     = 1 << 3,
00136     SG_GLOB_TILDE       = 1 << 4,
00137     SG_GLOB_ONLYDIR     = 1 << 5,
00138     SG_GLOB_ONLYFILE    = 1 << 6,
00139     SG_GLOB_NODOT       = 1 << 7,
00140     SG_GLOB_FULLSORT    = 1 << 8
00141 };
00142 
00144 enum SG_Error {
00145     SG_SUCCESS          =  0,
00146     SG_ERR_NOMATCH      =  1,
00147     SG_ERR_MEMORY       = -1,
00148     SG_ERR_FAILURE      = -2
00149 };
00150 
00151 // ---------------------------------------------------------------------------
00152 // Platform dependent implementations
00153 
00154 // if we aren't on Windows and we have ICU available, then enable ICU
00155 // by default. Define this to 0 to intentially disable it.
00156 #ifndef SG_HAVE_ICU
00157 # if !defined(_WIN32) && defined(USTRING_H)
00158 #   define SG_HAVE_ICU 1
00159 # else
00160 #   define SG_HAVE_ICU 0
00161 # endif
00162 #endif
00163 
00164 // don't include this in documentation as it isn't relevant
00165 #ifndef DOXYGEN
00166 
00167 // on Windows we want to use MBCS aware string functions and mimic the
00168 // Unix glob functionality. On Unix we just use glob.
00169 #ifdef _WIN32
00170 # include <mbstring.h>
00171 # define sg_strchr          ::_mbschr
00172 # define sg_strrchr         ::_mbsrchr
00173 # define sg_strlen          ::_mbslen
00174 # if __STDC_WANT_SECURE_LIB__
00175 #  define sg_strcpy_s(a,n,b) ::_mbscpy_s(a,n,b)
00176 # else
00177 #  define sg_strcpy_s(a,n,b) ::_mbscpy(a,b)
00178 # endif
00179 # define sg_strcmp          ::_mbscmp
00180 # define sg_strcasecmp      ::_mbsicmp
00181 # define SOCHAR_T           unsigned char
00182 #else
00183 # include <sys/types.h>
00184 # include <sys/stat.h>
00185 # include <glob.h>
00186 # include <limits.h>
00187 # define MAX_PATH           PATH_MAX
00188 # define sg_strchr          ::strchr
00189 # define sg_strrchr         ::strrchr
00190 # define sg_strlen          ::strlen
00191 # define sg_strcpy_s(a,n,b) ::strcpy(a,b)
00192 # define sg_strcmp          ::strcmp
00193 # define sg_strcasecmp      ::strcasecmp
00194 # define SOCHAR_T           char
00195 #endif
00196 
00197 #include <stdlib.h>
00198 #include <string.h>
00199 #include <wchar.h>
00200 
00201 // use assertions to test the input data
00202 #ifdef _DEBUG
00203 # ifdef _MSC_VER
00204 #  include <crtdbg.h>
00205 #  define SG_ASSERT(b)    _ASSERTE(b)
00206 # else
00207 #  include <assert.h>
00208 #  define SG_ASSERT(b)    assert(b)
00209 # endif
00210 #else
00211 # define SG_ASSERT(b)
00212 #endif
00213 
00215 class SimpleGlobUtil
00216 {
00217 public:
00218     static const char * strchr(const char *s, char c) {
00219         return (char *) sg_strchr((const SOCHAR_T *)s, c);
00220     }
00221     static const wchar_t * strchr(const wchar_t *s, wchar_t c) {
00222         return ::wcschr(s, c);
00223     }
00224 #if SG_HAVE_ICU
00225     static const UChar * strchr(const UChar *s, UChar c) {
00226         return ::u_strchr(s, c);
00227     }
00228 #endif
00229 
00230     static const char * strrchr(const char *s, char c) {
00231         return (char *) sg_strrchr((const SOCHAR_T *)s, c);
00232     }
00233     static const wchar_t * strrchr(const wchar_t *s, wchar_t c) {
00234         return ::wcsrchr(s, c);
00235     }
00236 #if SG_HAVE_ICU
00237     static const UChar * strrchr(const UChar *s, UChar c) {
00238         return ::u_strrchr(s, c);
00239     }
00240 #endif
00241 
00242     // Note: char strlen returns number of bytes, not characters
00243     static size_t strlen(const char *s) { return ::strlen(s); }
00244     static size_t strlen(const wchar_t *s) { return ::wcslen(s); }
00245 #if SG_HAVE_ICU
00246     static size_t strlen(const UChar *s) { return ::u_strlen(s); }
00247 #endif
00248 
00249     static void strcpy_s(char *dst, size_t n, const char *src)  {
00250         (void) n;
00251         sg_strcpy_s((SOCHAR_T *)dst, n, (const SOCHAR_T *)src);
00252     }
00253     static void strcpy_s(wchar_t *dst, size_t n, const wchar_t *src) {
00254 # if __STDC_WANT_SECURE_LIB__
00255         ::wcscpy_s(dst, n, src);
00256 #else
00257         (void) n;
00258         ::wcscpy(dst, src);
00259 #endif
00260     }
00261 #if SG_HAVE_ICU
00262     static void strcpy_s(UChar *dst, size_t n, const UChar *src)  {
00263         ::u_strncpy(dst, src, n);
00264     }
00265 #endif
00266 
00267     static int strcmp(const char *s1, const char *s2) {
00268         return sg_strcmp((const SOCHAR_T *)s1, (const SOCHAR_T *)s2);
00269     }
00270     static int strcmp(const wchar_t *s1, const wchar_t *s2) {
00271         return ::wcscmp(s1, s2);
00272     }
00273 #if SG_HAVE_ICU
00274     static int strcmp(const UChar *s1, const UChar *s2) {
00275         return ::u_strcmp(s1, s2);
00276     }
00277 #endif
00278 
00279     static int strcasecmp(const char *s1, const char *s2) {
00280         return sg_strcasecmp((const SOCHAR_T *)s1, (const SOCHAR_T *)s2);
00281     }
00282 #if _WIN32
00283     static int strcasecmp(const wchar_t *s1, const wchar_t *s2) {
00284         return ::_wcsicmp(s1, s2);
00285     }
00286 #endif // _WIN32
00287 #if SG_HAVE_ICU
00288     static int strcasecmp(const UChar *s1, const UChar *s2) {
00289         return u_strcasecmp(s1, s2, 0);
00290     }
00291 #endif
00292 };
00293 
00294 enum SG_FileType {
00295     SG_FILETYPE_INVALID,
00296     SG_FILETYPE_FILE,
00297     SG_FILETYPE_DIR
00298 };
00299 
00300 #ifdef _WIN32
00301 
00302 #ifndef INVALID_FILE_ATTRIBUTES
00303 # define INVALID_FILE_ATTRIBUTES    ((DWORD)-1)
00304 #endif
00305 
00306 #define SG_PATH_CHAR    '\\'
00307 
00309 template<class SOCHAR>
00310 struct SimpleGlobBase
00311 {
00312     SimpleGlobBase() : m_hFind(INVALID_HANDLE_VALUE) { }
00313 
00314     int FindFirstFileS(const char * a_pszFileSpec, unsigned int) {
00315         m_hFind = FindFirstFileA(a_pszFileSpec, &m_oFindDataA);
00316         if (m_hFind != INVALID_HANDLE_VALUE) {
00317             return SG_SUCCESS;
00318         }
00319         DWORD dwErr = GetLastError();
00320         if (dwErr == ERROR_FILE_NOT_FOUND) {
00321             return SG_ERR_NOMATCH;
00322         }
00323         return SG_ERR_FAILURE;
00324     }
00325     int FindFirstFileS(const wchar_t * a_pszFileSpec, unsigned int) {
00326         m_hFind = FindFirstFileW(a_pszFileSpec, &m_oFindDataW);
00327         if (m_hFind != INVALID_HANDLE_VALUE) {
00328             return SG_SUCCESS;
00329         }
00330         DWORD dwErr = GetLastError();
00331         if (dwErr == ERROR_FILE_NOT_FOUND) {
00332             return SG_ERR_NOMATCH;
00333         }
00334         return SG_ERR_FAILURE;
00335     }
00336 
00337     bool FindNextFileS(char) {
00338         return FindNextFileA(m_hFind, &m_oFindDataA) != FALSE;
00339     }
00340     bool FindNextFileS(wchar_t) {
00341         return FindNextFileW(m_hFind, &m_oFindDataW) != FALSE;
00342     }
00343 
00344     void FindDone() {
00345         FindClose(m_hFind);
00346     }
00347 
00348     const char * GetFileNameS(char) const {
00349         return m_oFindDataA.cFileName;
00350     }
00351     const wchar_t * GetFileNameS(wchar_t) const {
00352         return m_oFindDataW.cFileName;
00353     }
00354 
00355     bool IsDirS(char) const {
00356         return GetFileTypeS(m_oFindDataA.dwFileAttributes) == SG_FILETYPE_DIR;
00357     }
00358     bool IsDirS(wchar_t) const {
00359         return GetFileTypeS(m_oFindDataW.dwFileAttributes) == SG_FILETYPE_DIR;
00360     }
00361 
00362     SG_FileType GetFileTypeS(const char * a_pszPath) {
00363         return GetFileTypeS(GetFileAttributesA(a_pszPath));
00364     }
00365     SG_FileType GetFileTypeS(const wchar_t * a_pszPath)  {
00366         return GetFileTypeS(GetFileAttributesW(a_pszPath));
00367     }
00368     SG_FileType GetFileTypeS(DWORD a_dwAttribs) const {
00369         if (a_dwAttribs == INVALID_FILE_ATTRIBUTES) {
00370             return SG_FILETYPE_INVALID;
00371         }
00372         if (a_dwAttribs & FILE_ATTRIBUTE_DIRECTORY) {
00373             return SG_FILETYPE_DIR;
00374         }
00375         return SG_FILETYPE_FILE;
00376     }
00377 
00378 private:
00379     HANDLE              m_hFind;
00380     WIN32_FIND_DATAA    m_oFindDataA;
00381     WIN32_FIND_DATAW    m_oFindDataW;
00382 };
00383 
00384 #else // !_WIN32
00385 
00386 #define SG_PATH_CHAR    '/'
00387 
00389 template<class SOCHAR>
00390 struct SimpleGlobBase
00391 {
00392     SimpleGlobBase() {
00393         memset(&m_glob, 0, sizeof(m_glob));
00394         m_uiCurr = (size_t)-1;
00395     }
00396 
00397     ~SimpleGlobBase() {
00398         globfree(&m_glob);
00399     }
00400 
00401     void FilePrep() {
00402         m_bIsDir = false;
00403         size_t len = strlen(m_glob.gl_pathv[m_uiCurr]);
00404         if (m_glob.gl_pathv[m_uiCurr][len-1] == '/') {
00405             m_bIsDir = true;
00406             m_glob.gl_pathv[m_uiCurr][len-1] = 0;
00407         }
00408     }
00409 
00410     int FindFirstFileS(const char * a_pszFileSpec, unsigned int a_uiFlags) {
00411         int nFlags = GLOB_MARK | GLOB_NOSORT;
00412         if (a_uiFlags & SG_GLOB_ERR)    nFlags |= GLOB_ERR;
00413         if (a_uiFlags & SG_GLOB_TILDE)  nFlags |= GLOB_TILDE;
00414         int rc = glob(a_pszFileSpec, nFlags, NULL, &m_glob);
00415         if (rc == GLOB_NOSPACE) return SG_ERR_MEMORY;
00416         if (rc == GLOB_ABORTED) return SG_ERR_FAILURE;
00417         if (rc == GLOB_NOMATCH) return SG_ERR_NOMATCH;
00418         m_uiCurr = 0;
00419         FilePrep();
00420         return SG_SUCCESS;
00421     }
00422 
00423 #if SG_HAVE_ICU
00424     int FindFirstFileS(const UChar * a_pszFileSpec, unsigned int a_uiFlags) {
00425         char buf[PATH_MAX] = { 0 };
00426         UErrorCode status = U_ZERO_ERROR;
00427         u_strToUTF8(buf, sizeof(buf), NULL, a_pszFileSpec, -1, &status);
00428         if (U_FAILURE(status)) return SG_ERR_FAILURE;
00429         return FindFirstFileS(buf, a_uiFlags);
00430     }
00431 #endif
00432 
00433     bool FindNextFileS(char) {
00434         SG_ASSERT(m_uiCurr != (size_t)-1);
00435         if (++m_uiCurr >= m_glob.gl_pathc) {
00436             return false;
00437         }
00438         FilePrep();
00439         return true;
00440     }
00441 
00442 #if SG_HAVE_ICU
00443     bool FindNextFileS(UChar) {
00444         return FindNextFileS((char)0);
00445     }
00446 #endif
00447 
00448     void FindDone() {
00449         globfree(&m_glob);
00450         memset(&m_glob, 0, sizeof(m_glob));
00451         m_uiCurr = (size_t)-1;
00452     }
00453 
00454     const char * GetFileNameS(char) const {
00455         SG_ASSERT(m_uiCurr != (size_t)-1);
00456         return m_glob.gl_pathv[m_uiCurr];
00457     }
00458 
00459 #if SG_HAVE_ICU
00460     const UChar * GetFileNameS(UChar) const {
00461         const char * pszFile = GetFileNameS((char)0);
00462         if (!pszFile) return NULL;
00463         UErrorCode status = U_ZERO_ERROR;
00464         memset(m_szBuf, 0, sizeof(m_szBuf));
00465         u_strFromUTF8(m_szBuf, PATH_MAX, NULL, pszFile, -1, &status);
00466         if (U_FAILURE(status)) return NULL;
00467         return m_szBuf;
00468     }
00469 #endif
00470 
00471     bool IsDirS(char) const {
00472         SG_ASSERT(m_uiCurr != (size_t)-1);
00473         return m_bIsDir;
00474     }
00475 
00476 #if SG_HAVE_ICU
00477     bool IsDirS(UChar) const {
00478         return IsDirS((char)0);
00479     }
00480 #endif
00481 
00482     SG_FileType GetFileTypeS(const char * a_pszPath) const {
00483         struct stat sb;
00484         if (0 != stat(a_pszPath, &sb)) {
00485             return SG_FILETYPE_INVALID;
00486         }
00487         if (S_ISDIR(sb.st_mode)) {
00488             return SG_FILETYPE_DIR;
00489         }
00490         if (S_ISREG(sb.st_mode)) {
00491             return SG_FILETYPE_FILE;
00492         }
00493         return SG_FILETYPE_INVALID;
00494     }
00495 
00496 #if SG_HAVE_ICU
00497     SG_FileType GetFileTypeS(const UChar * a_pszPath) const {
00498         char buf[PATH_MAX] = { 0 };
00499         UErrorCode status = U_ZERO_ERROR;
00500         u_strToUTF8(buf, sizeof(buf), NULL, a_pszPath, -1, &status);
00501         if (U_FAILURE(status)) return SG_FILETYPE_INVALID;
00502         return GetFileTypeS(buf);
00503     }
00504 #endif
00505 
00506 private:
00507     glob_t  m_glob;
00508     size_t  m_uiCurr;
00509     bool    m_bIsDir;
00510 #if SG_HAVE_ICU
00511     mutable UChar m_szBuf[PATH_MAX];
00512 #endif
00513 };
00514 
00515 #endif // _WIN32
00516 
00517 #endif // DOXYGEN
00518 
00519 // ---------------------------------------------------------------------------
00520 //                              MAIN TEMPLATE CLASS
00521 // ---------------------------------------------------------------------------
00522 
00524 template<class SOCHAR>
00525 class CSimpleGlobTempl : private SimpleGlobBase<SOCHAR>
00526 {
00527 public:
00536     CSimpleGlobTempl(unsigned int a_uiFlags = 0, int a_nReservedSlots = 0);
00537 
00539     ~CSimpleGlobTempl();
00540 
00553     int Init(unsigned int a_uiFlags = 0, int a_nReservedSlots = 0);
00554 
00568     int Add(const SOCHAR *a_pszFileSpec);
00569 
00584     int Add(int a_nCount, const SOCHAR * const * a_rgpszFileSpec);
00585 
00588     inline int FileCount() const { return m_nArgsLen; }
00589 
00591     inline SOCHAR ** Files() {
00592         SetArgvArrayType(POINTERS);
00593         return m_rgpArgs;
00594     }
00595 
00597     inline SOCHAR * File(int n) {
00598         SG_ASSERT(n >= 0 && n < m_nArgsLen);
00599         return Files()[n];
00600     }
00601 
00602 private:
00603     CSimpleGlobTempl(const CSimpleGlobTempl &); // disabled
00604     CSimpleGlobTempl & operator=(const CSimpleGlobTempl &); // disabled
00605 
00611     enum ARG_ARRAY_TYPE { OFFSETS, POINTERS };
00612 
00614     void SetArgvArrayType(ARG_ARRAY_TYPE a_nNewType);
00615 
00617     int AppendName(const SOCHAR *a_pszFileName, bool a_bIsDir);
00618 
00620     bool GrowArgvArray(int a_nNewLen);
00621 
00623     bool GrowStringBuffer(size_t a_uiMinSize);
00624 
00626     static int fileSortCompare(const void *a1, const void *a2);
00627 
00628 private:
00629     unsigned int        m_uiFlags;
00630     ARG_ARRAY_TYPE      m_nArgArrayType;    
00631     SOCHAR **           m_rgpArgs;          
00632     int                 m_nReservedSlots;   
00633     int                 m_nArgsSize;        
00634     int                 m_nArgsLen;         
00635     SOCHAR *            m_pBuffer;          
00636     size_t              m_uiBufferSize;     
00637     size_t              m_uiBufferLen;      
00638     SOCHAR              m_szPathPrefix[MAX_PATH]; 
00639 };
00640 
00641 // ---------------------------------------------------------------------------
00642 //                                  IMPLEMENTATION
00643 // ---------------------------------------------------------------------------
00644 
00645 template<class SOCHAR>
00646 CSimpleGlobTempl<SOCHAR>::CSimpleGlobTempl(
00647     unsigned int    a_uiFlags,
00648     int             a_nReservedSlots
00649     )
00650 {
00651     m_rgpArgs           = NULL;
00652     m_nArgsSize         = 0;
00653     m_pBuffer           = NULL;
00654     m_uiBufferSize      = 0;
00655 
00656     Init(a_uiFlags, a_nReservedSlots);
00657 }
00658 
00659 template<class SOCHAR>
00660 CSimpleGlobTempl<SOCHAR>::~CSimpleGlobTempl()
00661 {
00662     if (m_rgpArgs) free(m_rgpArgs);
00663     if (m_pBuffer) free(m_pBuffer);
00664 }
00665 
00666 template<class SOCHAR>
00667 int
00668 CSimpleGlobTempl<SOCHAR>::Init(
00669     unsigned int    a_uiFlags,
00670     int             a_nReservedSlots
00671     )
00672 {
00673     m_nArgArrayType     = POINTERS;
00674     m_uiFlags           = a_uiFlags;
00675     m_nArgsLen          = a_nReservedSlots;
00676     m_nReservedSlots    = a_nReservedSlots;
00677     m_uiBufferLen       = 0;
00678 
00679     if (m_nReservedSlots > 0) {
00680         if (!GrowArgvArray(m_nReservedSlots)) {
00681             return SG_ERR_MEMORY;
00682         }
00683         for (int n = 0; n < m_nReservedSlots; ++n) {
00684             m_rgpArgs[n] = NULL;
00685         }
00686     }
00687 
00688     return SG_SUCCESS;
00689 }
00690 
00691 template<class SOCHAR>
00692 int
00693 CSimpleGlobTempl<SOCHAR>::Add(
00694     const SOCHAR *a_pszFileSpec
00695     )
00696 {
00697 #ifdef _WIN32
00698     // Windows FindFirst/FindNext recognizes forward slash as the same as 
00699     // backward slash and follows the directories. We need to do the same 
00700     // when calculating the prefix and when we have no wildcards.
00701     SOCHAR szFileSpec[MAX_PATH];
00702     SimpleGlobUtil::strcpy_s(szFileSpec, MAX_PATH, a_pszFileSpec);
00703     const SOCHAR * pszPath = SimpleGlobUtil::strchr(szFileSpec, '/');
00704     while (pszPath) {
00705         szFileSpec[pszPath - szFileSpec] = SG_PATH_CHAR;
00706         pszPath = SimpleGlobUtil::strchr(pszPath + 1, '/');
00707     }
00708     a_pszFileSpec = szFileSpec;
00709 #endif
00710 
00711     // if this doesn't contain wildcards then we can just add it directly
00712     m_szPathPrefix[0] = 0;
00713     if (!SimpleGlobUtil::strchr(a_pszFileSpec, '*') &&
00714         !SimpleGlobUtil::strchr(a_pszFileSpec, '?'))
00715     {
00716         SG_FileType nType = GetFileTypeS(a_pszFileSpec);
00717         if (nType == SG_FILETYPE_INVALID) {
00718             if (m_uiFlags & SG_GLOB_NOCHECK) {
00719                 return AppendName(a_pszFileSpec, false);
00720             }
00721             return SG_ERR_NOMATCH;
00722         }
00723         return AppendName(a_pszFileSpec, nType == SG_FILETYPE_DIR);
00724     }
00725 
00726 #ifdef _WIN32
00727     // Windows doesn't return the directory with the filename, so we need to 
00728     // extract the path from the search string ourselves and prefix it to the 
00729     // filename we get back.
00730     const SOCHAR * pszFilename = 
00731         SimpleGlobUtil::strrchr(a_pszFileSpec, SG_PATH_CHAR);
00732     if (pszFilename) {
00733         SimpleGlobUtil::strcpy_s(m_szPathPrefix, MAX_PATH, a_pszFileSpec);
00734         m_szPathPrefix[pszFilename - a_pszFileSpec + 1] = 0;
00735     }
00736 #endif
00737 
00738     // search for the first match on the file
00739     int rc = FindFirstFileS(a_pszFileSpec, m_uiFlags);
00740     if (rc != SG_SUCCESS) {
00741         if (rc == SG_ERR_NOMATCH && (m_uiFlags & SG_GLOB_NOCHECK)) {
00742             int ok = AppendName(a_pszFileSpec, false);
00743             if (ok != SG_SUCCESS) rc = ok;
00744         }
00745         return rc;
00746     }
00747 
00748     // add it and find all subsequent matches
00749     int nError, nStartLen = m_nArgsLen;
00750     bool bSuccess;
00751     do {
00752         nError = AppendName(GetFileNameS((SOCHAR)0), IsDirS((SOCHAR)0));
00753         bSuccess = FindNextFileS((SOCHAR)0);
00754     }
00755     while (nError == SG_SUCCESS && bSuccess);
00756     SimpleGlobBase<SOCHAR>::FindDone();
00757 
00758     // sort these files if required
00759     if (m_nArgsLen > nStartLen && !(m_uiFlags & SG_GLOB_NOSORT)) {
00760         if (m_uiFlags & SG_GLOB_FULLSORT) {
00761             nStartLen = m_nReservedSlots;
00762         }
00763         SetArgvArrayType(POINTERS);
00764         qsort(
00765             m_rgpArgs + nStartLen,
00766             m_nArgsLen - nStartLen,
00767             sizeof(m_rgpArgs[0]), fileSortCompare);
00768     }
00769 
00770     return nError;
00771 }
00772 
00773 template<class SOCHAR>
00774 int
00775 CSimpleGlobTempl<SOCHAR>::Add(
00776     int                     a_nCount,
00777     const SOCHAR * const *  a_rgpszFileSpec
00778     )
00779 {
00780     int nResult;
00781     for (int n = 0; n < a_nCount; ++n) {
00782         nResult = Add(a_rgpszFileSpec[n]);
00783         if (nResult != SG_SUCCESS) {
00784             return nResult;
00785         }
00786     }
00787     return SG_SUCCESS;
00788 }
00789 
00790 template<class SOCHAR>
00791 int
00792 CSimpleGlobTempl<SOCHAR>::AppendName(
00793     const SOCHAR *  a_pszFileName,
00794     bool            a_bIsDir
00795     )
00796 {
00797     // we need the argv array as offsets in case we resize it
00798     SetArgvArrayType(OFFSETS);
00799 
00800     // check for special cases which cause us to ignore this entry
00801     if ((m_uiFlags & SG_GLOB_ONLYDIR) && !a_bIsDir) {
00802         return SG_SUCCESS;
00803     }
00804     if ((m_uiFlags & SG_GLOB_ONLYFILE) && a_bIsDir) {
00805         return SG_SUCCESS;
00806     }
00807     if ((m_uiFlags & SG_GLOB_NODOT) && a_bIsDir) {
00808         if (a_pszFileName[0] == '.') {
00809             if (a_pszFileName[1] == '\0') {
00810                 return SG_SUCCESS;
00811             }
00812             if (a_pszFileName[1] == '.' && a_pszFileName[2] == '\0') {
00813                 return SG_SUCCESS;
00814             }
00815         }
00816     }
00817 
00818     // ensure that we have enough room in the argv array
00819     if (!GrowArgvArray(m_nArgsLen + 1)) {
00820         return SG_ERR_MEMORY;
00821     }
00822 
00823     // ensure that we have enough room in the string buffer (+1 for null)
00824     size_t uiPrefixLen = SimpleGlobUtil::strlen(m_szPathPrefix);
00825     size_t uiLen = uiPrefixLen + SimpleGlobUtil::strlen(a_pszFileName) + 1; 
00826     if (a_bIsDir && (m_uiFlags & SG_GLOB_MARK) == SG_GLOB_MARK) {
00827         ++uiLen;    // need space for the backslash
00828     }
00829     if (!GrowStringBuffer(m_uiBufferLen + uiLen)) {
00830         return SG_ERR_MEMORY;
00831     }
00832 
00833     // add this entry. m_uiBufferLen is offset from beginning of buffer.
00834     m_rgpArgs[m_nArgsLen++] = (SOCHAR*)m_uiBufferLen;
00835     SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen,
00836         m_uiBufferSize - m_uiBufferLen, m_szPathPrefix);
00837     SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen + uiPrefixLen,
00838         m_uiBufferSize - m_uiBufferLen - uiPrefixLen, a_pszFileName);
00839     m_uiBufferLen += uiLen;
00840 
00841     // add the directory slash if desired
00842     if (a_bIsDir && (m_uiFlags & SG_GLOB_MARK) == SG_GLOB_MARK) {
00843         const static SOCHAR szDirSlash[] = { SG_PATH_CHAR, 0 };
00844         SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen - 2,
00845             m_uiBufferSize - (m_uiBufferLen - 2), szDirSlash);
00846     }
00847 
00848     return SG_SUCCESS;
00849 }
00850 
00851 template<class SOCHAR>
00852 void
00853 CSimpleGlobTempl<SOCHAR>::SetArgvArrayType(
00854     ARG_ARRAY_TYPE  a_nNewType
00855     )
00856 {
00857     if (m_nArgArrayType == a_nNewType) return;
00858     if (a_nNewType == POINTERS) {
00859         SG_ASSERT(m_nArgArrayType == OFFSETS);
00860         for (int n = 0; n < m_nArgsLen; ++n) {
00861             m_rgpArgs[n] = (m_rgpArgs[n] == (SOCHAR*)-1) ?
00862                 NULL : m_pBuffer + (size_t) m_rgpArgs[n];
00863         }
00864     }
00865     else {
00866         SG_ASSERT(a_nNewType == OFFSETS);
00867         SG_ASSERT(m_nArgArrayType == POINTERS);
00868         for (int n = 0; n < m_nArgsLen; ++n) {
00869             m_rgpArgs[n] = (m_rgpArgs[n] == NULL) ?
00870                 (SOCHAR*) -1 : (SOCHAR*) (m_rgpArgs[n] - m_pBuffer);
00871         }
00872     }
00873     m_nArgArrayType = a_nNewType;
00874 }
00875 
00876 template<class SOCHAR>
00877 bool
00878 CSimpleGlobTempl<SOCHAR>::GrowArgvArray(
00879     int a_nNewLen
00880     )
00881 {
00882     if (a_nNewLen >= m_nArgsSize) {
00883         static const int SG_ARGV_INITIAL_SIZE = 32;
00884         int nNewSize = (m_nArgsSize > 0) ? 
00885             m_nArgsSize * 2 : SG_ARGV_INITIAL_SIZE;
00886         while (a_nNewLen >= nNewSize) {
00887             nNewSize *= 2;
00888         }
00889         void * pNewBuffer = realloc(m_rgpArgs, nNewSize * sizeof(SOCHAR*));
00890         if (!pNewBuffer) return false;
00891         m_nArgsSize = nNewSize;
00892         m_rgpArgs = (SOCHAR**) pNewBuffer;
00893     }
00894     return true;
00895 }
00896 
00897 template<class SOCHAR>
00898 bool
00899 CSimpleGlobTempl<SOCHAR>::GrowStringBuffer(
00900     size_t a_uiMinSize
00901     )
00902 {
00903     if (a_uiMinSize >= m_uiBufferSize) {
00904         static const int SG_BUFFER_INITIAL_SIZE = 1024;
00905         size_t uiNewSize = (m_uiBufferSize > 0) ? 
00906             m_uiBufferSize * 2 : SG_BUFFER_INITIAL_SIZE;
00907         while (a_uiMinSize >= uiNewSize) {
00908             uiNewSize *= 2;
00909         }
00910         void * pNewBuffer = realloc(m_pBuffer, uiNewSize * sizeof(SOCHAR));
00911         if (!pNewBuffer) return false;
00912         m_uiBufferSize = uiNewSize;
00913         m_pBuffer = (SOCHAR*) pNewBuffer;
00914     }
00915     return true;
00916 }
00917 
00918 template<class SOCHAR>
00919 int
00920 CSimpleGlobTempl<SOCHAR>::fileSortCompare(
00921     const void *a1,
00922     const void *a2
00923     )
00924 {
00925     const SOCHAR * s1 = *(const SOCHAR **)a1;
00926     const SOCHAR * s2 = *(const SOCHAR **)a2;
00927     if (s1 && s2) {
00928         return SimpleGlobUtil::strcasecmp(s1, s2);
00929     }
00930     // NULL sorts first
00931     return s1 == s2 ? 0 : (s1 ? 1 : -1);
00932 }
00933 
00934 // ---------------------------------------------------------------------------
00935 //                                  TYPE DEFINITIONS
00936 // ---------------------------------------------------------------------------
00937 
00939 typedef CSimpleGlobTempl<char>    CSimpleGlobA;
00940 
00942 typedef CSimpleGlobTempl<wchar_t> CSimpleGlobW; 
00943 
00944 #if SG_HAVE_ICU
00945 
00946 typedef CSimpleGlobTempl<UChar> CSimpleGlobU; 
00947 #endif
00948 
00949 #ifdef _UNICODE
00950 
00951 # if SG_HAVE_ICU
00952 #  define CSimpleGlob CSimpleGlobU
00953 # else
00954 #  define CSimpleGlob CSimpleGlobW   
00955 # endif
00956 #else
00957 
00958 # define CSimpleGlob CSimpleGlobA   
00959 #endif
00960 
00961 #endif // INCLUDED_SimpleGlob