18b52a31eSHanoh Haim/*! @file SimpleGlob.h
28b52a31eSHanoh Haim
38b52a31eSHanoh Haim    @version 3.5
48b52a31eSHanoh Haim
58b52a31eSHanoh Haim    @brief A cross-platform file globbing library providing the ability to
68b52a31eSHanoh Haim    expand wildcards in command-line arguments to a list of all matching
78b52a31eSHanoh Haim    files. It is designed explicitly to be portable to any platform and has
88b52a31eSHanoh Haim    been tested on Windows and Linux. See CSimpleGlobTempl for the class
98b52a31eSHanoh Haim    definition.
108b52a31eSHanoh Haim
118b52a31eSHanoh Haim    @section features FEATURES
128b52a31eSHanoh Haim
138b52a31eSHanoh Haim    -   MIT Licence allows free use in all software (including GPL and
148b52a31eSHanoh Haim        commercial)
158b52a31eSHanoh Haim    -   multi-platform (Windows 95/98/ME/NT/2K/XP, Linux, Unix)
168b52a31eSHanoh Haim    -   supports most of the standard linux glob() options
178b52a31eSHanoh Haim    -   recognition of a forward paths as equivalent to a backward slash
188b52a31eSHanoh Haim        on Windows. e.g. "c:/path/foo*" is equivalent to "c:\path\foo*".
198b52a31eSHanoh Haim    -   implemented with only a single C++ header file
208b52a31eSHanoh Haim    -   char, wchar_t and Windows TCHAR in the same program
218b52a31eSHanoh Haim    -   complete working examples included
228b52a31eSHanoh Haim    -   compiles cleanly at warning level 4 (Windows/VC.NET 2003),
238b52a31eSHanoh Haim        warning level 3 (Windows/VC6) and -Wall (Linux/gcc)
248b52a31eSHanoh Haim
258b52a31eSHanoh Haim    @section usage USAGE
268b52a31eSHanoh Haim
278b52a31eSHanoh Haim    The SimpleGlob class is used by following these steps:
288b52a31eSHanoh Haim
298b52a31eSHanoh Haim    <ol>
308b52a31eSHanoh Haim    <li> Include the SimpleGlob.h header file
318b52a31eSHanoh Haim
328b52a31eSHanoh Haim        <pre>
338b52a31eSHanoh Haim        \#include "SimpleGlob.h"
348b52a31eSHanoh Haim        </pre>
358b52a31eSHanoh Haim
368b52a31eSHanoh Haim   <li> Instantiate a CSimpleGlob object supplying the appropriate flags.
378b52a31eSHanoh Haim
388b52a31eSHanoh Haim        <pre>
398b52a31eSHanoh Haim        @link CSimpleGlobTempl CSimpleGlob @endlink glob(FLAGS);
408b52a31eSHanoh Haim        </pre>
418b52a31eSHanoh Haim
428b52a31eSHanoh Haim   <li> Add all file specifications to the glob class.
438b52a31eSHanoh Haim
448b52a31eSHanoh Haim        <pre>
458b52a31eSHanoh Haim        glob.Add("file*");
468b52a31eSHanoh Haim        glob.Add(argc, argv);
478b52a31eSHanoh Haim        </pre>
488b52a31eSHanoh Haim
498b52a31eSHanoh Haim   <li> Process all files with File(), Files() and FileCount()
508b52a31eSHanoh Haim
518b52a31eSHanoh Haim        <pre>
528b52a31eSHanoh Haim        for (int n = 0; n < glob.FileCount(); ++n) {
538b52a31eSHanoh Haim            ProcessFile(glob.File(n));
548b52a31eSHanoh Haim        }
558b52a31eSHanoh Haim        </pre>
568b52a31eSHanoh Haim
578b52a31eSHanoh Haim    </ol>
588b52a31eSHanoh Haim
598b52a31eSHanoh Haim    @section licence MIT LICENCE
608b52a31eSHanoh Haim
618b52a31eSHanoh Haim    The licence text below is the boilerplate "MIT Licence" used from:
628b52a31eSHanoh Haim    http://www.opensource.org/licenses/mit-license.php
638b52a31eSHanoh Haim
648b52a31eSHanoh Haim    Copyright (c) 2006-2007, Brodie Thiesfield
658b52a31eSHanoh Haim
668b52a31eSHanoh Haim    Permission is hereby granted, free of charge, to any person obtaining a
678b52a31eSHanoh Haim    copy of this software and associated documentation files (the "Software"),
688b52a31eSHanoh Haim    to deal in the Software without restriction, including without limitation
698b52a31eSHanoh Haim    the rights to use, copy, modify, merge, publish, distribute, sublicense,
708b52a31eSHanoh Haim    and/or sell copies of the Software, and to permit persons to whom the
718b52a31eSHanoh Haim    Software is furnished to do so, subject to the following conditions:
728b52a31eSHanoh Haim
738b52a31eSHanoh Haim    The above copyright notice and this permission notice shall be included
748b52a31eSHanoh Haim    in all copies or substantial portions of the Software.
758b52a31eSHanoh Haim
838b52a31eSHanoh Haim*/
848b52a31eSHanoh Haim
858b52a31eSHanoh Haim#ifndef INCLUDED_SimpleGlob
868b52a31eSHanoh Haim#define INCLUDED_SimpleGlob
878b52a31eSHanoh Haim
888b52a31eSHanoh Haim/*! @brief The operation of SimpleGlob is fine-tuned via the use of a
898b52a31eSHanoh Haim    combination of the following flags.
908b52a31eSHanoh Haim
918b52a31eSHanoh Haim    The flags may be passed at initialization of the class and used for every
928b52a31eSHanoh Haim    filespec added, or alternatively they may optionally be specified in the
938b52a31eSHanoh Haim    call to Add() and be different for each filespec.
948b52a31eSHanoh Haim
958b52a31eSHanoh Haim    @param SG_GLOB_ERR
968b52a31eSHanoh Haim        Return upon read error (e.g. directory does not have read permission)
978b52a31eSHanoh Haim
988b52a31eSHanoh Haim    @param SG_GLOB_MARK
998b52a31eSHanoh Haim        Append a slash (backslash in Windows) to every path which corresponds
1008b52a31eSHanoh Haim        to a directory
1018b52a31eSHanoh Haim
1028b52a31eSHanoh Haim    @param SG_GLOB_NOSORT
1038b52a31eSHanoh Haim        By default, files are returned in sorted into string order. With this
1048b52a31eSHanoh Haim        flag, no sorting is done. This is not compatible with
1058b52a31eSHanoh Haim        SG_GLOB_FULLSORT.
1068b52a31eSHanoh Haim
1078b52a31eSHanoh Haim    @param SG_GLOB_FULLSORT
1088b52a31eSHanoh Haim        By default, files are sorted in groups belonging to each filespec that
1098b52a31eSHanoh Haim        was added. For example if the filespec "b*" was added before the
1108b52a31eSHanoh Haim        filespec "a*" then the argv array will contain all b* files sorted in
1118b52a31eSHanoh Haim        order, followed by all a* files sorted in order. If this flag is
1128b52a31eSHanoh Haim        specified, the entire array will be sorted ignoring the filespec
1138b52a31eSHanoh Haim        groups.
1148b52a31eSHanoh Haim
1158b52a31eSHanoh Haim    @param SG_GLOB_NOCHECK
1168b52a31eSHanoh Haim        If the pattern doesn't match anything, return the original pattern.
1178b52a31eSHanoh Haim
1188b52a31eSHanoh Haim    @param SG_GLOB_TILDE
1198b52a31eSHanoh Haim        Tilde expansion is carried out (on Unix platforms)
1208b52a31eSHanoh Haim
1218b52a31eSHanoh Haim    @param SG_GLOB_ONLYDIR
1228b52a31eSHanoh Haim        Return only directories which match (not compatible with
1238b52a31eSHanoh Haim        SG_GLOB_ONLYFILE)
1248b52a31eSHanoh Haim
1258b52a31eSHanoh Haim    @param SG_GLOB_ONLYFILE
1268b52a31eSHanoh Haim        Return only files which match (not compatible with SG_GLOB_ONLYDIR)
1278b52a31eSHanoh Haim
1288b52a31eSHanoh Haim    @param SG_GLOB_NODOT
1298b52a31eSHanoh Haim        Do not return the "." or ".." special directories.
1308b52a31eSHanoh Haim */
1318b52a31eSHanoh Haimenum SG_Flags {
1328b52a31eSHanoh Haim    SG_GLOB_ERR         = 1 << 0,
1338b52a31eSHanoh Haim    SG_GLOB_MARK        = 1 << 1,
1348b52a31eSHanoh Haim    SG_GLOB_NOSORT      = 1 << 2,
1358b52a31eSHanoh Haim    SG_GLOB_NOCHECK     = 1 << 3,
1368b52a31eSHanoh Haim    SG_GLOB_TILDE       = 1 << 4,
1378b52a31eSHanoh Haim    SG_GLOB_ONLYDIR     = 1 << 5,
1388b52a31eSHanoh Haim    SG_GLOB_ONLYFILE    = 1 << 6,
1398b52a31eSHanoh Haim    SG_GLOB_NODOT       = 1 << 7,
1408b52a31eSHanoh Haim    SG_GLOB_FULLSORT    = 1 << 8
1418b52a31eSHanoh Haim};
1428b52a31eSHanoh Haim
1438b52a31eSHanoh Haim/*! @brief Error return codes */
1448b52a31eSHanoh Haimenum SG_Error {
1458b52a31eSHanoh Haim    SG_SUCCESS          =  0,
1468b52a31eSHanoh Haim    SG_ERR_NOMATCH      =  1,
1478b52a31eSHanoh Haim    SG_ERR_MEMORY       = -1,
1488b52a31eSHanoh Haim    SG_ERR_FAILURE      = -2
1498b52a31eSHanoh Haim};
1508b52a31eSHanoh Haim#ifndef MAX_PATH
1518b52a31eSHanoh Haim# define MAX_PATH           4096
1528b52a31eSHanoh Haim#endif
1538b52a31eSHanoh Haim// ---------------------------------------------------------------------------
1548b52a31eSHanoh Haim// Platform dependent implementations
1558b52a31eSHanoh Haim
1568b52a31eSHanoh Haim// if we aren't on Windows and we have ICU available, then enable ICU
1578b52a31eSHanoh Haim// by default. Define this to 0 to intentially disable it.
1588b52a31eSHanoh Haim#ifndef SG_HAVE_ICU
1598b52a31eSHanoh Haim# if !defined(WIN32) && defined(USTRING_H)
1608b52a31eSHanoh Haim#   define SG_HAVE_ICU 1
1618b52a31eSHanoh Haim# else
1628b52a31eSHanoh Haim#   define SG_HAVE_ICU 0
1638b52a31eSHanoh Haim# endif
1648b52a31eSHanoh Haim#endif
1658b52a31eSHanoh Haim
1668b52a31eSHanoh Haim// don't include this in documentation as it isn't relevant
1678b52a31eSHanoh Haim#ifndef DOXYGEN
1688b52a31eSHanoh Haim
1698b52a31eSHanoh Haim// on Windows we want to use MBCS aware string functions and mimic the
1708b52a31eSHanoh Haim// Unix glob functionality. On Unix we just use glob.
1718b52a31eSHanoh Haim#ifdef WIN32
1728b52a31eSHanoh Haim# include <mbstring.h>
1738b52a31eSHanoh Haim# define sg_strchr          ::_mbschr
1748b52a31eSHanoh Haim# define sg_strrchr         ::_mbsrchr
1758b52a31eSHanoh Haim# define sg_strlen          ::_mbslen
1768b52a31eSHanoh Haim# if __STDC_WANT_SECURE_LIB__
1778b52a31eSHanoh Haim#  define sg_strcpy_s(a,n,b) ::_mbscpy_s(a,n,b)
1788b52a31eSHanoh Haim# else
1798b52a31eSHanoh Haim#  define sg_strcpy_s(a,n,b) ::_mbscpy(a,b)
1808b52a31eSHanoh Haim# endif
1818b52a31eSHanoh Haim# define sg_strcmp          ::_mbscmp
1828b52a31eSHanoh Haim# define sg_strcasecmp      ::_mbsicmp
1838b52a31eSHanoh Haim# define SOCHAR_T           unsigned char
1848b52a31eSHanoh Haim#else
1858b52a31eSHanoh Haim# include <sys/types.h>
1868b52a31eSHanoh Haim# include <sys/stat.h>
1878b52a31eSHanoh Haim# include <glob.h>
1888b52a31eSHanoh Haim# include <limits.h>
1898b52a31eSHanoh Haim# define sg_strchr          ::strchr
1908b52a31eSHanoh Haim# define sg_strrchr         ::strrchr
1918b52a31eSHanoh Haim# define sg_strlen          ::strlen
1928b52a31eSHanoh Haim# define sg_strcpy_s(a,n,b) ::strcpy(a,b)
1938b52a31eSHanoh Haim# define sg_strcmp          ::strcmp
1948b52a31eSHanoh Haim# define sg_strcasecmp      ::strcasecmp
1958b52a31eSHanoh Haim# define SOCHAR_T           char
1968b52a31eSHanoh Haim#endif
1978b52a31eSHanoh Haim
1988b52a31eSHanoh Haim#include <stdlib.h>
1998b52a31eSHanoh Haim#include <string.h>
2008b52a31eSHanoh Haim#include <wchar.h>
2018b52a31eSHanoh Haim
2028b52a31eSHanoh Haim// use assertions to test the input data
2038b52a31eSHanoh Haim#ifdef _DEBUG
2048b52a31eSHanoh Haim# ifdef _MSC_VER
2058b52a31eSHanoh Haim#  include <crtdbg.h>
2068b52a31eSHanoh Haim#  define SG_ASSERT(b)    _ASSERTE(b)
2078b52a31eSHanoh Haim# else
2088b52a31eSHanoh Haim#  include <assert.h>
2098b52a31eSHanoh Haim#  define SG_ASSERT(b)    assert(b)
2108b52a31eSHanoh Haim# endif
2118b52a31eSHanoh Haim#else
2128b52a31eSHanoh Haim# define SG_ASSERT(b)
2138b52a31eSHanoh Haim#endif
2148b52a31eSHanoh Haim
2158b52a31eSHanoh Haim/*! @brief String manipulation functions. */
2168b52a31eSHanoh Haimclass SimpleGlobUtil
2178b52a31eSHanoh Haim{
2188b52a31eSHanoh Haimpublic:
2198b52a31eSHanoh Haim    static const char * strchr(const char *s, char c) {
2208b52a31eSHanoh Haim        return (char *) sg_strchr((const SOCHAR_T *)s, c);
2218b52a31eSHanoh Haim    }
2228b52a31eSHanoh Haim    static const wchar_t * strchr(const wchar_t *s, wchar_t c) {
2238b52a31eSHanoh Haim        return ::wcschr(s, c);
2248b52a31eSHanoh Haim    }
2258b52a31eSHanoh Haim#if SG_HAVE_ICU
2268b52a31eSHanoh Haim    static const UChar * strchr(const UChar *s, UChar c) {
2278b52a31eSHanoh Haim        return ::u_strchr(s, c);
2288b52a31eSHanoh Haim    }
2298b52a31eSHanoh Haim#endif
2308b52a31eSHanoh Haim
2318b52a31eSHanoh Haim    static const char * strrchr(const char *s, char c) {
2328b52a31eSHanoh Haim        return (char *) sg_strrchr((const SOCHAR_T *)s, c);
2338b52a31eSHanoh Haim    }
2348b52a31eSHanoh Haim    static const wchar_t * strrchr(const wchar_t *s, wchar_t c) {
2358b52a31eSHanoh Haim        return ::wcsrchr(s, c);
2368b52a31eSHanoh Haim    }
2378b52a31eSHanoh Haim#if SG_HAVE_ICU
2388b52a31eSHanoh Haim    static const UChar * strrchr(const UChar *s, UChar c) {
2398b52a31eSHanoh Haim        return ::u_strrchr(s, c);
2408b52a31eSHanoh Haim    }
2418b52a31eSHanoh Haim#endif
2428b52a31eSHanoh Haim
2438b52a31eSHanoh Haim    // Note: char strlen returns number of bytes, not characters
2448b52a31eSHanoh Haim    static size_t strlen(const char *s) { return ::strlen(s); }
2458b52a31eSHanoh Haim    static size_t strlen(const wchar_t *s) { return ::wcslen(s); }
2468b52a31eSHanoh Haim#if SG_HAVE_ICU
2478b52a31eSHanoh Haim    static size_t strlen(const UChar *s) { return ::u_strlen(s); }
2488b52a31eSHanoh Haim#endif
2498b52a31eSHanoh Haim
2508b52a31eSHanoh Haim    static void strcpy_s(char *dst, size_t n, const char *src)  {
2518b52a31eSHanoh Haim        (void) n;
2528b52a31eSHanoh Haim        sg_strcpy_s((SOCHAR_T *)dst, n, (const SOCHAR_T *)src);
2538b52a31eSHanoh Haim    }
2548b52a31eSHanoh Haim    static void strcpy_s(wchar_t *dst, size_t n, const wchar_t *src) {
2558b52a31eSHanoh Haim# if __STDC_WANT_SECURE_LIB__
2568b52a31eSHanoh Haim        ::wcscpy_s(dst, n, src);
2578b52a31eSHanoh Haim#else
2588b52a31eSHanoh Haim        (void) n;
2598b52a31eSHanoh Haim        ::wcscpy(dst, src);
2608b52a31eSHanoh Haim#endif
2618b52a31eSHanoh Haim    }
2628b52a31eSHanoh Haim#if SG_HAVE_ICU
2638b52a31eSHanoh Haim    static void strcpy_s(UChar *dst, size_t n, const UChar *src)  {
2648b52a31eSHanoh Haim        ::u_strncpy(dst, src, n);
2658b52a31eSHanoh Haim    }
2668b52a31eSHanoh Haim#endif
2678b52a31eSHanoh Haim
2688b52a31eSHanoh Haim    static int strcmp(const char *s1, const char *s2) {
2698b52a31eSHanoh Haim        return sg_strcmp((const SOCHAR_T *)s1, (const SOCHAR_T *)s2);
2708b52a31eSHanoh Haim    }
2718b52a31eSHanoh Haim    static int strcmp(const wchar_t *s1, const wchar_t *s2) {
2728b52a31eSHanoh Haim        return ::wcscmp(s1, s2);
2738b52a31eSHanoh Haim    }
2748b52a31eSHanoh Haim#if SG_HAVE_ICU
2758b52a31eSHanoh Haim    static int strcmp(const UChar *s1, const UChar *s2) {
2768b52a31eSHanoh Haim        return ::u_strcmp(s1, s2);
2778b52a31eSHanoh Haim    }
2788b52a31eSHanoh Haim#endif
2798b52a31eSHanoh Haim
2808b52a31eSHanoh Haim    static int strcasecmp(const char *s1, const char *s2) {
2818b52a31eSHanoh Haim        return sg_strcasecmp((const SOCHAR_T *)s1, (const SOCHAR_T *)s2);
2828b52a31eSHanoh Haim    }
2838b52a31eSHanoh Haim#if WIN32
2848b52a31eSHanoh Haim    static int strcasecmp(const wchar_t *s1, const wchar_t *s2) {
2858b52a31eSHanoh Haim        return ::_wcsicmp(s1, s2);
2868b52a31eSHanoh Haim    }
2878b52a31eSHanoh Haim#endif // WIN32
2888b52a31eSHanoh Haim#if SG_HAVE_ICU
2898b52a31eSHanoh Haim    static int strcasecmp(const UChar *s1, const UChar *s2) {
2908b52a31eSHanoh Haim        return u_strcasecmp(s1, s2, 0);
2918b52a31eSHanoh Haim    }
2928b52a31eSHanoh Haim#endif
2938b52a31eSHanoh Haim};
2948b52a31eSHanoh Haim
2958b52a31eSHanoh Haimenum SG_FileType {
2968b52a31eSHanoh Haim    SG_FILETYPE_INVALID,
2978b52a31eSHanoh Haim    SG_FILETYPE_FILE,
2988b52a31eSHanoh Haim    SG_FILETYPE_DIR
2998b52a31eSHanoh Haim};
3008b52a31eSHanoh Haim
3018b52a31eSHanoh Haim#ifdef WIN32
3028b52a31eSHanoh Haim#ifndef INVALID_FILE_ATTRIBUTES
3038b52a31eSHanoh Haim# define INVALID_FILE_ATTRIBUTES    ((uint32_t)-1)
3048b52a31eSHanoh Haim#endif
3058b52a31eSHanoh Haim
3068b52a31eSHanoh Haim#define SG_PATH_CHAR    '\\'
3078b52a31eSHanoh Haim
3088b52a31eSHanoh Haim/*! @brief Windows glob implementation. */
3098b52a31eSHanoh Haimtemplate<class SOCHAR>
3108b52a31eSHanoh Haimstruct SimpleGlobBase
3118b52a31eSHanoh Haim{
3128b52a31eSHanoh Haim    SimpleGlobBase() : m_hFind(INVALID_HANDLE_VALUE) { }
3138b52a31eSHanoh Haim
3148b52a31eSHanoh Haim    int FindFirstFileS(const char * a_pszFileSpec, unsigned int) {
3158b52a31eSHanoh Haim        m_hFind = FindFirstFileA(a_pszFileSpec, &m_oFindDataA);
3168b52a31eSHanoh Haim        if (m_hFind != INVALID_HANDLE_VALUE) {
3178b52a31eSHanoh Haim            return SG_SUCCESS;
3188b52a31eSHanoh Haim        }
3198b52a31eSHanoh Haim        uint32_t dwErr = GetLastError();
3208b52a31eSHanoh Haim        if (dwErr == ERROR_FILE_NOT_FOUND) {
3218b52a31eSHanoh Haim            return SG_ERR_NOMATCH;
3228b52a31eSHanoh Haim        }
3238b52a31eSHanoh Haim        return SG_ERR_FAILURE;
3248b52a31eSHanoh Haim    }
3258b52a31eSHanoh Haim   /* int FindFirstFileS(const wchar_t * a_pszFileSpec, unsigned int) {
3268b52a31eSHanoh Haim        m_hFind = FindFirstFileW(a_pszFileSpec, &m_oFindDataW);
3278b52a31eSHanoh Haim        if (m_hFind != INVALID_HANDLE_VALUE) {
3288b52a31eSHanoh Haim            return SG_SUCCESS;
3298b52a31eSHanoh Haim        }
3308b52a31eSHanoh Haim        uint32_t dwErr = GetLastError();
3318b52a31eSHanoh Haim        if (dwErr == ERROR_FILE_NOT_FOUND) {
3328b52a31eSHanoh Haim            return SG_ERR_NOMATCH;
3338b52a31eSHanoh Haim        }
3348b52a31eSHanoh Haim        return SG_ERR_FAILURE;
3358b52a31eSHanoh Haim    }*/
3368b52a31eSHanoh Haim
3378b52a31eSHanoh Haim    bool FindNextFileS(char) {
3388b52a31eSHanoh Haim        return FindNextFileA(m_hFind, &m_oFindDataA) != FALSE;
3398b52a31eSHanoh Haim    }
3408b52a31eSHanoh Haim  /*  bool FindNextFileS(wchar_t) {
3418b52a31eSHanoh Haim        return FindNextFileW(m_hFind, &m_oFindDataW) != FALSE;
3428b52a31eSHanoh Haim    }*/
3438b52a31eSHanoh Haim
3448b52a31eSHanoh Haim    void FindDone() {
3458b52a31eSHanoh Haim        FindClose(m_hFind);
3468b52a31eSHanoh Haim    }
3478b52a31eSHanoh Haim
3488b52a31eSHanoh Haim    const char * GetFileNameS(char) const {
3498b52a31eSHanoh Haim        return m_oFindDataA.cFileName;
3508b52a31eSHanoh Haim    }
3518b52a31eSHanoh Haim    /*const wchar_t * GetFileNameS(wchar_t) const {
3528b52a31eSHanoh Haim        return m_oFindDataW.cFileName;
3538b52a31eSHanoh Haim    }*/
3548b52a31eSHanoh Haim
3558b52a31eSHanoh Haim    bool IsDirS(char) const {
3568b52a31eSHanoh Haim        return GetFileTypeS(m_oFindDataA.dwFileAttributes) == SG_FILETYPE_DIR;
3578b52a31eSHanoh Haim    }
3588b52a31eSHanoh Haim    /*bool IsDirS(wchar_t) const {
3598b52a31eSHanoh Haim        return GetFileTypeS(m_oFindDataW.dwFileAttributes) == SG_FILETYPE_DIR;
3608b52a31eSHanoh Haim    }*/
3618b52a31eSHanoh Haim
3628b52a31eSHanoh Haim    SG_FileType GetFileTypeS(const char * a_pszPath) {
3638b52a31eSHanoh Haim        return GetFileTypeS(GetFileAttributesA(a_pszPath));
3648b52a31eSHanoh Haim    }
3658b52a31eSHanoh Haim    /*SG_FileType GetFileTypeS(const wchar_t * a_pszPath)  {
3668b52a31eSHanoh Haim        return GetFileTypeS(GetFileAttributesW(a_pszPath));
3678b52a31eSHanoh Haim    }*/
3688b52a31eSHanoh Haim    SG_FileType GetFileTypeS(uint32_t a_dwAttribs) const {
3698b52a31eSHanoh Haim        if (a_dwAttribs == INVALID_FILE_ATTRIBUTES) {
3708b52a31eSHanoh Haim            return SG_FILETYPE_INVALID;
3718b52a31eSHanoh Haim        }
3728b52a31eSHanoh Haim        if (a_dwAttribs & FILE_ATTRIBUTE_DIRECTORY) {
3738b52a31eSHanoh Haim            return SG_FILETYPE_DIR;
3748b52a31eSHanoh Haim        }
3758b52a31eSHanoh Haim        return SG_FILETYPE_FILE;
3768b52a31eSHanoh Haim    }
3778b52a31eSHanoh Haimtypedef struct _FILETIME {
3788b52a31eSHanoh Haim    uint32_t dwLowDateTime;
3798b52a31eSHanoh Haim    uint32_t dwHighDateTime;
3808b52a31eSHanoh Haim} FILETIME;
3818b52a31eSHanoh Haim
3828b52a31eSHanoh Haim
3838b52a31eSHanoh Haimtypedef struct _WIN32_FIND_DATAA {
3848b52a31eSHanoh Haim    uint32_t dwFileAttributes;
3858b52a31eSHanoh Haim    FILETIME ftCreationTime;
3868b52a31eSHanoh Haim    FILETIME ftLastAccessTime;
3878b52a31eSHanoh Haim    FILETIME ftLastWriteTime;
3888b52a31eSHanoh Haim    uint32_t nFileSizeHigh;
3898b52a31eSHanoh Haim    uint32_t nFileSizerLow;
3908b52a31eSHanoh Haim    uint32_t dwReserved0;
3918b52a31eSHanoh Haim    uint32_t dwReserved1;
3928b52a31eSHanoh Haim    char     cFileName[MAX_PATH];
3938b52a31eSHanoh Haim    char     cAlternateFileName[14];
3948b52a31eSHanoh Haim} WIN32_FIND_DATAA;
3958b52a31eSHanoh Haim
3968b52a31eSHanoh Haimprivate:
3978b52a31eSHanoh Haim    void *              m_hFind;
3988b52a31eSHanoh Haim    WIN32_FIND_DATAA    m_oFindDataA;
3998b52a31eSHanoh Haim    WIN32_FIND_DATAA    m_oFindDataW;
4008b52a31eSHanoh Haim};
4018b52a31eSHanoh Haim
4028b52a31eSHanoh Haim#else // !WIN32
4038b52a31eSHanoh Haim
4048b52a31eSHanoh Haim#define SG_PATH_CHAR    '/'
4058b52a31eSHanoh Haim
4068b52a31eSHanoh Haim/*! @brief Unix glob implementation. */
4078b52a31eSHanoh Haimtemplate<class SOCHAR>
4088b52a31eSHanoh Haimstruct SimpleGlobBase
4098b52a31eSHanoh Haim{
4108b52a31eSHanoh Haim    SimpleGlobBase() {
4118b52a31eSHanoh Haim        memset(&m_glob, 0, sizeof(m_glob));
4128b52a31eSHanoh Haim        m_uiCurr = (size_t)-1;
4138b52a31eSHanoh Haim    }
4148b52a31eSHanoh Haim
4158b52a31eSHanoh Haim    ~SimpleGlobBase() {
4168b52a31eSHanoh Haim        globfree(&m_glob);
4178b52a31eSHanoh Haim    }
4188b52a31eSHanoh Haim
4198b52a31eSHanoh Haim    void FilePrep() {
4208b52a31eSHanoh Haim        m_bIsDir = false;
4218b52a31eSHanoh Haim        size_t len = strlen(m_glob.gl_pathv[m_uiCurr]);
4228b52a31eSHanoh Haim        if (m_glob.gl_pathv[m_uiCurr][len-1] == '/') {
4238b52a31eSHanoh Haim            m_bIsDir = true;
4248b52a31eSHanoh Haim            m_glob.gl_pathv[m_uiCurr][len-1] = 0;
4258b52a31eSHanoh Haim        }
4268b52a31eSHanoh Haim    }
4278b52a31eSHanoh Haim
4288b52a31eSHanoh Haim    int FindFirstFileS(const char * a_pszFileSpec, unsigned int a_uiFlags) {
4298b52a31eSHanoh Haim        int nFlags = GLOB_MARK | GLOB_NOSORT;
4308b52a31eSHanoh Haim        if (a_uiFlags & SG_GLOB_ERR)    nFlags |= GLOB_ERR;
4318b52a31eSHanoh Haim        if (a_uiFlags & SG_GLOB_TILDE)  nFlags |= GLOB_TILDE;
4328b52a31eSHanoh Haim        int rc = glob(a_pszFileSpec, nFlags, NULL, &m_glob);
4338b52a31eSHanoh Haim        if (rc == GLOB_NOSPACE) return SG_ERR_MEMORY;
4348b52a31eSHanoh Haim        if (rc == GLOB_ABORTED) return SG_ERR_FAILURE;
4358b52a31eSHanoh Haim        if (rc == GLOB_NOMATCH) return SG_ERR_NOMATCH;
4368b52a31eSHanoh Haim        m_uiCurr = 0;
4378b52a31eSHanoh Haim        FilePrep();
4388b52a31eSHanoh Haim        return SG_SUCCESS;
4398b52a31eSHanoh Haim    }
4408b52a31eSHanoh Haim
4418b52a31eSHanoh Haim#if SG_HAVE_ICU
4428b52a31eSHanoh Haim    int FindFirstFileS(const UChar * a_pszFileSpec, unsigned int a_uiFlags) {
4438b52a31eSHanoh Haim        char buf[PATH_MAX] = { 0 };
4448b52a31eSHanoh Haim        UErrorCode status = U_ZERO_ERROR;
4458b52a31eSHanoh Haim        u_strToUTF8(buf, sizeof(buf), NULL, a_pszFileSpec, -1, &status);
4468b52a31eSHanoh Haim        if (U_FAILURE(status)) return SG_ERR_FAILURE;
4478b52a31eSHanoh Haim        return FindFirstFileS(buf, a_uiFlags);
4488b52a31eSHanoh Haim    }
4498b52a31eSHanoh Haim#endif
4508b52a31eSHanoh Haim
4518b52a31eSHanoh Haim    bool FindNextFileS(char) {
4528b52a31eSHanoh Haim        SG_ASSERT(m_uiCurr != (size_t)-1);
4538b52a31eSHanoh Haim        if (++m_uiCurr >= m_glob.gl_pathc) {
4548b52a31eSHanoh Haim            return false;
4558b52a31eSHanoh Haim        }
4568b52a31eSHanoh Haim        FilePrep();
4578b52a31eSHanoh Haim        return true;
4588b52a31eSHanoh Haim    }
4598b52a31eSHanoh Haim
4608b52a31eSHanoh Haim#if SG_HAVE_ICU
4618b52a31eSHanoh Haim    bool FindNextFileS(UChar) {
4628b52a31eSHanoh Haim        return FindNextFileS((char)0);
4638b52a31eSHanoh Haim    }
4648b52a31eSHanoh Haim#endif
4658b52a31eSHanoh Haim
4668b52a31eSHanoh Haim    void FindDone() {
4678b52a31eSHanoh Haim        globfree(&m_glob);
4688b52a31eSHanoh Haim        memset(&m_glob, 0, sizeof(m_glob));
4698b52a31eSHanoh Haim        m_uiCurr = (size_t)-1;
4708b52a31eSHanoh Haim    }
4718b52a31eSHanoh Haim
4728b52a31eSHanoh Haim    const char * GetFileNameS(char) const {
4738b52a31eSHanoh Haim        SG_ASSERT(m_uiCurr != (size_t)-1);
4748b52a31eSHanoh Haim        return m_glob.gl_pathv[m_uiCurr];
4758b52a31eSHanoh Haim    }
4768b52a31eSHanoh Haim
4778b52a31eSHanoh Haim#if SG_HAVE_ICU
4788b52a31eSHanoh Haim    const UChar * GetFileNameS(UChar) const {
4798b52a31eSHanoh Haim        const char * pszFile = GetFileNameS((char)0);
4808b52a31eSHanoh Haim        if (!pszFile) return NULL;
4818b52a31eSHanoh Haim        UErrorCode status = U_ZERO_ERROR;
4828b52a31eSHanoh Haim        memset(m_szBuf, 0, sizeof(m_szBuf));
4838b52a31eSHanoh Haim        u_strFromUTF8(m_szBuf, PATH_MAX, NULL, pszFile, -1, &status);
4848b52a31eSHanoh Haim        if (U_FAILURE(status)) return NULL;
4858b52a31eSHanoh Haim        return m_szBuf;
4868b52a31eSHanoh Haim    }
4878b52a31eSHanoh Haim#endif
4888b52a31eSHanoh Haim
4898b52a31eSHanoh Haim    bool IsDirS(char) const {
4908b52a31eSHanoh Haim        SG_ASSERT(m_uiCurr != (size_t)-1);
4918b52a31eSHanoh Haim        return m_bIsDir;
4928b52a31eSHanoh Haim    }
4938b52a31eSHanoh Haim
4948b52a31eSHanoh Haim#if SG_HAVE_ICU
4958b52a31eSHanoh Haim    bool IsDirS(UChar) const {
4968b52a31eSHanoh Haim        return IsDirS((char)0);
4978b52a31eSHanoh Haim    }
4988b52a31eSHanoh Haim#endif
4998b52a31eSHanoh Haim
5008b52a31eSHanoh Haim    SG_FileType GetFileTypeS(const char * a_pszPath) const {
5018b52a31eSHanoh Haim        struct stat sb;
5028b52a31eSHanoh Haim        if (0 != stat(a_pszPath, &sb)) {
5038b52a31eSHanoh Haim            return SG_FILETYPE_INVALID;
5048b52a31eSHanoh Haim        }
5058b52a31eSHanoh Haim        if (S_ISDIR(sb.st_mode)) {
5068b52a31eSHanoh Haim            return SG_FILETYPE_DIR;
5078b52a31eSHanoh Haim        }
5088b52a31eSHanoh Haim        if (S_ISREG(sb.st_mode)) {
5098b52a31eSHanoh Haim            return SG_FILETYPE_FILE;
5108b52a31eSHanoh Haim        }
5118b52a31eSHanoh Haim        return SG_FILETYPE_INVALID;
5128b52a31eSHanoh Haim    }
5138b52a31eSHanoh Haim
5148b52a31eSHanoh Haim#if SG_HAVE_ICU
5158b52a31eSHanoh Haim    SG_FileType GetFileTypeS(const UChar * a_pszPath) const {
5168b52a31eSHanoh Haim        char buf[PATH_MAX] = { 0 };
5178b52a31eSHanoh Haim        UErrorCode status = U_ZERO_ERROR;
5188b52a31eSHanoh Haim        u_strToUTF8(buf, sizeof(buf), NULL, a_pszPath, -1, &status);
5198b52a31eSHanoh Haim        if (U_FAILURE(status)) return SG_FILETYPE_INVALID;
5208b52a31eSHanoh Haim        return GetFileTypeS(buf);
5218b52a31eSHanoh Haim    }
5228b52a31eSHanoh Haim#endif
5238b52a31eSHanoh Haim
5248b52a31eSHanoh Haimprivate:
5258b52a31eSHanoh Haim    glob_t  m_glob;
5268b52a31eSHanoh Haim    size_t  m_uiCurr;
5278b52a31eSHanoh Haim    bool    m_bIsDir;
5288b52a31eSHanoh Haim#if SG_HAVE_ICU
5298b52a31eSHanoh Haim    mutable UChar m_szBuf[PATH_MAX];
5308b52a31eSHanoh Haim#endif
5318b52a31eSHanoh Haim};
5328b52a31eSHanoh Haim
5338b52a31eSHanoh Haim#endif // WIN32
5348b52a31eSHanoh Haim
5358b52a31eSHanoh Haim#endif // DOXYGEN
5368b52a31eSHanoh Haim
5378b52a31eSHanoh Haim// ---------------------------------------------------------------------------
5388b52a31eSHanoh Haim//                              MAIN TEMPLATE CLASS
5398b52a31eSHanoh Haim// ---------------------------------------------------------------------------
5408b52a31eSHanoh Haim
5418b52a31eSHanoh Haim/*! @brief Implementation of the SimpleGlob class */
5428b52a31eSHanoh Haimtemplate<class SOCHAR>
5438b52a31eSHanoh Haimclass CSimpleGlobTempl : private SimpleGlobBase<SOCHAR>
5448b52a31eSHanoh Haim{
5458b52a31eSHanoh Haimpublic:
5468b52a31eSHanoh Haim    /*! @brief Initialize the class.
5478b52a31eSHanoh Haim
5488b52a31eSHanoh Haim        @param a_uiFlags            Combination of SG_GLOB flags.
5498b52a31eSHanoh Haim        @param a_nReservedSlots     Number of slots in the argv array that
5508b52a31eSHanoh Haim            should be reserved. In the returned array these slots
5518b52a31eSHanoh Haim            argv[0] ... argv[a_nReservedSlots-1] will be left empty for
5528b52a31eSHanoh Haim            the caller to fill in.
5538b52a31eSHanoh Haim     */
5548b52a31eSHanoh Haim    CSimpleGlobTempl(unsigned int a_uiFlags = 0, int a_nReservedSlots = 0);
5558b52a31eSHanoh Haim
5568b52a31eSHanoh Haim    /*! @brief Deallocate all memory buffers. */
5578b52a31eSHanoh Haim    ~CSimpleGlobTempl();
5588b52a31eSHanoh Haim
5598b52a31eSHanoh Haim    /*! @brief Initialize (or re-initialize) the class in preparation for
5608b52a31eSHanoh Haim        adding new filespecs.
5618b52a31eSHanoh Haim
5628b52a31eSHanoh Haim        All existing files are cleared. Note that allocated memory is only
5638b52a31eSHanoh Haim        deallocated at object destruction.
5648b52a31eSHanoh Haim
5658b52a31eSHanoh Haim        @param a_uiFlags            Combination of SG_GLOB flags.
5668b52a31eSHanoh Haim        @param a_nReservedSlots     Number of slots in the argv array that
5678b52a31eSHanoh Haim            should be reserved. In the returned array these slots
5688b52a31eSHanoh Haim            argv[0] ... argv[a_nReservedSlots-1] will be left empty for
5698b52a31eSHanoh Haim            the caller to fill in.
5708b52a31eSHanoh Haim     */
5718b52a31eSHanoh Haim    int Init(unsigned int a_uiFlags = 0, int a_nReservedSlots = 0);
5728b52a31eSHanoh Haim
5738b52a31eSHanoh Haim    /*! @brief Add a new filespec to the glob.
5748b52a31eSHanoh Haim
5758b52a31eSHanoh Haim        The filesystem will be immediately scanned for all matching files and
5768b52a31eSHanoh Haim        directories and they will be added to the glob.
5778b52a31eSHanoh Haim
5788b52a31eSHanoh Haim        @param a_pszFileSpec    Filespec to add to the glob.
5798b52a31eSHanoh Haim
5808b52a31eSHanoh Haim        @return SG_SUCCESS      Matching files were added to the glob.
5818b52a31eSHanoh Haim        @return SG_ERR_NOMATCH  Nothing matched the pattern. To ignore this
5828b52a31eSHanoh Haim                                error compare return value to >= SG_SUCCESS.
5838b52a31eSHanoh Haim        @return SG_ERR_MEMORY   Out of memory failure.
5848b52a31eSHanoh Haim        @return SG_ERR_FAILURE  General failure.
5858b52a31eSHanoh Haim     */
5868b52a31eSHanoh Haim    int Add(const SOCHAR *a_pszFileSpec);
5878b52a31eSHanoh Haim
5888b52a31eSHanoh Haim    /*! @brief Add an array of filespec to the glob.
5898b52a31eSHanoh Haim
5908b52a31eSHanoh Haim        The filesystem will be immediately scanned for all matching files and
5918b52a31eSHanoh Haim        directories in each filespec and they will be added to the glob.
5928b52a31eSHanoh Haim
5938b52a31eSHanoh Haim        @param a_nCount         Number of filespec in the array.
5948b52a31eSHanoh Haim        @param a_rgpszFileSpec  Array of filespec to add to the glob.
5958b52a31eSHanoh Haim
5968b52a31eSHanoh Haim        @return SG_SUCCESS      Matching files were added to the glob.
5978b52a31eSHanoh Haim        @return SG_ERR_NOMATCH  Nothing matched the pattern. To ignore this
5988b52a31eSHanoh Haim                                error compare return value to >= SG_SUCCESS.
5998b52a31eSHanoh Haim        @return SG_ERR_MEMORY   Out of memory failure.
6008b52a31eSHanoh Haim        @return SG_ERR_FAILURE  General failure.
6018b52a31eSHanoh Haim     */
6028b52a31eSHanoh Haim    int Add(int a_nCount, const SOCHAR * const * a_rgpszFileSpec);
6038b52a31eSHanoh Haim
6048b52a31eSHanoh Haim    /*! @brief Return the number of files in the argv array.
6058b52a31eSHanoh Haim     */
6068b52a31eSHanoh Haim    inline int FileCount() const { return m_nArgsLen; }
6078b52a31eSHanoh Haim
6088b52a31eSHanoh Haim    /*! @brief Return the full argv array. */
6098b52a31eSHanoh Haim    inline SOCHAR ** Files() {
6108b52a31eSHanoh Haim        SetArgvArrayType(POINTERS);
6118b52a31eSHanoh Haim        return m_rgpArgs;
6128b52a31eSHanoh Haim    }
6138b52a31eSHanoh Haim
6148b52a31eSHanoh Haim    /*! @brief Return the a single file. */
6158b52a31eSHanoh Haim    inline SOCHAR * File(int n) {
6168b52a31eSHanoh Haim        SG_ASSERT(n >= 0 && n < m_nArgsLen);
6178b52a31eSHanoh Haim        return Files()[n];
6188b52a31eSHanoh Haim    }
6198b52a31eSHanoh Haim
6208b52a31eSHanoh Haimprivate:
6218b52a31eSHanoh Haim    CSimpleGlobTempl(const CSimpleGlobTempl &); // disabled
6228b52a31eSHanoh Haim    CSimpleGlobTempl & operator=(const CSimpleGlobTempl &); // disabled
6238b52a31eSHanoh Haim
6248b52a31eSHanoh Haim    /*! @brief The argv array has it's members stored as either an offset into
6258b52a31eSHanoh Haim        the string buffer, or as pointers to their string in the buffer. The
6268b52a31eSHanoh Haim        offsets are used because if the string buffer is dynamically resized,
6278b52a31eSHanoh Haim        all pointers into that buffer would become invalid.
6288b52a31eSHanoh Haim     */
6298b52a31eSHanoh Haim    enum ARG_ARRAY_TYPE { OFFSETS, POINTERS };
6308b52a31eSHanoh Haim
6318b52a31eSHanoh Haim    /*! @brief Change the type of data stored in the argv array. */
6328b52a31eSHanoh Haim    void SetArgvArrayType(ARG_ARRAY_TYPE a_nNewType);
6338b52a31eSHanoh Haim
6348b52a31eSHanoh Haim    /*! @brief Add a filename to the array if it passes all requirements. */
6358b52a31eSHanoh Haim    int AppendName(const SOCHAR *a_pszFileName, bool a_bIsDir);
6368b52a31eSHanoh Haim
6378b52a31eSHanoh Haim    /*! @brief Grow the argv array to the required size. */
6388b52a31eSHanoh Haim    bool GrowArgvArray(int a_nNewLen);
6398b52a31eSHanoh Haim
6408b52a31eSHanoh Haim    /*! @brief Grow the string buffer to the required size. */
6418b52a31eSHanoh Haim    bool GrowStringBuffer(size_t a_uiMinSize);
6428b52a31eSHanoh Haim
6438b52a31eSHanoh Haim    /*! @brief Compare two (possible NULL) strings */
6448b52a31eSHanoh Haim    static int fileSortCompare(const void *a1, const void *a2);
6458b52a31eSHanoh Haim
6468b52a31eSHanoh Haimprivate:
6478b52a31eSHanoh Haim    unsigned int        m_uiFlags;
6488b52a31eSHanoh Haim    ARG_ARRAY_TYPE      m_nArgArrayType;    //!< argv is indexes or pointers
6498b52a31eSHanoh Haim    SOCHAR **           m_rgpArgs;          //!< argv
6508b52a31eSHanoh Haim    int                 m_nReservedSlots;   //!< # client slots in argv array
6518b52a31eSHanoh Haim    int                 m_nArgsSize;        //!< allocated size of array
6528b52a31eSHanoh Haim    int                 m_nArgsLen;         //!< used length
6538b52a31eSHanoh Haim    SOCHAR *            m_pBuffer;          //!< argv string buffer
6548b52a31eSHanoh Haim    size_t              m_uiBufferSize;     //!< allocated size of buffer
6558b52a31eSHanoh Haim    size_t              m_uiBufferLen;      //!< used length of buffer
6568b52a31eSHanoh Haim    SOCHAR              m_szPathPrefix[MAX_PATH]; //!< wildcard path prefix
6578b52a31eSHanoh Haim};
6588b52a31eSHanoh Haim
6598b52a31eSHanoh Haim// ---------------------------------------------------------------------------
6608b52a31eSHanoh Haim//                                  IMPLEMENTATION
6618b52a31eSHanoh Haim// ---------------------------------------------------------------------------
6628b52a31eSHanoh Haim
6638b52a31eSHanoh Haimtemplate<class SOCHAR>
6648b52a31eSHanoh HaimCSimpleGlobTempl<SOCHAR>::CSimpleGlobTempl(
6658b52a31eSHanoh Haim    unsigned int    a_uiFlags,
6668b52a31eSHanoh Haim    int             a_nReservedSlots
6678b52a31eSHanoh Haim    )
6688b52a31eSHanoh Haim{
6698b52a31eSHanoh Haim    m_rgpArgs           = NULL;
6708b52a31eSHanoh Haim    m_nArgsSize         = 0;
6718b52a31eSHanoh Haim    m_pBuffer           = NULL;
6728b52a31eSHanoh Haim    m_uiBufferSize      = 0;
6738b52a31eSHanoh Haim
6748b52a31eSHanoh Haim    Init(a_uiFlags, a_nReservedSlots);
6758b52a31eSHanoh Haim}
6768b52a31eSHanoh Haim
6778b52a31eSHanoh Haimtemplate<class SOCHAR>
6788b52a31eSHanoh HaimCSimpleGlobTempl<SOCHAR>::~CSimpleGlobTempl()
6798b52a31eSHanoh Haim{
6808b52a31eSHanoh Haim    if (m_rgpArgs) free(m_rgpArgs);
6818b52a31eSHanoh Haim    if (m_pBuffer) free(m_pBuffer);
6828b52a31eSHanoh Haim}
6838b52a31eSHanoh Haim
6848b52a31eSHanoh Haimtemplate<class SOCHAR>
6858b52a31eSHanoh Haimint
6868b52a31eSHanoh HaimCSimpleGlobTempl<SOCHAR>::Init(
6878b52a31eSHanoh Haim    unsigned int    a_uiFlags,
6888b52a31eSHanoh Haim    int             a_nReservedSlots
6898b52a31eSHanoh Haim    )
6908b52a31eSHanoh Haim{
6918b52a31eSHanoh Haim    m_nArgArrayType     = POINTERS;
6928b52a31eSHanoh Haim    m_uiFlags           = a_uiFlags;
6938b52a31eSHanoh Haim    m_nArgsLen          = a_nReservedSlots;
6948b52a31eSHanoh Haim    m_nReservedSlots    = a_nReservedSlots;
6958b52a31eSHanoh Haim    m_uiBufferLen       = 0;
6968b52a31eSHanoh Haim
6978b52a31eSHanoh Haim    if (m_nReservedSlots > 0) {
6988b52a31eSHanoh Haim        if (!GrowArgvArray(m_nReservedSlots)) {
6998b52a31eSHanoh Haim            return SG_ERR_MEMORY;
7008b52a31eSHanoh Haim        }
7018b52a31eSHanoh Haim        for (int n = 0; n < m_nReservedSlots; ++n) {
7028b52a31eSHanoh Haim            m_rgpArgs[n] = NULL;
7038b52a31eSHanoh Haim        }
7048b52a31eSHanoh Haim    }
7058b52a31eSHanoh Haim
7068b52a31eSHanoh Haim    return SG_SUCCESS;
7078b52a31eSHanoh Haim}
7088b52a31eSHanoh Haim
7098b52a31eSHanoh Haimtemplate<class SOCHAR>
7108b52a31eSHanoh Haimint
7118b52a31eSHanoh HaimCSimpleGlobTempl<SOCHAR>::Add(
7128b52a31eSHanoh Haim    const SOCHAR *a_pszFileSpec
7138b52a31eSHanoh Haim    )
7148b52a31eSHanoh Haim{
7158b52a31eSHanoh Haim#ifdef WIN32
7168b52a31eSHanoh Haim    // Windows FindFirst/FindNext recognizes forward slash as the same as
7178b52a31eSHanoh Haim    // backward slash and follows the directories. We need to do the same
7188b52a31eSHanoh Haim    // when calculating the prefix and when we have no wildcards.
7198b52a31eSHanoh Haim    SOCHAR szFileSpec[MAX_PATH];
7208b52a31eSHanoh Haim    SimpleGlobUtil::strcpy_s(szFileSpec, MAX_PATH, a_pszFileSpec);
7218b52a31eSHanoh Haim    const SOCHAR * pszPath = SimpleGlobUtil::strchr(szFileSpec, '/');
7228b52a31eSHanoh Haim    while (pszPath) {
7238b52a31eSHanoh Haim        szFileSpec[pszPath - szFileSpec] = SG_PATH_CHAR;
7248b52a31eSHanoh Haim        pszPath = SimpleGlobUtil::strchr(pszPath + 1, '/');
7258b52a31eSHanoh Haim    }
7268b52a31eSHanoh Haim    a_pszFileSpec = szFileSpec;
7278b52a31eSHanoh Haim#endif
7288b52a31eSHanoh Haim
7298b52a31eSHanoh Haim    // if this doesn't contain wildcards then we can just add it directly
7308b52a31eSHanoh Haim    m_szPathPrefix[0] = 0;
7318b52a31eSHanoh Haim    if (!SimpleGlobUtil::strchr(a_pszFileSpec, '*') &&
7328b52a31eSHanoh Haim        !SimpleGlobUtil::strchr(a_pszFileSpec, '?'))
7338b52a31eSHanoh Haim    {
7348b52a31eSHanoh Haim        SG_FileType nType = GetFileTypeS(a_pszFileSpec);
7358b52a31eSHanoh Haim        if (nType == SG_FILETYPE_INVALID) {
7368b52a31eSHanoh Haim            if (m_uiFlags & SG_GLOB_NOCHECK) {
7378b52a31eSHanoh Haim                return AppendName(a_pszFileSpec, false);
7388b52a31eSHanoh Haim            }
7398b52a31eSHanoh Haim            return SG_ERR_NOMATCH;
7408b52a31eSHanoh Haim        }
7418b52a31eSHanoh Haim        return AppendName(a_pszFileSpec, nType == SG_FILETYPE_DIR);
7428b52a31eSHanoh Haim    }
7438b52a31eSHanoh Haim
7448b52a31eSHanoh Haim#ifdef WIN32
7458b52a31eSHanoh Haim    // Windows doesn't return the directory with the filename, so we need to
7468b52a31eSHanoh Haim    // extract the path from the search string ourselves and prefix it to the
7478b52a31eSHanoh Haim    // filename we get back.
7488b52a31eSHanoh Haim    const SOCHAR * pszFilename =
7498b52a31eSHanoh Haim        SimpleGlobUtil::strrchr(a_pszFileSpec, SG_PATH_CHAR);
7508b52a31eSHanoh Haim    if (pszFilename) {
7518b52a31eSHanoh Haim        SimpleGlobUtil::strcpy_s(m_szPathPrefix, MAX_PATH, a_pszFileSpec);
7528b52a31eSHanoh Haim        m_szPathPrefix[pszFilename - a_pszFileSpec + 1] = 0;
7538b52a31eSHanoh Haim    }
7548b52a31eSHanoh Haim#endif
7558b52a31eSHanoh Haim
7568b52a31eSHanoh Haim    // search for the first match on the file
7578b52a31eSHanoh Haim    int rc = FindFirstFileS(a_pszFileSpec, m_uiFlags);
7588b52a31eSHanoh Haim    if (rc != SG_SUCCESS) {
7598b52a31eSHanoh Haim        if (rc == SG_ERR_NOMATCH && (m_uiFlags & SG_GLOB_NOCHECK)) {
7608b52a31eSHanoh Haim            int ok = AppendName(a_pszFileSpec, false);
7618b52a31eSHanoh Haim            if (ok != SG_SUCCESS) rc = ok;
7628b52a31eSHanoh Haim        }
7638b52a31eSHanoh Haim        return rc;
7648b52a31eSHanoh Haim    }
7658b52a31eSHanoh Haim
7668b52a31eSHanoh Haim    // add it and find all subsequent matches
7678b52a31eSHanoh Haim    int nError, nStartLen = m_nArgsLen;
7688b52a31eSHanoh Haim    bool bSuccess;
7698b52a31eSHanoh Haim    do {
7708b52a31eSHanoh Haim        nError = AppendName(GetFileNameS((SOCHAR)0), IsDirS((SOCHAR)0));
7718b52a31eSHanoh Haim        bSuccess = FindNextFileS((SOCHAR)0);
7728b52a31eSHanoh Haim    }
7738b52a31eSHanoh Haim    while (nError == SG_SUCCESS && bSuccess);
7748b52a31eSHanoh Haim    SimpleGlobBase<SOCHAR>::FindDone();
7758b52a31eSHanoh Haim
7768b52a31eSHanoh Haim    // sort these files if required
7778b52a31eSHanoh Haim    if (m_nArgsLen > nStartLen && !(m_uiFlags & SG_GLOB_NOSORT)) {
7788b52a31eSHanoh Haim        if (m_uiFlags & SG_GLOB_FULLSORT) {
7798b52a31eSHanoh Haim            nStartLen = m_nReservedSlots;
7808b52a31eSHanoh Haim        }
7818b52a31eSHanoh Haim        SetArgvArrayType(POINTERS);
7828b52a31eSHanoh Haim        qsort(
7838b52a31eSHanoh Haim            m_rgpArgs + nStartLen,
7848b52a31eSHanoh Haim            m_nArgsLen - nStartLen,
7858b52a31eSHanoh Haim            sizeof(m_rgpArgs[0]), fileSortCompare);
7868b52a31eSHanoh Haim    }
7878b52a31eSHanoh Haim
7888b52a31eSHanoh Haim    return nError;
7898b52a31eSHanoh Haim}
7908b52a31eSHanoh Haim
7918b52a31eSHanoh Haimtemplate<class SOCHAR>
7928b52a31eSHanoh Haimint
7938b52a31eSHanoh HaimCSimpleGlobTempl<SOCHAR>::Add(
7948b52a31eSHanoh Haim    int                     a_nCount,
7958b52a31eSHanoh Haim    const SOCHAR * const *  a_rgpszFileSpec
7968b52a31eSHanoh Haim    )
7978b52a31eSHanoh Haim{
7988b52a31eSHanoh Haim    int nResult;
7998b52a31eSHanoh Haim    for (int n = 0; n < a_nCount; ++n) {
8008b52a31eSHanoh Haim        nResult = Add(a_rgpszFileSpec[n]);
8018b52a31eSHanoh Haim        if (nResult != SG_SUCCESS) {
8028b52a31eSHanoh Haim            return nResult;
8038b52a31eSHanoh Haim        }
8048b52a31eSHanoh Haim    }
8058b52a31eSHanoh Haim    return SG_SUCCESS;
8068b52a31eSHanoh Haim}
8078b52a31eSHanoh Haim
8088b52a31eSHanoh Haimtemplate<class SOCHAR>
8098b52a31eSHanoh Haimint
8108b52a31eSHanoh HaimCSimpleGlobTempl<SOCHAR>::AppendName(
8118b52a31eSHanoh Haim    const SOCHAR *  a_pszFileName,
8128b52a31eSHanoh Haim    bool            a_bIsDir
8138b52a31eSHanoh Haim    )
8148b52a31eSHanoh Haim{
8158b52a31eSHanoh Haim    // we need the argv array as offsets in case we resize it
8168b52a31eSHanoh Haim    SetArgvArrayType(OFFSETS);
8178b52a31eSHanoh Haim
8188b52a31eSHanoh Haim    // check for special cases which cause us to ignore this entry
8198b52a31eSHanoh Haim    if ((m_uiFlags & SG_GLOB_ONLYDIR) && !a_bIsDir) {
8208b52a31eSHanoh Haim        return SG_SUCCESS;
8218b52a31eSHanoh Haim    }
8228b52a31eSHanoh Haim    if ((m_uiFlags & SG_GLOB_ONLYFILE) && a_bIsDir) {
8238b52a31eSHanoh Haim        return SG_SUCCESS;
8248b52a31eSHanoh Haim    }
8258b52a31eSHanoh Haim    if ((m_uiFlags & SG_GLOB_NODOT) && a_bIsDir) {
8268b52a31eSHanoh Haim        if (a_pszFileName[0] == '.') {
8278b52a31eSHanoh Haim            if (a_pszFileName[1] == '\0') {
8288b52a31eSHanoh Haim                return SG_SUCCESS;
8298b52a31eSHanoh Haim            }
8308b52a31eSHanoh Haim            if (a_pszFileName[1] == '.' && a_pszFileName[2] == '\0') {
8318b52a31eSHanoh Haim                return SG_SUCCESS;
8328b52a31eSHanoh Haim            }
8338b52a31eSHanoh Haim        }
8348b52a31eSHanoh Haim    }
8358b52a31eSHanoh Haim
8368b52a31eSHanoh Haim    // ensure that we have enough room in the argv array
8378b52a31eSHanoh Haim    if (!GrowArgvArray(m_nArgsLen + 1)) {
8388b52a31eSHanoh Haim        return SG_ERR_MEMORY;
8398b52a31eSHanoh Haim    }
8408b52a31eSHanoh Haim
8418b52a31eSHanoh Haim    // ensure that we have enough room in the string buffer (+1 for null)
8428b52a31eSHanoh Haim    size_t uiPrefixLen = SimpleGlobUtil::strlen(m_szPathPrefix);
8438b52a31eSHanoh Haim    size_t uiLen = uiPrefixLen + SimpleGlobUtil::strlen(a_pszFileName) + 1;
8448b52a31eSHanoh Haim    if (a_bIsDir && (m_uiFlags & SG_GLOB_MARK) == SG_GLOB_MARK) {
8458b52a31eSHanoh Haim        ++uiLen;    // need space for the backslash
8468b52a31eSHanoh Haim    }
8478b52a31eSHanoh Haim    if (!GrowStringBuffer(m_uiBufferLen + uiLen)) {
8488b52a31eSHanoh Haim        return SG_ERR_MEMORY;
8498b52a31eSHanoh Haim    }
8508b52a31eSHanoh Haim
8518b52a31eSHanoh Haim    // add this entry. m_uiBufferLen is offset from beginning of buffer.
8528b52a31eSHanoh Haim    m_rgpArgs[m_nArgsLen++] = (SOCHAR*)m_uiBufferLen;
8538b52a31eSHanoh Haim    SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen,
8548b52a31eSHanoh Haim        m_uiBufferSize - m_uiBufferLen, m_szPathPrefix);
8558b52a31eSHanoh Haim    SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen + uiPrefixLen,
8568b52a31eSHanoh Haim        m_uiBufferSize - m_uiBufferLen - uiPrefixLen, a_pszFileName);
8578b52a31eSHanoh Haim    m_uiBufferLen += uiLen;
8588b52a31eSHanoh Haim
8598b52a31eSHanoh Haim    // add the directory slash if desired
8608b52a31eSHanoh Haim    if (a_bIsDir && (m_uiFlags & SG_GLOB_MARK) == SG_GLOB_MARK) {
8618b52a31eSHanoh Haim        const static SOCHAR szDirSlash[] = { SG_PATH_CHAR, 0 };
8628b52a31eSHanoh Haim        SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen - 2,
8638b52a31eSHanoh Haim            m_uiBufferSize - (m_uiBufferLen - 2), szDirSlash);
8648b52a31eSHanoh Haim    }
8658b52a31eSHanoh Haim
8668b52a31eSHanoh Haim    return SG_SUCCESS;
8678b52a31eSHanoh Haim}
8688b52a31eSHanoh Haim
8698b52a31eSHanoh Haimtemplate<class SOCHAR>
8708b52a31eSHanoh Haimvoid
8718b52a31eSHanoh HaimCSimpleGlobTempl<SOCHAR>::SetArgvArrayType(
8728b52a31eSHanoh Haim    ARG_ARRAY_TYPE  a_nNewType
8738b52a31eSHanoh Haim    )
8748b52a31eSHanoh Haim{
8758b52a31eSHanoh Haim    if (m_nArgArrayType == a_nNewType) return;
8768b52a31eSHanoh Haim    if (a_nNewType == POINTERS) {
8778b52a31eSHanoh Haim        SG_ASSERT(m_nArgArrayType == OFFSETS);
8788b52a31eSHanoh Haim        for (int n = 0; n < m_nArgsLen; ++n) {
8798b52a31eSHanoh Haim            m_rgpArgs[n] = (m_rgpArgs[n] == (SOCHAR*)-1) ?
8808b52a31eSHanoh Haim                NULL : m_pBuffer + (size_t) m_rgpArgs[n];
8818b52a31eSHanoh Haim        }
8828b52a31eSHanoh Haim    }
8838b52a31eSHanoh Haim    else {
8848b52a31eSHanoh Haim        SG_ASSERT(a_nNewType == OFFSETS);
8858b52a31eSHanoh Haim        SG_ASSERT(m_nArgArrayType == POINTERS);
8868b52a31eSHanoh Haim        for (int n = 0; n < m_nArgsLen; ++n) {
8878b52a31eSHanoh Haim            m_rgpArgs[n] = (m_rgpArgs[n] == NULL) ?
8888b52a31eSHanoh Haim                (SOCHAR*) -1 : (SOCHAR*) (m_rgpArgs[n] - m_pBuffer);
8898b52a31eSHanoh Haim        }
8908b52a31eSHanoh Haim    }
8918b52a31eSHanoh Haim    m_nArgArrayType = a_nNewType;
8928b52a31eSHanoh Haim}
8938b52a31eSHanoh Haim
8948b52a31eSHanoh Haimtemplate<class SOCHAR>
8958b52a31eSHanoh Haimbool
8968b52a31eSHanoh HaimCSimpleGlobTempl<SOCHAR>::GrowArgvArray(
8978b52a31eSHanoh Haim    int a_nNewLen
8988b52a31eSHanoh Haim    )
8998b52a31eSHanoh Haim{
9008b52a31eSHanoh Haim    if (a_nNewLen >= m_nArgsSize) {
9018b52a31eSHanoh Haim        static const int SG_ARGV_INITIAL_SIZE = 32;
9028b52a31eSHanoh Haim        int nNewSize = (m_nArgsSize > 0) ?
9038b52a31eSHanoh Haim            m_nArgsSize * 2 : SG_ARGV_INITIAL_SIZE;
9048b52a31eSHanoh Haim        while (a_nNewLen >= nNewSize) {
9058b52a31eSHanoh Haim            nNewSize *= 2;
9068b52a31eSHanoh Haim        }
9078b52a31eSHanoh Haim        void * pNewBuffer = realloc(m_rgpArgs, nNewSize * sizeof(SOCHAR*));
9088b52a31eSHanoh Haim        if (!pNewBuffer) return false;
9098b52a31eSHanoh Haim        m_nArgsSize = nNewSize;
9108b52a31eSHanoh Haim        m_rgpArgs = (SOCHAR**) pNewBuffer;
9118b52a31eSHanoh Haim    }
9128b52a31eSHanoh Haim    return true;
9138b52a31eSHanoh Haim}
9148b52a31eSHanoh Haim
9158b52a31eSHanoh Haimtemplate<class SOCHAR>
9168b52a31eSHanoh Haimbool
9178b52a31eSHanoh HaimCSimpleGlobTempl<SOCHAR>::GrowStringBuffer(
9188b52a31eSHanoh Haim    size_t a_uiMinSize
9198b52a31eSHanoh Haim    )
9208b52a31eSHanoh Haim{
9218b52a31eSHanoh Haim    if (a_uiMinSize >= m_uiBufferSize) {
9228b52a31eSHanoh Haim        static const int SG_BUFFER_INITIAL_SIZE = 1024;
9238b52a31eSHanoh Haim        size_t uiNewSize = (m_uiBufferSize > 0) ?
9248b52a31eSHanoh Haim            m_uiBufferSize * 2 : SG_BUFFER_INITIAL_SIZE;
9258b52a31eSHanoh Haim        while (a_uiMinSize >= uiNewSize) {
9268b52a31eSHanoh Haim            uiNewSize *= 2;
9278b52a31eSHanoh Haim        }
9288b52a31eSHanoh Haim        void * pNewBuffer = realloc(m_pBuffer, uiNewSize * sizeof(SOCHAR));
9298b52a31eSHanoh Haim        if (!pNewBuffer) return false;
9308b52a31eSHanoh Haim        m_uiBufferSize = uiNewSize;
9318b52a31eSHanoh Haim        m_pBuffer = (SOCHAR*) pNewBuffer;
9328b52a31eSHanoh Haim    }
9338b52a31eSHanoh Haim    return true;
9348b52a31eSHanoh Haim}
9358b52a31eSHanoh Haim
9368b52a31eSHanoh Haimtemplate<class SOCHAR>
9378b52a31eSHanoh Haimint
9388b52a31eSHanoh HaimCSimpleGlobTempl<SOCHAR>::fileSortCompare(
9398b52a31eSHanoh Haim    const void *a1,
9408b52a31eSHanoh Haim    const void *a2
9418b52a31eSHanoh Haim    )
9428b52a31eSHanoh Haim{
9438b52a31eSHanoh Haim    const SOCHAR * s1 = *(const SOCHAR **)a1;
9448b52a31eSHanoh Haim    const SOCHAR * s2 = *(const SOCHAR **)a2;
9458b52a31eSHanoh Haim    if (s1 && s2) {
9468b52a31eSHanoh Haim        return SimpleGlobUtil::strcasecmp(s1, s2);
9478b52a31eSHanoh Haim    }
9488b52a31eSHanoh Haim    // NULL sorts first
9498b52a31eSHanoh Haim    return s1 == s2 ? 0 : (s1 ? 1 : -1);
9508b52a31eSHanoh Haim}
9518b52a31eSHanoh Haim
9528b52a31eSHanoh Haim// ---------------------------------------------------------------------------
9538b52a31eSHanoh Haim//                                  TYPE DEFINITIONS
9548b52a31eSHanoh Haim// ---------------------------------------------------------------------------
9558b52a31eSHanoh Haim
9568b52a31eSHanoh Haim/*! @brief ASCII/MBCS version of CSimpleGlob */
9578b52a31eSHanoh Haimtypedef CSimpleGlobTempl<char>    CSimpleGlobA;
9588b52a31eSHanoh Haim
9598b52a31eSHanoh Haim/*! @brief wchar_t version of CSimpleGlob */
9608b52a31eSHanoh Haimtypedef CSimpleGlobTempl<wchar_t> CSimpleGlobW;
9618b52a31eSHanoh Haim
9628b52a31eSHanoh Haim#if SG_HAVE_ICU
9638b52a31eSHanoh Haim/*! @brief UChar version of CSimpleGlob */
9648b52a31eSHanoh Haimtypedef CSimpleGlobTempl<UChar> CSimpleGlobU;
9658b52a31eSHanoh Haim#endif
9668b52a31eSHanoh Haim
9678b52a31eSHanoh Haim#ifdef _UNICODE
9688b52a31eSHanoh Haim/*! @brief TCHAR version dependent on if _UNICODE is defined */
9698b52a31eSHanoh Haim# if SG_HAVE_ICU
9708b52a31eSHanoh Haim#  define CSimpleGlob CSimpleGlobU
9718b52a31eSHanoh Haim# else
9728b52a31eSHanoh Haim#  define CSimpleGlob CSimpleGlobW
9738b52a31eSHanoh Haim# endif
9748b52a31eSHanoh Haim#else
9758b52a31eSHanoh Haim/*! @brief TCHAR version dependent on if _UNICODE is defined */
9768b52a31eSHanoh Haim# define CSimpleGlob CSimpleGlobA
9778b52a31eSHanoh Haim#endif
9788b52a31eSHanoh Haim
9798b52a31eSHanoh Haim#endif // INCLUDED_SimpleGlob