toulbar2
SimpleGlob.h
1 
83 #ifndef INCLUDED_SimpleGlob
84 #define INCLUDED_SimpleGlob
85 
129 enum 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 
142 enum 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 
213 class SimpleGlobUtil {
214 public:
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 
309 enum 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 
324 template <class SOCHAR>
325 struct 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 
407 private:
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 
418 template <class SOCHAR>
419 struct 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 
557 private:
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 
575 template <class SOCHAR>
576 class CSimpleGlobTempl : private SimpleGlobBase<SOCHAR> {
577 public:
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 
654 private:
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 
681 private:
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 
698 template <class SOCHAR>
699 CSimpleGlobTempl<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 
711 template <class SOCHAR>
712 CSimpleGlobTempl<SOCHAR>::~CSimpleGlobTempl()
713 {
714  if (m_rgpArgs)
715  free(m_rgpArgs);
716  if (m_pBuffer)
717  free(m_pBuffer);
718 }
719 
720 template <class SOCHAR>
721 int 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 
743 template <class SOCHAR>
744 int 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 
820 template <class SOCHAR>
821 int 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 
835 template <class SOCHAR>
836 int 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 
894 template <class SOCHAR>
895 void 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 
915 template <class SOCHAR>
916 bool 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 
934 template <class SOCHAR>
935 bool 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 
953 template <class SOCHAR>
954 int 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 
972 typedef CSimpleGlobTempl<char> CSimpleGlobA;
973 
975 typedef CSimpleGlobTempl<wchar_t> CSimpleGlobW;
976 
977 #if SG_HAVE_ICU
979 typedef 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