RTS API Documentation  1.10.11
SimpleGlob.h
Go to the documentation of this file.
1 /*! @file SimpleGlob.h
2 
3  @version 2.8
4 
5  @brief A cross-platform file globbing library providing the ability to
6  expand wildcards in command-line arguments to a list of all matching files.
7  It is designed explicitly to be portable to any platform and has been tested on
8  Windows and Linux. See CSimpleGlobTempl for the class definition.
9 
10  @section features FEATURES
11 
12  - MIT Licence allows free use in all software (including GPL and commercial)
13  - multi-platform (Windows 95/98/ME/NT/2K/XP, Linux, Unix)
14  - supports most of the standard linux glob() options
15  - recognition of a forward paths as equivalent to a backward slash on Windows.
16  e.g. "c:/path/foo*" is equivalent to "c:\path\foo*".
17  - implemented with only a single C++ header file
18  - char, wchar_t and Windows TCHAR in the same program
19  - complete working examples included
20  - compiles cleanly at warning level 4 (Windows/VC.NET 2003), warning level
21  3 (Windows/VC6) and -Wall (Linux/gcc)
22 
23  @section usage USAGE
24 
25  The SimpleGlob class is used by following these steps:
26 
27  <ol>
28  <li> Include the SimpleGlob.h header file
29 
30  <pre>
31  \#include "SimpleGlob.h"
32  </pre>
33 
34  <li> Instantiate a CSimpleGlob object supplying the appropriate flags.
35 
36  <pre>
37  @link CSimpleGlobTempl CSimpleGlob @endlink glob(FLAGS);
38  </pre>
39 
40  <li> Add all file specifications to the glob class.
41 
42  <pre>
43  glob.Add("file*");
44  glob.Add(argc, argv);
45  </pre>
46 
47  <li> Process all files with File(), Files() and FileCount()
48 
49  <pre>
50  for (int n = 0; n < glob.FileCount(); ++n) {
51  ProcessFile(glob.File(n));
52  }
53  </pre>
54 
55  </ol>
56 
57  @section licence MIT LICENCE
58 
59  The licence text below is the boilerplate "MIT Licence" used from:
60  http://www.opensource.org/licenses/mit-license.php
61 
62  Copyright (c) 2006-2007, Brodie Thiesfield
63 
64  Permission is hereby granted, free of charge, to any person obtaining a copy
65  of this software and associated documentation files (the "Software"), to deal
66  in the Software without restriction, including without limitation the rights
67  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
68  copies of the Software, and to permit persons to whom the Software is furnished
69  to do so, subject to the following conditions:
70 
71  The above copyright notice and this permission notice shall be included in
72  all copies or substantial portions of the Software.
73 
74  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
75  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
76  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
77  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
78  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
79  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
80 */
81 
82 #ifndef INCLUDED_SimpleGlob
83 #define INCLUDED_SimpleGlob
84 
85 /*! @brief The operation of SimpleGlob is fine-tuned via the use of a combination
86  of the following flags.
87 
88  The flags may be passed at initialization of the class and used for every
89  filespec added, or alternatively they may optionally be specified in the
90  call to Add() and be different for each filespec.
91 
92  @param SG_GLOB_ERR
93  Return upon read error (e.g. directory does not have read permission)
94 
95  @param SG_GLOB_MARK
96  Append a slash (backslash in Windows) to every path which corresponds
97  to a directory
98 
99  @param SG_GLOB_NOSORT
100  By default, files are returned in sorted into string order. With this
101  flag, no sorting is done. This is not compatible with SG_GLOB_FULLSORT.
102 
103  @param SG_GLOB_FULLSORT
104  By default, files are sorted in groups belonging to each filespec that
105  was added. For example if the filespec "b*" was added before the filespec
106  "a*" then the argv array will contain all b* files sorted in order,
107  followed by all a* files sorted in order. If this flag is specified, the
108  entire array will be sorted ignoring the filespec groups.
109 
110  @param SG_GLOB_NOCHECK
111  If the pattern doesn't match anything, return the original pattern.
112 
113  @param SG_GLOB_TILDE
114  Tilde expansion is carried out (on Unix platforms)
115 
116  @param SG_GLOB_ONLYDIR
117  Return only directories which match (not compatible with SG_GLOB_ONLYFILE)
118 
119  @param SG_GLOB_ONLYFILE
120  Return only files which match (not compatible with SG_GLOB_ONLYDIR)
121 
122  @param SG_GLOB_NODOT
123  Do not return the "." or ".." special directories.
124  */
125 enum SG_Flags {
126  SG_GLOB_ERR = 1 << 0,
127  SG_GLOB_MARK = 1 << 1,
128  SG_GLOB_NOSORT = 1 << 2,
129  SG_GLOB_NOCHECK = 1 << 3,
130  SG_GLOB_TILDE = 1 << 4,
131  SG_GLOB_ONLYDIR = 1 << 5,
133  SG_GLOB_NODOT = 1 << 7,
135 };
136 
137 /*! @brief Error return codes */
138 enum SG_Error {
143 };
144 
145 // ----------------------------------------------------------------------------
146 // Platform dependent implementations
147 
148 // don't include this in documentation as it isn't relevant
149 #ifndef DOXYGEN
150 
151 // on Windows we want to use MBCS aware string functions and mimic the
152 // Unix glob functionality. On Unix we just use glob.
153 #ifdef _WIN32
154 # include <mbstring.h>
155 # define sg_strchr ::_mbschr
156 # define sg_strrchr ::_mbsrchr
157 # define sg_strlen ::_mbslen
158 # if __STDC_WANT_SECURE_LIB__
159 # define sg_strcpy_s(a,n,b) ::_mbscpy_s(a,n,b)
160 # else
161 # define sg_strcpy_s(a,n,b) ::_mbscpy(a,b)
162 # endif
163 # define sg_strcmp ::_mbscmp
164 # define sg_strcasecmp ::_mbsicmp
165 # define SOCHAR_T unsigned char
166 #else
167 # include <sys/types.h>
168 # include <sys/stat.h>
169 # include <glob.h>
170 # include <limits.h>
171 # define MAX_PATH PATH_MAX
172 # define sg_strchr ::strchr
173 # define sg_strrchr ::strrchr
174 # define sg_strlen ::strlen
175 # define sg_strcpy_s(a,n,b) ::strcpy(a,b)
176 # define sg_strcmp ::strcmp
177 # define sg_strcasecmp ::strcasecmp
178 # define SOCHAR_T char
179 #endif
180 
181 #include <stdlib.h>
182 #include <string.h>
183 #include <wchar.h>
184 
185 // use assertions to test the input data
186 #ifdef _DEBUG
187 # ifdef _WIN32
188 # include <crtdbg.h>
189 # define SG_ASSERT(b) _ASSERTE(b)
190 # else
191 # include <assert.h>
192 # define SG_ASSERT(b) assert(b)
193 # endif
194 #else
195 # define SG_ASSERT(b)
196 #endif
197 
198 /*! @brief String manipulation functions. */
200  public:
201  static const char *strchr(const char *s, char c) {
202  return (char *) sg_strchr((const SOCHAR_T *) s, c);
203  } static const wchar_t *strchr(const wchar_t *s, wchar_t c) {
204  return::wcschr(s, c);
205  }
206 
207  static const char *strrchr(const char *s, char c) {
208  return (char *) sg_strrchr((const SOCHAR_T *) s, c);
209  }
210  static const wchar_t *strrchr(const wchar_t *s, wchar_t c) {
211  return::wcsrchr(s, c);
212  }
213 
214  // Note: char strlen returns number of bytes, not characters
215  static size_t strlen(const char *s) {
216  return::strlen(s);
217  }
218  static size_t strlen(const wchar_t *s) {
219  return::wcslen(s);
220  }
221 
222  static void strcpy_s(char *dst, size_t n, const char *src) {
223  (void) n;
224  sg_strcpy_s((SOCHAR_T *) dst, n, (const SOCHAR_T *) src);
225  }
226  static void strcpy_s(wchar_t *dst, size_t n, const wchar_t *src) {
227 # if __STDC_WANT_SECURE_LIB__
228  ::wcscpy_s(dst, n, src);
229 #else
230  (void) n;
231  ::wcscpy(dst, src);
232 #endif
233  }
234 
235  static int strcmp(const char *s1, const char *s2) {
236  return sg_strcmp((const SOCHAR_T *) s1, (const SOCHAR_T *) s2);
237  }
238  static int strcmp(const wchar_t *s1, const wchar_t *s2) {
239  return::wcscmp(s1, s2);
240  }
241 
242  static int strcasecmp(const char *s1, const char *s2) {
243  return sg_strcasecmp((const SOCHAR_T *) s1, (const SOCHAR_T *) s2);
244  }
245 #if _WIN32
246  static int strcasecmp(const wchar_t *s1, const wchar_t *s2) {
247  return::_wcsicmp(s1, s2);
248  }
249 #endif // _WIN32
250 };
251 
256 };
257 
258 #ifdef _WIN32
259 
260 #ifndef INVALID_FILE_ATTRIBUTES
261 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
262 #endif
263 
264 #define SG_PATH_CHAR '\\'
265 
266 /*! @brief Windows glob implementation. */
267 template < class SOCHAR > struct SimpleGlobBase {
268  SimpleGlobBase():m_hFind(INVALID_HANDLE_VALUE) {
269  } int FindFirstFileS(const char *a_pszFileSpec, unsigned int) {
270  m_hFind = FindFirstFileA(a_pszFileSpec, &m_oFindDataA);
271  if (m_hFind != INVALID_HANDLE_VALUE) {
272  return SG_SUCCESS;
273  }
274  DWORD dwErr = GetLastError();
275  if (dwErr == ERROR_FILE_NOT_FOUND) {
276  return SG_ERR_NOMATCH;
277  }
278  return SG_ERR_FAILURE;
279  }
280  int FindFirstFileS(const wchar_t *a_pszFileSpec, unsigned int) {
281  m_hFind = FindFirstFileW(a_pszFileSpec, &m_oFindDataW);
282  if (m_hFind != INVALID_HANDLE_VALUE) {
283  return SG_SUCCESS;
284  }
285  DWORD dwErr = GetLastError();
286  if (dwErr == ERROR_FILE_NOT_FOUND) {
287  return SG_ERR_NOMATCH;
288  }
289  return SG_ERR_FAILURE;
290  }
291 
292  bool FindNextFileS(char) {
293  return FindNextFileA(m_hFind, &m_oFindDataA) != FALSE;
294  }
295  bool FindNextFileS(wchar_t) {
296  return FindNextFileW(m_hFind, &m_oFindDataW) != FALSE;
297  }
298 
299  void FindDone() {
300  FindClose(m_hFind);
301  }
302 
303  const char *GetFileNameS(char) const {
304  return m_oFindDataA.cFileName;
305  }
306  const wchar_t *GetFileNameS(wchar_t) const {
307  return m_oFindDataW.cFileName;
308  }
309  bool IsDirS(char) const {
310  return GetFileTypeS(m_oFindDataA.dwFileAttributes) == SG_FILETYPE_DIR;
311  }
312  bool IsDirS(wchar_t) const {
313  return GetFileTypeS(m_oFindDataW.dwFileAttributes) == SG_FILETYPE_DIR;
314  }
315  SG_FileType GetFileTypeS(const char *a_pszPath) {
316  return GetFileTypeS(GetFileAttributesA(a_pszPath));
317  }
318  SG_FileType GetFileTypeS(const wchar_t *a_pszPath) {
319  return GetFileTypeS(GetFileAttributesW(a_pszPath));
320  }
321  SG_FileType GetFileTypeS(DWORD a_dwAttribs) const {
322  if (a_dwAttribs == INVALID_FILE_ATTRIBUTES) {
323  return SG_FILETYPE_INVALID;
324  }
325  if (a_dwAttribs & FILE_ATTRIBUTE_DIRECTORY) {
326  return SG_FILETYPE_DIR;
327  }
328  return SG_FILETYPE_FILE;
329  }
330 
331  private:
332  HANDLE m_hFind;
333  WIN32_FIND_DATAA m_oFindDataA;
334  WIN32_FIND_DATAW m_oFindDataW;
335 };
336 
337 #else // !_WIN32
338 
339 #define SG_PATH_CHAR '/'
340 
341 /*! @brief Unix glob implementation. */
342 template < class SOCHAR > struct SimpleGlobBase {
344  memset(&m_glob, 0, sizeof(m_glob));
345  m_uiCurr = (size_t) -1;
347  globfree(&m_glob);
348  }
349 
350  void FilePrep() {
351  m_bIsDir = false;
352  size_t len = strlen(m_glob.gl_pathv[m_uiCurr]);
353  if (m_glob.gl_pathv[m_uiCurr][len - 1] == '/') {
354  m_bIsDir = true;
355  m_glob.gl_pathv[m_uiCurr][len - 1] = 0;
356  }
357  }
358 
359  int FindFirstFileS(const char *a_pszFileSpec, unsigned int a_uiFlags) {
360  int nFlags = GLOB_MARK | GLOB_NOSORT;
361  if (a_uiFlags & SG_GLOB_ERR)
362  nFlags |= GLOB_ERR;
363 #ifdef GLOB_TILDE
364  if (a_uiFlags & SG_GLOB_TILDE)
365  nFlags |= GLOB_TILDE;
366 #endif
367  int rc = glob(a_pszFileSpec, nFlags, NULL, &m_glob);
368  if (rc == GLOB_NOSPACE)
369  return SG_ERR_MEMORY;
370  if (rc == GLOB_ABORTED)
371  return SG_ERR_FAILURE;
372  if (rc == GLOB_NOMATCH)
373  return SG_ERR_NOMATCH;
374  m_uiCurr = 0;
375  FilePrep();
376  return SG_SUCCESS;
377  }
378 
379  bool FindNextFileS(char) {
380  SG_ASSERT(m_uiCurr != (size_t) -1);
381  if (++m_uiCurr >= m_glob.gl_pathc) {
382  return false;
383  }
384  FilePrep();
385  return true;
386  }
387 
388  void FindDone() {
389  globfree(&m_glob);
390  memset(&m_glob, 0, sizeof(m_glob));
391  m_uiCurr = (size_t) -1;
392  }
393 
394  const char *GetFileNameS(char) const {
395  SG_ASSERT(m_uiCurr != (size_t) -1);
396  return m_glob.gl_pathv[m_uiCurr];
397  }
398  bool IsDirS(char) const {
399  SG_ASSERT(m_uiCurr != (size_t) -1);
400  return m_bIsDir;
401  }
402  SG_FileType GetFileTypeS(const char *a_pszPath) const {
403  struct stat sb;
404  if (0 != stat(a_pszPath, &sb)) {
405  return SG_FILETYPE_INVALID;
406  }
407  if (S_ISDIR(sb.st_mode)) {
408  return SG_FILETYPE_DIR;
409  }
410  if (S_ISREG(sb.st_mode)) {
411  return SG_FILETYPE_FILE;
412  }
413  return SG_FILETYPE_INVALID;
414  }
415 
416  private:
417  glob_t m_glob;
418  size_t m_uiCurr;
419  bool m_bIsDir;
420 };
421 
422 #endif // _WIN32
423 
424 #endif // DOXYGEN
425 
426 // ---------------------------------------------------------------------------
427 // MAIN TEMPLATE CLASS
428 // ---------------------------------------------------------------------------
429 
430 /*! @brief Implementation of the SimpleGlob class */
431 template < class SOCHAR > class CSimpleGlobTempl:private SimpleGlobBase < SOCHAR > {
432  public:
433  /*! @brief Initialize the class.
434 
435  @param a_uiFlags Combination of SG_GLOB flags.
436  @param a_nReservedSlots Number of slots in the argv array that
437  should be reserved. In the returned array these slots
438  argv[0] ... argv[a_nReservedSlots-1] will be left empty for
439  the caller to fill in.
440  */
441  CSimpleGlobTempl(unsigned int a_uiFlags = 0, int a_nReservedSlots = 0);
442 
443  /*! @brief Deallocate all memory buffers. */
444  ~CSimpleGlobTempl();
445 
446  /*! @brief Initialize (or re-initialize) the class in preparation for
447  adding new filespecs.
448 
449  All existing files are cleared. Note that allocated memory is only
450  deallocated at object destruction.
451 
452  @param a_uiFlags Combination of SG_GLOB flags.
453  @param a_nReservedSlots Number of slots in the argv array that
454  should be reserved. In the returned array these slots
455  argv[0] ... argv[a_nReservedSlots-1] will be left empty for
456  the caller to fill in.
457  */
458  int Init(unsigned int a_uiFlags = 0, int a_nReservedSlots = 0);
459 
460  /*! @brief Add a new filespec to the glob.
461 
462  The filesystem will be immediately scanned for all matching files and
463  directories and they will be added to the glob.
464 
465  @param a_pszFileSpec Filespec to add to the glob.
466 
467  @return SG_SUCCESS Matching files were added to the glob.
468  @return SG_ERR_NOMATCH Nothing matched the pattern. To ignore this error
469  compare the return value to >= SG_SUCCESS.
470  @return SG_ERR_MEMORY Out of memory failure.
471  @return SG_ERR_FAILURE General failure.
472  */
473  int Add(const SOCHAR * a_pszFileSpec);
474 
475  /*! @brief Add an array of filespec to the glob.
476 
477  The filesystem will be immediately scanned for all matching files and
478  directories in each filespec and they will be added to the glob.
479 
480  @param a_nCount Number of filespec in the array.
481  @param a_rgpszFileSpec Array of filespec to add to the glob.
482 
483  @return SG_SUCCESS Matching files were added to the glob.
484  @return SG_ERR_NOMATCH Nothing matched the pattern. To ignore this error
485  compare the return value to >= SG_SUCCESS.
486  @return SG_ERR_MEMORY Out of memory failure.
487  @return SG_ERR_FAILURE General failure.
488  */
489  int Add(int a_nCount, const SOCHAR * const *a_rgpszFileSpec);
490 
491  /*! @brief Return the number of files in the argv array.
492  */
493  inline int FileCount() const {
494  return m_nArgsLen;
495  }
496  /*! @brief Return the full argv array. */ inline SOCHAR **Files() {
497  SetArgvArrayType(POINTERS);
498  return m_rgpArgs;
499  }
500 
501  /*! @brief Return the a single file. */
502  inline SOCHAR *File(int n) {
503  SG_ASSERT(n >= 0 && n < m_nArgsLen);
504  return Files()[n];
505  }
506 
507  private:
508  /*! @brief The argv array has it's members stored as either an offset into
509  the string buffer, or as pointers to their string in the buffer. The offsets
510  are used because if the string buffer is dynamically resized, all pointers
511  into that buffer would become invalid.
512  */
513  enum ARG_ARRAY_TYPE { OFFSETS, POINTERS };
514 
515  /*! @brief Change the type of data stored in the argv array. */
516  void SetArgvArrayType(ARG_ARRAY_TYPE a_nNewType);
517 
518  /*! @brief Add a filename to the array if it passes all requirements. */
519  int AppendName(const SOCHAR * a_pszFileName, bool a_bIsDir);
520 
521  /*! @brief Grow the argv array to the required size. */
522  bool GrowArgvArray(int a_nNewLen);
523 
524  /*! @brief Grow the string buffer to the required size. */
525  bool GrowStringBuffer(size_t a_uiMinSize);
526 
527  /*! @brief Compare two (possible NULL) strings */
528  static int fileSortCompare(const void *a1, const void *a2);
529 
530  private:
531  unsigned int m_uiFlags;
532  ARG_ARRAY_TYPE m_nArgArrayType; //!< is the argv array storing indexes or pointers
533  SOCHAR **m_rgpArgs; //!< argv array
534  int m_nReservedSlots; //!< number of client reserved slots in the argv array
535  int m_nArgsSize; //!< allocated size of array
536  int m_nArgsLen; //!< used length
537  SOCHAR *m_pBuffer; //!< argv string buffer
538  size_t m_uiBufferSize; //!< allocated size of buffer
539  size_t m_uiBufferLen; //!< used length of buffer
540  SOCHAR m_szPathPrefix[MAX_PATH]; //!< path prefix of any wildcard filenames
541 };
542 
543 // ---------------------------------------------------------------------------
544 // IMPLEMENTATION
545 // ---------------------------------------------------------------------------
546 
547 template < class SOCHAR > CSimpleGlobTempl < SOCHAR >::CSimpleGlobTempl(unsigned int a_uiFlags, int a_nReservedSlots)
548 {
549  m_rgpArgs = NULL;
550  m_nArgsSize = 0;
551  m_pBuffer = NULL;
552  m_uiBufferSize = 0;
553 
554  Init(a_uiFlags, a_nReservedSlots);
555 }
556 
557 template < class SOCHAR > CSimpleGlobTempl < SOCHAR >::~CSimpleGlobTempl()
558 {
559  if (m_rgpArgs)
560  free(m_rgpArgs);
561  if (m_pBuffer)
562  free(m_pBuffer);
563 }
564 
565 template < class SOCHAR > int CSimpleGlobTempl < SOCHAR >::Init(unsigned int a_uiFlags, int a_nReservedSlots)
566 {
567  m_nArgArrayType = POINTERS;
568  m_uiFlags = a_uiFlags;
569  m_nArgsLen = a_nReservedSlots;
570  m_nReservedSlots = a_nReservedSlots;
571  m_uiBufferLen = 0;
572 
573  if (m_nReservedSlots > 0) {
574  if (!GrowArgvArray(m_nReservedSlots)) {
575  return SG_ERR_MEMORY;
576  }
577  for (int n = 0; n < m_nReservedSlots; ++n) {
578  m_rgpArgs[n] = NULL;
579  }
580  }
581 
582  return SG_SUCCESS;
583 }
584 
585 template < class SOCHAR > int CSimpleGlobTempl < SOCHAR >::Add(const SOCHAR * a_pszFileSpec)
586 {
587 #ifdef _WIN32
588  // Windows FindFirst/FindNext recognizes forward slash as the same as backward slash
589  // and follows the directories. We need to do the same when calculating the prefix
590  // and when we have no wildcards.
591  SOCHAR szFileSpec[MAX_PATH];
592  SimpleGlobUtil::strcpy_s(szFileSpec, MAX_PATH, a_pszFileSpec);
593  const SOCHAR *pszPath = SimpleGlobUtil::strchr(szFileSpec, '/');
594  while (pszPath) {
595  szFileSpec[pszPath - szFileSpec] = SG_PATH_CHAR;
596  pszPath = SimpleGlobUtil::strchr(pszPath + 1, '/');
597  }
598  a_pszFileSpec = szFileSpec;
599 #endif
600 
601  // if this doesn't contain wildcards then we can just add it directly
602  m_szPathPrefix[0] = 0;
603  if (!SimpleGlobUtil::strchr(a_pszFileSpec, '*') && !SimpleGlobUtil::strchr(a_pszFileSpec, '?')) {
604  SG_FileType nType = GetFileTypeS(a_pszFileSpec);
605  if (nType == SG_FILETYPE_INVALID) {
606  if (m_uiFlags & SG_GLOB_NOCHECK) {
607  return AppendName(a_pszFileSpec, false);
608  }
609  return SG_ERR_NOMATCH;
610  }
611  return AppendName(a_pszFileSpec, nType == SG_FILETYPE_DIR);
612  }
613 #ifdef _WIN32
614  // Windows doesn't return the directory with the filename, so we need to extract the
615  // path from the search string ourselves and prefix it to the filename we get back.
616  const SOCHAR *pszFilename = SimpleGlobUtil::strrchr(a_pszFileSpec, SG_PATH_CHAR);
617  if (pszFilename) {
618  SimpleGlobUtil::strcpy_s(m_szPathPrefix, MAX_PATH, a_pszFileSpec);
619  m_szPathPrefix[pszFilename - a_pszFileSpec + 1] = 0;
620  }
621 #endif
622 
623  // search for the first match on the file
624  int rc = FindFirstFileS(a_pszFileSpec, m_uiFlags);
625  if (rc != SG_SUCCESS) {
626  if (rc == SG_ERR_NOMATCH && (m_uiFlags & SG_GLOB_NOCHECK)) {
627  int ok = AppendName(a_pszFileSpec, false);
628  if (ok != SG_SUCCESS)
629  rc = ok;
630  }
631  return rc;
632  }
633  // add it and find all subsequent matches
634  int nError, nStartLen = m_nArgsLen;
635  bool bSuccess;
636  do {
637  nError = AppendName(GetFileNameS((SOCHAR) 0), IsDirS((SOCHAR) 0));
638  bSuccess = FindNextFileS((SOCHAR) 0);
639  }
640  while (nError == SG_SUCCESS && bSuccess);
642 
643  // sort these files if required
644  if (m_nArgsLen > nStartLen && !(m_uiFlags & SG_GLOB_NOSORT)) {
645  if (m_uiFlags & SG_GLOB_FULLSORT) {
646  nStartLen = m_nReservedSlots;
647  }
648  SetArgvArrayType(POINTERS);
649  qsort(m_rgpArgs + nStartLen, m_nArgsLen - nStartLen, sizeof(m_rgpArgs[0]), fileSortCompare);
650  }
651 
652  return nError;
653 }
654 
655 template < class SOCHAR > int CSimpleGlobTempl < SOCHAR >::Add(int a_nCount, const SOCHAR * const *a_rgpszFileSpec)
656 {
657  int nResult;
658  for (int n = 0; n < a_nCount; ++n) {
659  nResult = Add(a_rgpszFileSpec[n]);
660  if (nResult != SG_SUCCESS) {
661  return nResult;
662  }
663  }
664  return SG_SUCCESS;
665 }
666 
667 template < class SOCHAR > int CSimpleGlobTempl < SOCHAR >::AppendName(const SOCHAR * a_pszFileName, bool a_bIsDir)
668 {
669  // we need the argv array as offsets in case we resize it
670  SetArgvArrayType(OFFSETS);
671 
672  // check for special cases which cause us to ignore this entry
673  if ((m_uiFlags & SG_GLOB_ONLYDIR) && !a_bIsDir) {
674  return SG_SUCCESS;
675  }
676  if ((m_uiFlags & SG_GLOB_ONLYFILE) && a_bIsDir) {
677  return SG_SUCCESS;
678  }
679  if ((m_uiFlags & SG_GLOB_NODOT) && a_bIsDir) {
680  if (a_pszFileName[0] == '.') {
681  if (a_pszFileName[1] == '\0') {
682  return SG_SUCCESS;
683  }
684  if (a_pszFileName[1] == '.' && a_pszFileName[2] == '\0') {
685  return SG_SUCCESS;
686  }
687  }
688  }
689  // ensure that we have enough room in the argv array
690  if (!GrowArgvArray(m_nArgsLen + 1)) {
691  return SG_ERR_MEMORY;
692  }
693  // ensure that we have enough room in the string buffer
694  size_t uiPrefixLen = SimpleGlobUtil::strlen(m_szPathPrefix);
695  size_t uiLen = uiPrefixLen + SimpleGlobUtil::strlen(a_pszFileName) + 1; // + null character
696  if (a_bIsDir && (m_uiFlags & SG_GLOB_MARK) == SG_GLOB_MARK) {
697  ++uiLen; // need space for the backslash
698  }
699  if (!GrowStringBuffer(m_uiBufferLen + uiLen)) {
700  return SG_ERR_MEMORY;
701  }
702  // add this entry
703  m_rgpArgs[m_nArgsLen++] = (SOCHAR *) m_uiBufferLen; // offset from beginning of buffer
704  SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen, m_uiBufferSize - m_uiBufferLen, m_szPathPrefix);
705  SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen + uiPrefixLen, m_uiBufferSize - m_uiBufferLen - uiPrefixLen, a_pszFileName);
706  m_uiBufferLen += uiLen;
707 
708  // add the directory slash if desired
709  if (a_bIsDir && (m_uiFlags & SG_GLOB_MARK) == SG_GLOB_MARK) {
710  const static SOCHAR szDirSlash[] = { SG_PATH_CHAR, 0 };
711  SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen - 2, m_uiBufferSize - (m_uiBufferLen - 2), szDirSlash);
712  }
713 
714  return SG_SUCCESS;
715 }
716 
717 template < class SOCHAR > void CSimpleGlobTempl < SOCHAR >::SetArgvArrayType(ARG_ARRAY_TYPE a_nNewType)
718 {
719  if (m_nArgArrayType == a_nNewType)
720  return;
721  if (a_nNewType == POINTERS) {
722  SG_ASSERT(m_nArgArrayType == OFFSETS);
723  for (int n = 0; n < m_nArgsLen; ++n) {
724  m_rgpArgs[n] = (m_rgpArgs[n] == (SOCHAR *) - 1) ? NULL : m_pBuffer + (size_t) m_rgpArgs[n];
725  }
726  } else {
727  SG_ASSERT(a_nNewType == OFFSETS);
728  SG_ASSERT(m_nArgArrayType == POINTERS);
729  for (int n = 0; n < m_nArgsLen; ++n) {
730  m_rgpArgs[n] = (m_rgpArgs[n] == NULL) ? (SOCHAR *) - 1 : (SOCHAR *) (m_rgpArgs[n] - m_pBuffer);
731  }
732  }
733  m_nArgArrayType = a_nNewType;
734 }
735 
736 template < class SOCHAR > bool CSimpleGlobTempl < SOCHAR >::GrowArgvArray(int a_nNewLen)
737 {
738  if (a_nNewLen >= m_nArgsSize) {
739  static const int SG_ARGV_INITIAL_SIZE = 32;
740  int nNewSize = (m_nArgsSize > 0) ? m_nArgsSize * 2 : SG_ARGV_INITIAL_SIZE;
741  while (a_nNewLen >= nNewSize) {
742  nNewSize *= 2;
743  }
744  void *pNewBuffer = realloc(m_rgpArgs, nNewSize * sizeof(SOCHAR *));
745  if (!pNewBuffer)
746  return false;
747  m_nArgsSize = nNewSize;
748  m_rgpArgs = (SOCHAR **) pNewBuffer;
749  }
750  return true;
751 }
752 
753 template < class SOCHAR > bool CSimpleGlobTempl < SOCHAR >::GrowStringBuffer(size_t a_uiMinSize)
754 {
755  if (a_uiMinSize >= m_uiBufferSize) {
756  static const int SG_BUFFER_INITIAL_SIZE = 1024;
757  size_t uiNewSize = (m_uiBufferSize > 0) ? m_uiBufferSize * 2 : SG_BUFFER_INITIAL_SIZE;
758  while (a_uiMinSize >= uiNewSize) {
759  uiNewSize *= 2;
760  }
761  void *pNewBuffer = realloc(m_pBuffer, uiNewSize * sizeof(SOCHAR));
762  if (!pNewBuffer)
763  return false;
764  m_uiBufferSize = uiNewSize;
765  m_pBuffer = (SOCHAR *) pNewBuffer;
766  }
767  return true;
768 }
769 
770 template < class SOCHAR > int CSimpleGlobTempl < SOCHAR >::fileSortCompare(const void *a1, const void *a2)
771 {
772  const SOCHAR *s1 = *(const SOCHAR **) a1;
773  const SOCHAR *s2 = *(const SOCHAR **) a2;
774  if (s1 && s2) {
775  return SimpleGlobUtil::strcasecmp(s1, s2);
776  }
777  // NULL sorts first
778  return s1 == s2 ? 0 : (s1 ? 1 : -1);
779 }
780 
781 // ---------------------------------------------------------------------------
782 // TYPE DEFINITIONS
783 // ---------------------------------------------------------------------------
784 
785 typedef CSimpleGlobTempl < char >CSimpleGlobA; /*!< @brief ASCII/MBCS version of CSimpleGlob */
786 typedef CSimpleGlobTempl < wchar_t > CSimpleGlobW; /*!< @brief wchar_t version of CSimpleGlob */
787 #if defined(_UNICODE)
788 # define CSimpleGlob CSimpleGlobW /*!< @brief TCHAR version dependent on if _UNICODE is defined */
789 #else
790 # define CSimpleGlob CSimpleGlobA /*!< @brief TCHAR version dependent on if _UNICODE is defined */
791 #endif
792 
793 #endif // INCLUDED_SimpleGlob
size_t m_uiBufferSize
allocated size of buffer
Definition: SimpleGlob.h:538
SG_FileType GetFileTypeS(const char *a_pszPath) const
Definition: SimpleGlob.h:402
Implementation of the SimpleGlob class.
Definition: SimpleGlob.h:431
#define SOCHAR_T
Definition: SimpleGlob.h:178
#define sg_strrchr
Definition: SimpleGlob.h:173
#define sg_strchr
Definition: SimpleGlob.h:172
static size_t strlen(const char *s)
Definition: SimpleGlob.h:215
SG_Flags
The operation of SimpleGlob is fine-tuned via the use of a combination of the following flags...
Definition: SimpleGlob.h:125
static const wchar_t * strchr(const wchar_t *s, wchar_t c)
Definition: SimpleGlob.h:203
#define MAX_PATH
Definition: SimpleGlob.h:171
SOCHAR ** Files()
Return the full argv array.
Definition: SimpleGlob.h:496
static const char * strrchr(const char *s, char c)
Definition: SimpleGlob.h:207
#define SG_ASSERT(b)
Definition: SimpleGlob.h:195
SOCHAR * m_pBuffer
argv string buffer
Definition: SimpleGlob.h:537
void FilePrep()
Definition: SimpleGlob.h:350
static int strcasecmp(const char *s1, const char *s2)
Definition: SimpleGlob.h:242
bool IsDirS(char) const
Definition: SimpleGlob.h:398
#define sg_strcpy_s(a, n, b)
Definition: SimpleGlob.h:175
int m_nArgsSize
allocated size of array
Definition: SimpleGlob.h:535
#define SG_PATH_CHAR
Definition: SimpleGlob.h:339
static int strcmp(const char *s1, const char *s2)
Definition: SimpleGlob.h:235
static void strcpy_s(char *dst, size_t n, const char *src)
Definition: SimpleGlob.h:222
unsigned int m_uiFlags
Definition: SimpleGlob.h:531
CSimpleGlobTempl< wchar_t > CSimpleGlobW
wchar_t version of CSimpleGlob
Definition: SimpleGlob.h:786
ARG_ARRAY_TYPE
The argv array has it&#39;s members stored as either an offset into the string buffer, or as pointers to their string in the buffer. The offsets are used because if the string buffer is dynamically resized, all pointers into that buffer would become invalid.
Definition: SimpleGlob.h:513
String manipulation functions.
Definition: SimpleGlob.h:199
static void strcpy_s(wchar_t *dst, size_t n, const wchar_t *src)
Definition: SimpleGlob.h:226
void FindDone()
Definition: SimpleGlob.h:388
bool FindNextFileS(char)
Definition: SimpleGlob.h:379
static size_t strlen(const wchar_t *s)
Definition: SimpleGlob.h:218
SG_FileType
Definition: SimpleGlob.h:252
CSimpleGlobTempl< char > CSimpleGlobA
ASCII/MBCS version of CSimpleGlob.
Definition: SimpleGlob.h:785
int FileCount() const
Return the number of files in the argv array.
Definition: SimpleGlob.h:493
#define FALSE
#define sg_strcmp
Definition: SimpleGlob.h:176
static const char * strchr(const char *s, char c)
Definition: SimpleGlob.h:201
int m_nReservedSlots
number of client reserved slots in the argv array
Definition: SimpleGlob.h:534
int m_nArgsLen
used length
Definition: SimpleGlob.h:536
int FindFirstFileS(const char *a_pszFileSpec, unsigned int a_uiFlags)
Definition: SimpleGlob.h:359
SG_Error
Error return codes.
Definition: SimpleGlob.h:138
static int strcmp(const wchar_t *s1, const wchar_t *s2)
Definition: SimpleGlob.h:238
size_t m_uiBufferLen
used length of buffer
Definition: SimpleGlob.h:539
SOCHAR ** m_rgpArgs
argv array
Definition: SimpleGlob.h:533
#define sg_strcasecmp
Definition: SimpleGlob.h:177
static const wchar_t * strrchr(const wchar_t *s, wchar_t c)
Definition: SimpleGlob.h:210
const char * GetFileNameS(char) const
Definition: SimpleGlob.h:394
memset(buf, 0, buflen)
Unix glob implementation.
Definition: SimpleGlob.h:342
size_t m_uiCurr
Definition: SimpleGlob.h:418
ARG_ARRAY_TYPE m_nArgArrayType
is the argv array storing indexes or pointers
Definition: SimpleGlob.h:532
SOCHAR * File(int n)
Return the a single file.
Definition: SimpleGlob.h:502