toulbar2
SimpleGlob.h
1
83#ifndef INCLUDED_SimpleGlob
84#define INCLUDED_SimpleGlob
85
129enum SG_Flags {
130 SG_GLOB_ERR = 1 << 0,
131 SG_GLOB_MARK = 1 << 1,
132 SG_GLOB_NOSORT = 1 << 2,
133 SG_GLOB_NOCHECK = 1 << 3,
134 SG_GLOB_TILDE = 1 << 4,
135 SG_GLOB_ONLYDIR = 1 << 5,
136 SG_GLOB_ONLYFILE = 1 << 6,
137 SG_GLOB_NODOT = 1 << 7,
138 SG_GLOB_FULLSORT = 1 << 8
139};
140
142enum SG_Error {
143 SG_SUCCESS = 0,
144 SG_ERR_NOMATCH = 1,
145 SG_ERR_MEMORY = -1,
146 SG_ERR_FAILURE = -2
147};
148
149// ---------------------------------------------------------------------------
150// Platform dependent implementations
151
152// if we aren't on Windows and we have ICU available, then enable ICU
153// by default. Define this to 0 to intentially disable it.
154#ifndef SG_HAVE_ICU
155#if !defined(__WIN32__) && defined(USTRING_H)
156#define SG_HAVE_ICU 1
157#else
158#define SG_HAVE_ICU 0
159#endif
160#endif
161
162// don't include this in documentation as it isn't relevant
163#ifndef DOXYGEN
164
165// on Windows we want to use MBCS aware string functions and mimic the
166// Unix glob functionality. On Unix we just use glob.
167#ifdef __WIN32__
168#include <mbstring.h>
169#define sg_strchr ::_mbschr
170#define sg_strrchr ::_mbsrchr
171#define sg_strlen ::_mbslen
172#if __STDC_WANT_SECURE_LIB__
173#define sg_strcpy_s(a, n, b) ::_mbscpy_s(a, n, b)
174#else
175#define sg_strcpy_s(a, n, b) ::_mbscpy(a, b)
176#endif
177#define sg_strcmp ::_mbscmp
178#define sg_strcasecmp ::_mbsicmp
179#define SOCHAR_T unsigned char
180#else
181#include <sys/types.h>
182#include <sys/stat.h>
183#include <glob.h>
184#include <limits.h>
185#define MAX_PATH PATH_MAX
186#define sg_strchr ::strchr
187#define sg_strrchr ::strrchr
188#define sg_strlen ::strlen
189#define sg_strcpy_s(a, n, b) ::strcpy(a, b)
190#define sg_strcmp ::strcmp
191#define sg_strcasecmp ::strcasecmp
192#define SOCHAR_T char
193#endif
194
195#include <stdlib.h>
196#include <string.h>
197#include <wchar.h>
198
199// use assertions to test the input data
200#ifdef _DEBUG
201#ifdef _MSC_VER
202#include <crtdbg.h>
203#define SG_ASSERT(b) _ASSERTE(b)
204#else
205#include <assert.h>
206#define SG_ASSERT(b) assert(b)
207#endif
208#else
209#define SG_ASSERT(b)
210#endif
211
213class SimpleGlobUtil {
214public:
215 static const char* strchr(const char* s, char c)
216 {
217 return (char*)sg_strchr((const SOCHAR_T*)s, c);
218 }
219 static const wchar_t* strchr(const wchar_t* s, wchar_t c)
220 {
221 return ::wcschr(s, c);
222 }
223#if SG_HAVE_ICU
224 static const UChar* strchr(const UChar* s, UChar c)
225 {
226 return ::u_strchr(s, c);
227 }
228#endif
229
230 static const char* strrchr(const char* s, char c)
231 {
232 return (char*)sg_strrchr((const SOCHAR_T*)s, c);
233 }
234 static const wchar_t* strrchr(const wchar_t* s, wchar_t c)
235 {
236 return ::wcsrchr(s, c);
237 }
238#if SG_HAVE_ICU
239 static const UChar* strrchr(const UChar* s, UChar c)
240 {
241 return ::u_strrchr(s, c);
242 }
243#endif
244
245 // Note: char strlen returns number of bytes, not characters
246 static size_t strlen(const char* s) { return ::strlen(s); }
247 static size_t strlen(const wchar_t* s) { return ::wcslen(s); }
248#if SG_HAVE_ICU
249 static size_t strlen(const UChar* s)
250 {
251 return ::u_strlen(s);
252 }
253#endif
254
255 static void strcpy_s(char* dst, size_t n, const char* src)
256 {
257 (void)n;
258 sg_strcpy_s((SOCHAR_T*)dst, n, (const SOCHAR_T*)src);
259 }
260 static void strcpy_s(wchar_t* dst, size_t n, const wchar_t* src)
261 {
262#if __STDC_WANT_SECURE_LIB__
263 ::wcscpy_s(dst, n, src);
264#else
265 (void)n;
266 ::wcscpy(dst, src);
267#endif
268 }
269#if SG_HAVE_ICU
270 static void strcpy_s(UChar* dst, size_t n, const UChar* src)
271 {
272 ::u_strncpy(dst, src, n);
273 }
274#endif
275
276 static int strcmp(const char* s1, const char* s2)
277 {
278 return sg_strcmp((const SOCHAR_T*)s1, (const SOCHAR_T*)s2);
279 }
280 static int strcmp(const wchar_t* s1, const wchar_t* s2)
281 {
282 return ::wcscmp(s1, s2);
283 }
284#if SG_HAVE_ICU
285 static int strcmp(const UChar* s1, const UChar* s2)
286 {
287 return ::u_strcmp(s1, s2);
288 }
289#endif
290
291 static int strcasecmp(const char* s1, const char* s2)
292 {
293 return sg_strcasecmp((const SOCHAR_T*)s1, (const SOCHAR_T*)s2);
294 }
295#if __WIN32__
296 static int strcasecmp(const wchar_t* s1, const wchar_t* s2)
297 {
298 return ::_wcsicmp(s1, s2);
299 }
300#endif // __WIN32__
301#if SG_HAVE_ICU
302 static int strcasecmp(const UChar* s1, const UChar* s2)
303 {
304 return u_strcasecmp(s1, s2, 0);
305 }
306#endif
307};
308
309enum SG_FileType {
310 SG_FILETYPE_INVALID,
311 SG_FILETYPE_FILE,
312 SG_FILETYPE_DIR
313};
314
315#ifdef __WIN32__
316
317#ifndef INVALID_FILE_ATTRIBUTES
318#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
319#endif
320
321#define SG_PATH_CHAR '\\'
322
324template <class SOCHAR>
325struct SimpleGlobBase {
326 SimpleGlobBase()
327 : m_hFind(INVALID_HANDLE_VALUE)
328 {
329 }
330
331 int FindFirstFileS(const char* a_pszFileSpec, unsigned int)
332 {
333 m_hFind = FindFirstFileA(a_pszFileSpec, &m_oFindDataA);
334 if (m_hFind != INVALID_HANDLE_VALUE) {
335 return SG_SUCCESS;
336 }
337 DWORD dwErr = GetLastError();
338 if (dwErr == ERROR_FILE_NOT_FOUND) {
339 return SG_ERR_NOMATCH;
340 }
341 return SG_ERR_FAILURE;
342 }
343 int FindFirstFileS(const wchar_t* a_pszFileSpec, unsigned int)
344 {
345 m_hFind = FindFirstFileW(a_pszFileSpec, &m_oFindDataW);
346 if (m_hFind != INVALID_HANDLE_VALUE) {
347 return SG_SUCCESS;
348 }
349 DWORD dwErr = GetLastError();
350 if (dwErr == ERROR_FILE_NOT_FOUND) {
351 return SG_ERR_NOMATCH;
352 }
353 return SG_ERR_FAILURE;
354 }
355
356 bool FindNextFileS(char)
357 {
358 return FindNextFileA(m_hFind, &m_oFindDataA) != FALSE;
359 }
360 bool FindNextFileS(wchar_t)
361 {
362 return FindNextFileW(m_hFind, &m_oFindDataW) != FALSE;
363 }
364
365 void FindDone()
366 {
367 FindClose(m_hFind);
368 }
369
370 const char* GetFileNameS(char) const
371 {
372 return m_oFindDataA.cFileName;
373 }
374 const wchar_t* GetFileNameS(wchar_t) const
375 {
376 return m_oFindDataW.cFileName;
377 }
378
379 bool IsDirS(char) const
380 {
381 return this->GetFileTypeS(m_oFindDataA.dwFileAttributes) == SG_FILETYPE_DIR;
382 }
383 bool IsDirS(wchar_t) const
384 {
385 return this->GetFileTypeS(m_oFindDataW.dwFileAttributes) == SG_FILETYPE_DIR;
386 }
387
388 SG_FileType GetFileTypeS(const char* a_pszPath)
389 {
390 return this->GetFileTypeS(GetFileAttributesA(a_pszPath));
391 }
392 SG_FileType GetFileTypeS(const wchar_t* a_pszPath)
393 {
394 return this->GetFileTypeS(GetFileAttributesW(a_pszPath));
395 }
396 SG_FileType GetFileTypeS(DWORD a_dwAttribs) const
397 {
398 if (a_dwAttribs == INVALID_FILE_ATTRIBUTES) {
399 return SG_FILETYPE_INVALID;
400 }
401 if (a_dwAttribs & FILE_ATTRIBUTE_DIRECTORY) {
402 return SG_FILETYPE_DIR;
403 }
404 return SG_FILETYPE_FILE;
405 }
406
407private:
408 HANDLE m_hFind;
409 WIN32_FIND_DATAA m_oFindDataA;
410 WIN32_FIND_DATAW m_oFindDataW;
411};
412
413#else // !__WIN32__
414
415#define SG_PATH_CHAR '/'
416
418template <class SOCHAR>
419struct SimpleGlobBase {
420 SimpleGlobBase()
421 {
422 memset(&m_glob, 0, sizeof(m_glob));
423 m_uiCurr = (size_t)-1;
424 }
425
426 ~SimpleGlobBase()
427 {
428 globfree(&m_glob);
429 }
430
431 void FilePrep()
432 {
433 m_bIsDir = false;
434 size_t len = strlen(m_glob.gl_pathv[m_uiCurr]);
435 if (m_glob.gl_pathv[m_uiCurr][len - 1] == '/') {
436 m_bIsDir = true;
437 m_glob.gl_pathv[m_uiCurr][len - 1] = 0;
438 }
439 }
440
441 int FindFirstFileS(const char* a_pszFileSpec, unsigned int a_uiFlags)
442 {
443 int nFlags = GLOB_MARK | GLOB_NOSORT;
444 if (a_uiFlags & SG_GLOB_ERR)
445 nFlags |= GLOB_ERR;
446 if (a_uiFlags & SG_GLOB_TILDE)
447 nFlags |= GLOB_TILDE;
448 int rc = glob(a_pszFileSpec, nFlags, NULL, &m_glob);
449 if (rc == GLOB_NOSPACE)
450 return SG_ERR_MEMORY;
451 if (rc == GLOB_ABORTED)
452 return SG_ERR_FAILURE;
453 if (rc == GLOB_NOMATCH)
454 return SG_ERR_NOMATCH;
455 m_uiCurr = 0;
456 FilePrep();
457 return SG_SUCCESS;
458 }
459
460#if SG_HAVE_ICU
461 int FindFirstFileS(const UChar* a_pszFileSpec, unsigned int a_uiFlags)
462 {
463 char buf[PATH_MAX] = { 0 };
464 UErrorCode status = U_ZERO_ERROR;
465 u_strToUTF8(buf, sizeof(buf), NULL, a_pszFileSpec, -1, &status);
466 if (U_FAILURE(status))
467 return SG_ERR_FAILURE;
468 return this->FindFirstFileS(buf, a_uiFlags);
469 }
470#endif
471
472 bool FindNextFileS(char)
473 {
474 SG_ASSERT(m_uiCurr != (size_t)-1);
475 if (++m_uiCurr >= m_glob.gl_pathc) {
476 return false;
477 }
478 FilePrep();
479 return true;
480 }
481
482#if SG_HAVE_ICU
483 bool FindNextFileS(UChar)
484 {
485 return this->FindNextFileS((char)0);
486 }
487#endif
488
489 void FindDone()
490 {
491 globfree(&m_glob);
492 memset(&m_glob, 0, sizeof(m_glob));
493 m_uiCurr = (size_t)-1;
494 }
495
496 const char* GetFileNameS(char) const
497 {
498 SG_ASSERT(m_uiCurr != (size_t)-1);
499 return m_glob.gl_pathv[m_uiCurr];
500 }
501
502#if SG_HAVE_ICU
503 const UChar* GetFileNameS(UChar) const
504 {
505 const char* pszFile = this->GetFileNameS((char)0);
506 if (!pszFile)
507 return NULL;
508 UErrorCode status = U_ZERO_ERROR;
509 memset(m_szBuf, 0, sizeof(m_szBuf));
510 u_strFromUTF8(m_szBuf, PATH_MAX, NULL, pszFile, -1, &status);
511 if (U_FAILURE(status))
512 return NULL;
513 return m_szBuf;
514 }
515#endif
516
517 bool IsDirS(char) const
518 {
519 SG_ASSERT(m_uiCurr != (size_t)-1);
520 return m_bIsDir;
521 }
522
523#if SG_HAVE_ICU
524 bool IsDirS(UChar) const
525 {
526 return this->IsDirS((char)0);
527 }
528#endif
529
530 SG_FileType GetFileTypeS(const char* a_pszPath) const
531 {
532 struct stat sb;
533 if (0 != stat(a_pszPath, &sb)) {
534 return SG_FILETYPE_INVALID;
535 }
536 if (S_ISDIR(sb.st_mode)) {
537 return SG_FILETYPE_DIR;
538 }
539 if (S_ISREG(sb.st_mode)) {
540 return SG_FILETYPE_FILE;
541 }
542 return SG_FILETYPE_INVALID;
543 }
544
545#if SG_HAVE_ICU
546 SG_FileType GetFileTypeS(const UChar* a_pszPath) const
547 {
548 char buf[PATH_MAX] = { 0 };
549 UErrorCode status = U_ZERO_ERROR;
550 u_strToUTF8(buf, sizeof(buf), NULL, a_pszPath, -1, &status);
551 if (U_FAILURE(status))
552 return SG_FILETYPE_INVALID;
553 return this->GetFileTypeS(buf);
554 }
555#endif
556
557private:
558 glob_t m_glob;
559 size_t m_uiCurr;
560 bool m_bIsDir;
561#if SG_HAVE_ICU
562 mutable UChar m_szBuf[PATH_MAX];
563#endif
564};
565
566#endif // __WIN32__
567
568#endif // DOXYGEN
569
570// ---------------------------------------------------------------------------
571// MAIN TEMPLATE CLASS
572// ---------------------------------------------------------------------------
573
575template <class SOCHAR>
576class CSimpleGlobTempl : private SimpleGlobBase<SOCHAR> {
577public:
586 CSimpleGlobTempl(unsigned int a_uiFlags = 0, int a_nReservedSlots = 0);
587
589 ~CSimpleGlobTempl();
590
603 int Init(unsigned int a_uiFlags = 0, int a_nReservedSlots = 0);
604
618 int Add(const SOCHAR* a_pszFileSpec);
619
634 int Add(int a_nCount, const SOCHAR* const* a_rgpszFileSpec);
635
638 inline int FileCount() const { return m_nArgsLen; }
639
641 inline SOCHAR** Files()
642 {
643 SetArgvArrayType(POINTERS);
644 return m_rgpArgs;
645 }
646
648 inline SOCHAR* File(int n)
649 {
650 SG_ASSERT(n >= 0 && n < m_nArgsLen);
651 return Files()[n];
652 }
653
654private:
655 CSimpleGlobTempl(const CSimpleGlobTempl&); // disabled
656 CSimpleGlobTempl& operator=(const CSimpleGlobTempl&); // disabled
657
663 enum ARG_ARRAY_TYPE { OFFSETS,
664 POINTERS };
665
667 void SetArgvArrayType(ARG_ARRAY_TYPE a_nNewType);
668
670 int AppendName(const SOCHAR* a_pszFileName, bool a_bIsDir);
671
673 bool GrowArgvArray(int a_nNewLen);
674
676 bool GrowStringBuffer(size_t a_uiMinSize);
677
679 static int fileSortCompare(const void* a1, const void* a2);
680
681private:
682 unsigned int m_uiFlags;
683 ARG_ARRAY_TYPE m_nArgArrayType;
684 SOCHAR** m_rgpArgs;
685 int m_nReservedSlots;
686 int m_nArgsSize;
687 int m_nArgsLen;
688 SOCHAR* m_pBuffer;
689 size_t m_uiBufferSize;
690 size_t m_uiBufferLen;
691 SOCHAR m_szPathPrefix[MAX_PATH];
692};
693
694// ---------------------------------------------------------------------------
695// IMPLEMENTATION
696// ---------------------------------------------------------------------------
697
698template <class SOCHAR>
699CSimpleGlobTempl<SOCHAR>::CSimpleGlobTempl(
700 unsigned int a_uiFlags,
701 int a_nReservedSlots)
702{
703 m_rgpArgs = NULL;
704 m_nArgsSize = 0;
705 m_pBuffer = NULL;
706 m_uiBufferSize = 0;
707
708 Init(a_uiFlags, a_nReservedSlots);
709}
710
711template <class SOCHAR>
712CSimpleGlobTempl<SOCHAR>::~CSimpleGlobTempl()
713{
714 if (m_rgpArgs)
715 free(m_rgpArgs);
716 if (m_pBuffer)
717 free(m_pBuffer);
718}
719
720template <class SOCHAR>
721int CSimpleGlobTempl<SOCHAR>::Init(
722 unsigned int a_uiFlags,
723 int a_nReservedSlots)
724{
725 m_nArgArrayType = POINTERS;
726 m_uiFlags = a_uiFlags;
727 m_nArgsLen = a_nReservedSlots;
728 m_nReservedSlots = a_nReservedSlots;
729 m_uiBufferLen = 0;
730
731 if (m_nReservedSlots > 0) {
732 if (!GrowArgvArray(m_nReservedSlots)) {
733 return SG_ERR_MEMORY;
734 }
735 for (int n = 0; n < m_nReservedSlots; ++n) {
736 m_rgpArgs[n] = NULL;
737 }
738 }
739
740 return SG_SUCCESS;
741}
742
743template <class SOCHAR>
744int CSimpleGlobTempl<SOCHAR>::Add(
745 const SOCHAR* a_pszFileSpec)
746{
747#ifdef __WIN32__
748 // Windows FindFirst/FindNext recognizes forward slash as the same as
749 // backward slash and follows the directories. We need to do the same
750 // when calculating the prefix and when we have no wildcards.
751 SOCHAR szFileSpec[MAX_PATH];
752 SimpleGlobUtil::strcpy_s(szFileSpec, MAX_PATH, a_pszFileSpec);
753 const SOCHAR* pszPath = SimpleGlobUtil::strchr(szFileSpec, '/');
754 while (pszPath) {
755 szFileSpec[pszPath - szFileSpec] = SG_PATH_CHAR;
756 pszPath = SimpleGlobUtil::strchr(pszPath + 1, '/');
757 }
758 a_pszFileSpec = szFileSpec;
759#endif
760
761 // if this doesn't contain wildcards then we can just add it directly
762 m_szPathPrefix[0] = 0;
763 if (!SimpleGlobUtil::strchr(a_pszFileSpec, '*') && !SimpleGlobUtil::strchr(a_pszFileSpec, '?')) {
764 SG_FileType nType = this->GetFileTypeS(a_pszFileSpec);
765 if (nType == SG_FILETYPE_INVALID) {
766 if (m_uiFlags & SG_GLOB_NOCHECK) {
767 return AppendName(a_pszFileSpec, false);
768 }
769 return SG_ERR_NOMATCH;
770 }
771 return AppendName(a_pszFileSpec, nType == SG_FILETYPE_DIR);
772 }
773
774#ifdef __WIN32__
775 // Windows doesn't return the directory with the filename, so we need to
776 // extract the path from the search string ourselves and prefix it to the
777 // filename we get back.
778 const SOCHAR* pszFilename = SimpleGlobUtil::strrchr(a_pszFileSpec, SG_PATH_CHAR);
779 if (pszFilename) {
780 SimpleGlobUtil::strcpy_s(m_szPathPrefix, MAX_PATH, a_pszFileSpec);
781 m_szPathPrefix[pszFilename - a_pszFileSpec + 1] = 0;
782 }
783#endif
784
785 // search for the first match on the file
786 int rc = this->FindFirstFileS(a_pszFileSpec, m_uiFlags);
787 if (rc != SG_SUCCESS) {
788 if (rc == SG_ERR_NOMATCH && (m_uiFlags & SG_GLOB_NOCHECK)) {
789 int ok = AppendName(a_pszFileSpec, false);
790 if (ok != SG_SUCCESS)
791 rc = ok;
792 }
793 return rc;
794 }
795
796 // add it and find all subsequent matches
797 int nError, nStartLen = m_nArgsLen;
798 bool bSuccess;
799 do {
800 nError = AppendName(this->GetFileNameS((SOCHAR)0), this->IsDirS((SOCHAR)0));
801 bSuccess = this->FindNextFileS((SOCHAR)0);
802 } while (nError == SG_SUCCESS && bSuccess);
803 SimpleGlobBase<SOCHAR>::FindDone();
804
805 // sort these files if required
806 if (m_nArgsLen > nStartLen && !(m_uiFlags & SG_GLOB_NOSORT)) {
807 if (m_uiFlags & SG_GLOB_FULLSORT) {
808 nStartLen = m_nReservedSlots;
809 }
810 SetArgvArrayType(POINTERS);
811 qsort(
812 m_rgpArgs + nStartLen,
813 m_nArgsLen - nStartLen,
814 sizeof(m_rgpArgs[0]), fileSortCompare);
815 }
816
817 return nError;
818}
819
820template <class SOCHAR>
821int CSimpleGlobTempl<SOCHAR>::Add(
822 int a_nCount,
823 const SOCHAR* const* a_rgpszFileSpec)
824{
825 int nResult;
826 for (int n = 0; n < a_nCount; ++n) {
827 nResult = Add(a_rgpszFileSpec[n]);
828 if (nResult != SG_SUCCESS) {
829 return nResult;
830 }
831 }
832 return SG_SUCCESS;
833}
834
835template <class SOCHAR>
836int CSimpleGlobTempl<SOCHAR>::AppendName(
837 const SOCHAR* a_pszFileName,
838 bool a_bIsDir)
839{
840 // we need the argv array as offsets in case we resize it
841 SetArgvArrayType(OFFSETS);
842
843 // check for special cases which cause us to ignore this entry
844 if ((m_uiFlags & SG_GLOB_ONLYDIR) && !a_bIsDir) {
845 return SG_SUCCESS;
846 }
847 if ((m_uiFlags & SG_GLOB_ONLYFILE) && a_bIsDir) {
848 return SG_SUCCESS;
849 }
850 if ((m_uiFlags & SG_GLOB_NODOT) && a_bIsDir) {
851 if (a_pszFileName[0] == '.') {
852 if (a_pszFileName[1] == '\0') {
853 return SG_SUCCESS;
854 }
855 if (a_pszFileName[1] == '.' && a_pszFileName[2] == '\0') {
856 return SG_SUCCESS;
857 }
858 }
859 }
860
861 // ensure that we have enough room in the argv array
862 if (!GrowArgvArray(m_nArgsLen + 1)) {
863 return SG_ERR_MEMORY;
864 }
865
866 // ensure that we have enough room in the string buffer (+1 for null)
867 size_t uiPrefixLen = SimpleGlobUtil::strlen(m_szPathPrefix);
868 size_t uiLen = uiPrefixLen + SimpleGlobUtil::strlen(a_pszFileName) + 1;
869 if (a_bIsDir && (m_uiFlags & SG_GLOB_MARK) == SG_GLOB_MARK) {
870 ++uiLen; // need space for the backslash
871 }
872 if (!GrowStringBuffer(m_uiBufferLen + uiLen)) {
873 return SG_ERR_MEMORY;
874 }
875
876 // add this entry. m_uiBufferLen is offset from beginning of buffer.
877 m_rgpArgs[m_nArgsLen++] = (SOCHAR*)m_uiBufferLen;
878 SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen,
879 m_uiBufferSize - m_uiBufferLen, m_szPathPrefix);
880 SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen + uiPrefixLen,
881 m_uiBufferSize - m_uiBufferLen - uiPrefixLen, a_pszFileName);
882 m_uiBufferLen += uiLen;
883
884 // add the directory slash if desired
885 if (a_bIsDir && (m_uiFlags & SG_GLOB_MARK) == SG_GLOB_MARK) {
886 const static SOCHAR szDirSlash[] = { SG_PATH_CHAR, 0 };
887 SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen - 2,
888 m_uiBufferSize - (m_uiBufferLen - 2), szDirSlash);
889 }
890
891 return SG_SUCCESS;
892}
893
894template <class SOCHAR>
895void CSimpleGlobTempl<SOCHAR>::SetArgvArrayType(
896 ARG_ARRAY_TYPE a_nNewType)
897{
898 if (m_nArgArrayType == a_nNewType)
899 return;
900 if (a_nNewType == POINTERS) {
901 SG_ASSERT(m_nArgArrayType == OFFSETS);
902 for (int n = 0; n < m_nArgsLen; ++n) {
903 m_rgpArgs[n] = (m_rgpArgs[n] == (SOCHAR*)-1) ? NULL : m_pBuffer + (size_t)m_rgpArgs[n];
904 }
905 } else {
906 SG_ASSERT(a_nNewType == OFFSETS);
907 SG_ASSERT(m_nArgArrayType == POINTERS);
908 for (int n = 0; n < m_nArgsLen; ++n) {
909 m_rgpArgs[n] = (m_rgpArgs[n] == NULL) ? (SOCHAR*)-1 : (SOCHAR*)(m_rgpArgs[n] - m_pBuffer);
910 }
911 }
912 m_nArgArrayType = a_nNewType;
913}
914
915template <class SOCHAR>
916bool CSimpleGlobTempl<SOCHAR>::GrowArgvArray(
917 int a_nNewLen)
918{
919 if (a_nNewLen >= m_nArgsSize) {
920 static const int SG_ARGV_INITIAL_SIZE = 32;
921 int nNewSize = (m_nArgsSize > 0) ? m_nArgsSize * 2 : SG_ARGV_INITIAL_SIZE;
922 while (a_nNewLen >= nNewSize) {
923 nNewSize *= 2;
924 }
925 void* pNewBuffer = realloc(m_rgpArgs, nNewSize * sizeof(SOCHAR*));
926 if (!pNewBuffer)
927 return false;
928 m_nArgsSize = nNewSize;
929 m_rgpArgs = (SOCHAR**)pNewBuffer;
930 }
931 return true;
932}
933
934template <class SOCHAR>
935bool CSimpleGlobTempl<SOCHAR>::GrowStringBuffer(
936 size_t a_uiMinSize)
937{
938 if (a_uiMinSize >= m_uiBufferSize) {
939 static const int SG_BUFFER_INITIAL_SIZE = 1024;
940 size_t uiNewSize = (m_uiBufferSize > 0) ? m_uiBufferSize * 2 : SG_BUFFER_INITIAL_SIZE;
941 while (a_uiMinSize >= uiNewSize) {
942 uiNewSize *= 2;
943 }
944 void* pNewBuffer = realloc(m_pBuffer, uiNewSize * sizeof(SOCHAR));
945 if (!pNewBuffer)
946 return false;
947 m_uiBufferSize = uiNewSize;
948 m_pBuffer = (SOCHAR*)pNewBuffer;
949 }
950 return true;
951}
952
953template <class SOCHAR>
954int CSimpleGlobTempl<SOCHAR>::fileSortCompare(
955 const void* a1,
956 const void* a2)
957{
958 const SOCHAR* s1 = *(const SOCHAR**)a1;
959 const SOCHAR* s2 = *(const SOCHAR**)a2;
960 if (s1 && s2) {
961 return SimpleGlobUtil::strcasecmp(s1, s2);
962 }
963 // NULL sorts first
964 return s1 == s2 ? 0 : (s1 ? 1 : -1);
965}
966
967// ---------------------------------------------------------------------------
968// TYPE DEFINITIONS
969// ---------------------------------------------------------------------------
970
972typedef CSimpleGlobTempl<char> CSimpleGlobA;
973
975typedef CSimpleGlobTempl<wchar_t> CSimpleGlobW;
976
977#if SG_HAVE_ICU
979typedef CSimpleGlobTempl<UChar> CSimpleGlobU;
980#endif
981
982#ifdef _UNICODE
984#if SG_HAVE_ICU
985#define CSimpleGlob CSimpleGlobU
986#else
987#define CSimpleGlob CSimpleGlobW
988#endif
989#else
991#define CSimpleGlob CSimpleGlobA
992#endif
993
994#endif // INCLUDED_SimpleGlob