From ac61c3044ae319e9bc153a8ab8e92a1aeb9253ae Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Sun, 14 Dec 2014 15:55:03 +0000 Subject: [PATCH] added qse_globmbs() and qse_globwcs() --- qse/include/qse/cmn/glob.h | 59 ++- qse/include/qse/cmn/path.h | 19 +- qse/include/qse/cmn/str.h | 9 +- qse/lib/cmn/Makefile.am | 1 + qse/lib/cmn/Makefile.in | 1 + qse/lib/cmn/glob.c | 722 ++++++++----------------------------- qse/lib/cmn/glob.h | 590 ++++++++++++++++++++++++++++++ qse/lib/cmn/pio.c | 2 +- 8 files changed, 794 insertions(+), 609 deletions(-) create mode 100644 qse/lib/cmn/glob.h diff --git a/qse/include/qse/cmn/glob.h b/qse/include/qse/cmn/glob.h index 8fc1a78d..88108a47 100644 --- a/qse/include/qse/cmn/glob.h +++ b/qse/include/qse/cmn/glob.h @@ -35,12 +35,17 @@ * in a path name. */ -typedef int (*qse_glob_cbimpl_t) ( - const qse_cstr_t* path, - void* cbctx +typedef int (*qse_glob_mbscbimpl_t) ( + const qse_mcstr_t* path, + void* cbctx ); -enum qse_glob_flags_t +typedef int (*qse_glob_wcscbimpl_t) ( + const qse_wcstr_t* path, + void* cbctx +); + +enum qse_glob_flag_t { /** Don't use the backslash as an escape charcter. * This option is on in Win32/OS2/DOS. */ @@ -58,30 +63,58 @@ enum qse_glob_flags_t /** Exclude special entries from matching. * Special entries include . and .. */ - QSE_GLOB_SKIPSPCDIR = (1 << 4) + QSE_GLOB_SKIPSPCDIR = (1 << 4), + + + /** + * bitsise-ORed of all valid enumerators + */ + QSE_GLOB_ALL = (QSE_GLOB_NOESCAPE | QSE_GLOB_PERIOD | + QSE_GLOB_IGNORECASE | QSE_GLOB_TOLERANT | + QSE_GLOB_SKIPSPCDIR) }; +typedef enum qse_glob_flag_t qse_glob_flag_t; #if defined(__cplusplus) extern "C" { #endif /** - * The qse_glob() function finds path names matchin the \a pattern. + * The qse_globmbs() function finds path names matchin the \a pattern. * It calls the call-back function \a cbimpl for each path name found. * * \return -1 on failure, 0 on no match, 1 if matches are found. */ -QSE_EXPORT int qse_glob ( - const qse_char_t* pattern, - qse_glob_cbimpl_t cbimpl, - void* cbctx, - int flags, - qse_mmgr_t* mmgr, - qse_cmgr_t* cmgr +QSE_EXPORT int qse_globmbs ( + const qse_mchar_t* pattern, + qse_glob_mbscbimpl_t cbimpl, + void* cbctx, + int flags, + qse_mmgr_t* mmgr, + qse_cmgr_t* cmgr ); +QSE_EXPORT int qse_globwcs ( + const qse_wchar_t* pattern, + qse_glob_wcscbimpl_t cbimpl, + void* cbctx, + int flags, + qse_mmgr_t* mmgr, + qse_cmgr_t* cmgr +); + +#if defined(QSE_CHAR_IS_MCHAR) + typedef qse_glob_mbscbimpl_t qse_glob_cbimpl_t; +# define qse_glob(pattern,cbimpl,cbctx,flags,mmgr,cmgr) qse_globmbs(pattern,cbimpl,cbctx,flags,mmgr,cmgr) + +#else + typedef qse_glob_wcscbimpl_t qse_glob_cbimpl_t; +# define qse_glob(pattern,cbimpl,cbctx,flags,mmgr,cmgr) qse_globwcs(pattern,cbimpl,cbctx,flags,mmgr,cmgr) +#endif + #if defined(__cplusplus) } #endif #endif + diff --git a/qse/include/qse/cmn/path.h b/qse/include/qse/cmn/path.h index c675b0ac..c87e48c1 100644 --- a/qse/include/qse/cmn/path.h +++ b/qse/include/qse/cmn/path.h @@ -50,22 +50,21 @@ enum qse_canonpath_flag_t # define QSE_ISPATHMBSEP(c) ((c) == QSE_MT('/') || (c) == QSE_MT('\\')) # define QSE_ISPATHWCSEP(c) ((c) == QSE_WT('/') || (c) == QSE_WT('\\')) -# define QSE_ISPATHMBDRIVE(s) \ - (((s[0] >= QSE_MT('A') && s[0] <= QSE_MT('Z')) || \ - (s[0] >= QSE_MT('a') && s[0] <= QSE_MT('z'))) && \ - s[1] == QSE_MT(':')) -# define QSE_ISPATHWCDRIVE(s) \ - (((s[0] >= QSE_WT('A') && s[0] <= QSE_WT('Z')) || \ - (s[0] >= QSE_WT('a') && s[0] <= QSE_WT('z'))) && \ - s[1] == QSE_WT(':')) - #else # define QSE_ISPATHMBSEP(c) ((c) == QSE_MT('/')) # define QSE_ISPATHWCSEP(c) ((c) == QSE_WT('/')) - /* QSE_ISPATHMBDRIVE() and QSE_ISPATHWCDRIVE() are not defined for this platform */ #endif +#define QSE_ISPATHMBDRIVE(s) \ + (((s[0] >= QSE_MT('A') && s[0] <= QSE_MT('Z')) || \ + (s[0] >= QSE_MT('a') && s[0] <= QSE_MT('z'))) && \ + s[1] == QSE_MT(':')) +#define QSE_ISPATHWCDRIVE(s) \ + (((s[0] >= QSE_WT('A') && s[0] <= QSE_WT('Z')) || \ + (s[0] >= QSE_WT('a') && s[0] <= QSE_WT('z'))) && \ + s[1] == QSE_WT(':')) + #define QSE_ISPATHMBSEPORNIL(c) (QSE_ISPATHMBSEP(c) || (c) == QSE_MT('\0')) #define QSE_ISPATHWCSEPORNIL(c) (QSE_ISPATHWCSEP(c) || (c) == QSE_WT('\0')) diff --git a/qse/include/qse/cmn/str.h b/qse/include/qse/cmn/str.h index 647405c8..01ca02aa 100644 --- a/qse/include/qse/cmn/str.h +++ b/qse/include/qse/cmn/str.h @@ -3259,12 +3259,12 @@ QSE_EXPORT qse_size_t qse_wcs_fmt ( ); #if defined(QSE_CHAR_IS_MCHAR) -# define qse_str_setmmgr(str,mmgr) qse_mbs_wetmmgr(str,mmgr) -# define qse_str_getmmgr(str) qse_mbs_getmmgr(str) # define qse_str_open(mmgr,ext,capa) qse_mbs_open(mmgr,ext,capa) # define qse_str_close(str) qse_mbs_close(str) # define qse_str_init(str,mmgr,capa) qse_mbs_init(str,mmgr,capa) # define qse_str_fini(str) qse_mbs_fini(str) +# define qse_str_setmmgr(str,mmgr) qse_mbs_setmmgr(str,mmgr) +# define qse_str_getmmgr(str) qse_mbs_getmmgr(str) # define qse_str_yield(str,buf,ncapa) qse_mbs_yield(str,buf,ncapa) # define qse_str_yieldptr(str,ncapa) qse_mbs_yieldptr(str,ncapa) # define qse_str_getsizer(str) qse_mbs_getsizer(str) @@ -3291,12 +3291,12 @@ QSE_EXPORT qse_size_t qse_wcs_fmt ( # define qse_str_fmt qse_mbs_fmt # define qse_str_vfmt qse_mbs_vfmt #else -# define qse_str_setmmgr(str,mmgr) qse_wcs_wetmmgr(str,mmgr) -# define qse_str_getmmgr(str) qse_wcs_getmmgr(str) # define qse_str_open(mmgr,ext,capa) qse_wcs_open(mmgr,ext,capa) # define qse_str_close(str) qse_wcs_close(str) # define qse_str_init(str,mmgr,capa) qse_wcs_init(str,mmgr,capa) # define qse_str_fini(str) qse_wcs_fini(str) +# define qse_str_setmmgr(str,mmgr) qse_wcs_setmmgr(str,mmgr) +# define qse_str_getmmgr(str) qse_wcs_getmmgr(str) # define qse_str_yield(str,buf,ncapa) qse_wcs_yield(str,buf,ncapa) # define qse_str_yieldptr(str,ncapa) qse_wcs_yieldptr(str,ncapa) # define qse_str_getsizer(str) qse_wcs_getsizer(str) @@ -3324,7 +3324,6 @@ QSE_EXPORT qse_size_t qse_wcs_fmt ( # define qse_str_vfmt qse_wcs_vfmt #endif - #if defined(__cplusplus) } #endif diff --git a/qse/lib/cmn/Makefile.am b/qse/lib/cmn/Makefile.am index 26af3341..52d10e14 100644 --- a/qse/lib/cmn/Makefile.am +++ b/qse/lib/cmn/Makefile.am @@ -13,6 +13,7 @@ noinst_HEADERS = \ fmt-intmax.h \ fmt-out.h \ fs.h \ + glob.h \ mem.h \ str-dyn.h \ str-fcpy.h \ diff --git a/qse/lib/cmn/Makefile.in b/qse/lib/cmn/Makefile.in index 9e152707..28bdd2b6 100644 --- a/qse/lib/cmn/Makefile.in +++ b/qse/lib/cmn/Makefile.in @@ -397,6 +397,7 @@ noinst_HEADERS = \ fmt-intmax.h \ fmt-out.h \ fs.h \ + glob.h \ mem.h \ str-dyn.h \ str-fcpy.h \ diff --git a/qse/lib/cmn/glob.c b/qse/lib/cmn/glob.c index 83a497e7..7b3b6f58 100644 --- a/qse/lib/cmn/glob.c +++ b/qse/lib/cmn/glob.c @@ -46,596 +46,158 @@ # include "syscall.h" #endif +#define NO_RECURSION 1 + +enum segment_type_t +{ + NONE, + ROOT, + NORMAL +}; +typedef enum segment_type_t segment_type_t; + #if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) /* i don't support escaping in these systems */ -# define IS_ESC(c) (0) +# define IS_ESC_MBS(c) (0) +# define IS_ESC_WCS(c) (0) #else -# define IS_ESC(c) ((c) == QSE_T('\\')) +# define IS_ESC_MBS(c) ((c) == QSE_MT('\\')) +# define IS_ESC_WCS(c) ((c) == QSE_WT('\\')) #endif -#define IS_SEP(c) QSE_ISPATHSEP(c) - -#define IS_NIL(c) ((c) == QSE_T('\0')) -#define IS_SEP_OR_NIL(c) (IS_SEP(c) || IS_NIL(c)) - -/* only for win32/os2/dos */ -#define IS_DRIVE(s) \ - (((s[0] >= QSE_T('A') && s[0] <= QSE_T('Z')) || \ - (s[0] >= QSE_T('a') && s[0] <= QSE_T('z'))) && \ - s[1] == QSE_T(':')) +#define IS_NIL_MBS(c) ((c) == QSE_MT('\0')) +#define IS_NIL_WCS(c) ((c) == QSE_WT('\0')) /* this macro only checks for top-level wild-cards among these. * *, ?, [], !, - * see str-fnmat.c for more wild-card letters */ -#define IS_WILD(c) ((c) == QSE_T('*') || (c) == QSE_T('?') || (c) == QSE_T('[')) +#define IS_WILD_MBS(c) ((c) == QSE_MT('*') || (c) == QSE_MT('?') || (c) == QSE_MT('[')) +#define IS_WILD_WCS(c) ((c) == QSE_WT('*') || (c) == QSE_WT('?') || (c) == QSE_WT('[')) -#define NO_RECURSION 1 -#if defined(NO_RECURSION) -typedef struct stack_node_t stack_node_t; + +/* -------------------------------------------------------------------- */ + +#define glob qse_globmbs +#define cbimpl_t qse_glob_mbscbimpl_t +#define glob_t mbs_glob_t +#define segment_t mbs_segment_t +#define stack_node_t mbs_stack_node_t +#define char_t qse_mchar_t +#define cstr_t qse_mcstr_t +#define T(x) QSE_MT(x) +#define IS_ESC(x) IS_ESC_MBS(x) +#define IS_DRIVE(x) QSE_ISPATHMBDRIVE(x) +#define IS_SEP(x) QSE_ISPATHMBSEP(x) +#define IS_SEP_OR_NIL(x) QSE_ISPATHMBSEPORNIL(x) +#define IS_NIL(x) IS_NIL_MBS(x) +#define IS_WILD(x) IS_WILD_MBS(x) +#define str_t qse_mbs_t +#define str_open qse_mbs_open +#define str_close qse_mbs_close +#define str_init qse_mbs_init +#define str_fini qse_mbs_fini +#define str_cat qse_mbs_cat +#define str_ccat qse_mbs_ccat +#define str_ncat qse_mbs_ncat +#define str_setcapa qse_mbs_setcapa +#define str_setlen qse_mbs_setlen +#define STR_CAPA(x) QSE_MBS_CAPA(x) +#define STR_LEN(x) QSE_MBS_LEN(x) +#define STR_PTR(x) QSE_MBS_PTR(x) +#define STR_XSTR(x) QSE_MBS_XSTR(x) +#define STR_CPTR(x,y) QSE_MBS_CPTR(x,y) +#define strnfnmat qse_mbsnfnmat +#define DIR_CHAR_FLAGS QSE_DIR_MBSPATH +#define path_exists mbs_path_exists +#define search mbs_search +#define get_next_segment mbs_get_next_segment +#define handle_non_wild_segments mbs_handle_non_wild_segments +#define CHAR_IS_MCHAR +#undef DECLARE_MBUF +#include "glob.h" + +/* -------------------------------------------------------------------- */ + +#undef glob +#undef cbimpl_t +#undef glob_t +#undef segment_t +#undef stack_node_t +#undef char_t +#undef cstr_t +#undef T +#undef IS_ESC +#undef IS_DRIVE +#undef IS_SEP +#undef IS_SEP_OR_NIL +#undef IS_NIL +#undef IS_WILD +#undef str_t +#undef str_open +#undef str_close +#undef str_init +#undef str_fini +#undef str_cat +#undef str_ccat +#undef str_ncat +#undef str_setcapa +#undef str_setlen +#undef STR_CAPA +#undef STR_LEN +#undef STR_PTR +#undef STR_XSTR +#undef STR_CPTR +#undef strnfnmat +#undef DIR_CHAR_FLAGS +#undef path_exists +#undef search +#undef get_next_segment +#undef handle_non_wild_segments +#undef CHAR_IS_MCHAR +#undef DECLARE_MBUF + +/* -------------------------------------------------------------------- */ + +#define glob qse_globwcs +#define cbimpl_t qse_glob_wcscbimpl_t +#define glob_t wcs_glob_t +#define segment_t wcs_segment_t +#define stack_node_t wcs_stack_node_t +#define char_t qse_wchar_t +#define cstr_t qse_wcstr_t +#define T(x) QSE_WT(x) +#define IS_ESC(x) IS_ESC_WCS(x) +#define IS_DRIVE(x) QSE_ISPATHWCDRIVE(x) +#define IS_SEP(x) QSE_ISPATHWCSEP(x) +#define IS_SEP_OR_NIL(x) QSE_ISPATHWCSEPORNIL(x) +#define IS_NIL(x) IS_NIL_WCS(x) +#define IS_WILD(x) IS_WILD_WCS(x) +#define str_t qse_wcs_t +#define str_open qse_wcs_open +#define str_close qse_wcs_close +#define str_init qse_wcs_init +#define str_fini qse_wcs_fini +#define str_cat qse_wcs_cat +#define str_ccat qse_wcs_ccat +#define str_ncat qse_wcs_ncat +#define str_setcapa qse_wcs_setcapa +#define str_setlen qse_wcs_setlen +#define STR_CAPA(x) QSE_WCS_CAPA(x) +#define STR_LEN(x) QSE_WCS_LEN(x) +#define STR_PTR(x) QSE_WCS_PTR(x) +#define STR_XSTR(x) QSE_WCS_XSTR(x) +#define STR_CPTR(x,y) QSE_WCS_CPTR(x,y) + +#define strnfnmat qse_wcsnfnmat +#define DIR_CHAR_FLAGS QSE_DIR_WCSPATH +#define path_exists wcs_path_exists +#define search wcs_search +#define get_next_segment wcs_get_next_segment +#define handle_non_wild_segments wcs_handle_non_wild_segments +#undef CHAR_IS_MCHAR +#if !defined(_WIN32) +# define DECLARE_MBUF 1 #endif - -struct glob_t -{ - qse_glob_cbimpl_t cbimpl; - void* cbctx; - - qse_mmgr_t* mmgr; - qse_cmgr_t* cmgr; - int flags; - - qse_str_t path; - qse_str_t tbuf; /* temporary buffer */ -#if defined(QSE_CHAR_IS_MCHAR) || defined(_WIN32) - /* nothing */ -#else - qse_mbs_t mbuf; -#endif - - int expanded; - int fnmat_flags; - -#if defined(NO_RECURSION) - stack_node_t* stack; - stack_node_t* free; -#endif -}; - -typedef struct glob_t glob_t; - -static qse_mchar_t* wcs_to_mbuf (glob_t* g, const qse_wchar_t* wcs, qse_mbs_t* mbs) -{ - qse_size_t ml, wl; - - if (qse_wcstombswithcmgr (wcs, &wl, QSE_NULL, &ml, g->cmgr) <= -1 || - qse_mbs_setlen (mbs, ml) == (qse_size_t)-1) return QSE_NULL; - - qse_wcstombswithcmgr (wcs, &wl, QSE_MBS_PTR(mbs), &ml, g->cmgr); - return QSE_MBS_PTR(mbs); -} - -static int path_exists (glob_t* g, const qse_char_t* name) -{ -#if defined(_WIN32) - - /* ------------------------------------------------------------------- */ - #if !defined(INVALID_FILE_ATTRIBUTES) - #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) - #endif - return (GetFileAttributes(name) != INVALID_FILE_ATTRIBUTES)? 1: 0; - /* ------------------------------------------------------------------- */ - -#elif defined(__OS2__) - - /* ------------------------------------------------------------------- */ - FILESTATUS3 fs; - APIRET rc; - const qse_mchar_t* mptr; - -#if defined(QSE_CHAR_IS_MCHAR) - mptr = name; -#else - mptr = wcs_to_mbuf (g, name, &g->mbuf); - if (mptr == QSE_NULL) return -1; -#endif - - rc = DosQueryPathInfo (mptr, FIL_STANDARD, &fs, QSE_SIZEOF(fs)); - - return (rc == NO_ERROR)? 1: - (rc == ERROR_PATH_NOT_FOUND)? 0: -1; - /* ------------------------------------------------------------------- */ - -#elif defined(__DOS__) - - /* ------------------------------------------------------------------- */ - unsigned int x, attr; - const qse_mchar_t* mptr; - -#if defined(QSE_CHAR_IS_MCHAR) - mptr = name; -#else - mptr = wcs_to_mbuf (g, name, &g->mbuf); - if (mptr == QSE_NULL) return -1; -#endif - - x = _dos_getfileattr (mptr, &attr); - return (x == 0)? 1: - (errno == ENOENT)? 0: -1; - /* ------------------------------------------------------------------- */ - -#elif defined(macintosh) - HFileInfo fpb; - const qse_mchar_t* mptr; - -#if defined(QSE_CHAR_IS_MCHAR) - mptr = name; -#else - mptr = wcs_to_mbuf (g, name, &g->mbuf); - if (mptr == QSE_NULL) return -1; -#endif - - QSE_MEMSET (&fpb, 0, QSE_SIZEOF(fpb)); - fpb.ioNamePtr = (unsigned char*)mptr; - - return (PBGetCatInfoSync ((CInfoPBRec*)&fpb) == noErr)? 1: 0; -#else - - /* ------------------------------------------------------------------- */ -#if defined(HAVE_LSTAT) - qse_lstat_t st; -#else - qse_stat_t st; -#endif - const qse_mchar_t* mptr; - -#if defined(QSE_CHAR_IS_MCHAR) - mptr = name; -#else - mptr = wcs_to_mbuf (g, name, &g->mbuf); - if (mptr == QSE_NULL) return -1; -#endif - -#if defined(HAVE_LSTAT) - return (QSE_LSTAT (mptr, &st) <= -1)? 0: 1; -#else - /* use stat() if no lstat() is available. */ - return (QSE_STAT (mptr, &st) <= -1)? 0: 1; -#endif - - /* ------------------------------------------------------------------- */ - -#endif -} - -struct segment_t -{ - enum - { - NONE, - ROOT, - NORMAL - } type; - - const qse_char_t* ptr; - qse_size_t len; - - qse_char_t sep; /* preceeding separator */ - unsigned int wild: 1; /* indicate that it contains wildcards */ - unsigned int esc: 1; /* indicate that it contains escaped letters */ - unsigned int next: 1; /* indicate that it has the following segment */ -}; - -typedef struct segment_t segment_t; - -static int get_next_segment (glob_t* g, segment_t* seg) -{ - if (seg->type == NONE) - { - /* seg->ptr must point to the beginning of the pattern - * and seg->len must be zero when seg->type is NONE. */ - if (IS_NIL(seg->ptr[0])) - { - /* nothing to do */ - } - else if (IS_SEP(seg->ptr[0])) - { - seg->type = ROOT; - seg->len = 1; - seg->next = IS_NIL(seg->ptr[1])? 0: 1; - seg->sep = QSE_T('\0'); - seg->wild = 0; - seg->esc = 0; - } - #if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) - else if (IS_DRIVE(seg->ptr)) - { - seg->type = ROOT; - seg->len = 2; - if (IS_SEP(seg->ptr[2])) seg->len++; - seg->next = IS_NIL(seg->ptr[seg->len])? 0: 1; - seg->sep = QSE_T('\0'); - seg->wild = 0; - seg->esc = 0; - } - #endif - else - { - int escaped = 0; - seg->type = NORMAL; - seg->sep = QSE_T('\0'); - seg->wild = 0; - seg->esc = 0; - do - { - if (escaped) escaped = 0; - else - { - if (IS_ESC(seg->ptr[seg->len])) - { - escaped = 1; - seg->esc = 1; - } - else if (IS_WILD(seg->ptr[seg->len])) seg->wild = 1; - } - - seg->len++; - } - while (!IS_SEP_OR_NIL(seg->ptr[seg->len])); - seg->next = IS_NIL(seg->ptr[seg->len])? 0: 1; - } - } - else if (seg->type == ROOT) - { - int escaped = 0; - seg->type = NORMAL; - seg->ptr = &seg->ptr[seg->len]; - seg->len = 0; - seg->sep = QSE_T('\0'); - seg->wild = 0; - seg->esc = 0; - - while (!IS_SEP_OR_NIL(seg->ptr[seg->len])) - { - if (escaped) escaped = 0; - else - { - if (IS_ESC(seg->ptr[seg->len])) - { - escaped = 1; - seg->esc = 1; - } - else if (IS_WILD(seg->ptr[seg->len])) seg->wild = 1; - } - seg->len++; - } - seg->next = IS_NIL(seg->ptr[seg->len])? 0: 1; - } - else - { - QSE_ASSERT (seg->type == NORMAL); - - seg->ptr = &seg->ptr[seg->len + 1]; - seg->len = 0; - seg->wild = 0; - seg->esc = 0; - if (IS_NIL(seg->ptr[-1])) - { - seg->type = NONE; - seg->next = 0; - seg->sep = QSE_T('\0'); - } - else - { - int escaped = 0; - seg->sep = seg->ptr[-1]; - while (!IS_SEP_OR_NIL(seg->ptr[seg->len])) - { - if (escaped) escaped = 0; - else - { - if (IS_ESC(seg->ptr[seg->len])) - { - escaped = 1; - seg->esc = 1; - } - else if (IS_WILD(seg->ptr[seg->len])) seg->wild = 1; - } - seg->len++; - } - seg->next = IS_NIL(seg->ptr[seg->len])? 0: 1; - } - } - - return seg->type; -} - -static int handle_non_wild_segments (glob_t* g, segment_t* seg) -{ - while (get_next_segment(g, seg) != NONE && !seg->wild) - { - QSE_ASSERT (seg->type != NONE && !seg->wild); - - if (seg->sep && qse_str_ccat (&g->path, seg->sep) == (qse_size_t)-1) return -1; - if (seg->esc) - { - /* if the segment contains escape sequences, - * strip the escape letters off the segment */ - - qse_cstr_t tmp; - qse_size_t i; - int escaped = 0; - - if (QSE_STR_CAPA(&g->tbuf) < seg->len && - qse_str_setcapa (&g->tbuf, seg->len) == (qse_size_t)-1) return -1; - - tmp.ptr = QSE_STR_PTR(&g->tbuf); - tmp.len = 0; - - /* the following loop drops the last character - * if it is the escape character */ - for (i = 0; i < seg->len; i++) - { - if (escaped) - { - escaped = 0; - tmp.ptr[tmp.len++] = seg->ptr[i]; - } - else - { - if (IS_ESC(seg->ptr[i])) - escaped = 1; - else - tmp.ptr[tmp.len++] = seg->ptr[i]; - } - } - - if (qse_str_ncat (&g->path, tmp.ptr, tmp.len) == (qse_size_t)-1) return -1; - } - else - { - /* if the segmetn doesn't contain escape sequences, - * append the segment to the path without special handling */ - if (qse_str_ncat (&g->path, seg->ptr, seg->len) == (qse_size_t)-1) return -1; - } - - if (!seg->next && path_exists(g, QSE_STR_PTR(&g->path)) > 0) - { - /* reached the last segment. match if the path exists */ - if (g->cbimpl (QSE_STR_XSTR(&g->path), g->cbctx) <= -1) return -1; - g->expanded = 1; - } - } - - return 0; -} - -#if defined(NO_RECURSION) -struct stack_node_t -{ - qse_size_t tmp; - qse_size_t tmp2; - qse_dir_t* dp; - segment_t seg; - - stack_node_t* next; -}; -#endif - -static int search (glob_t* g, segment_t* seg) -{ - qse_dir_t* dp; - qse_size_t tmp, tmp2; - qse_dir_ent_t ent; - int x; - -#if defined(NO_RECURSION) - stack_node_t* r; - -entry: -#endif - - dp = QSE_NULL; - - if (handle_non_wild_segments (g, seg) <= -1) goto oops; - - if (seg->wild) - { - int dir_flags = 0; - if (g->flags & QSE_GLOB_SKIPSPCDIR) dir_flags |= QSE_DIR_SKIPSPCDIR; - - dp = qse_dir_open ( - g->mmgr, 0, QSE_STR_PTR(&g->path), - dir_flags, QSE_NULL); - if (dp) - { - tmp = QSE_STR_LEN(&g->path); - - if (seg->sep && qse_str_ccat (&g->path, seg->sep) == (qse_size_t)-1) goto oops; - tmp2 = QSE_STR_LEN(&g->path); - - while (1) - { - qse_str_setlen (&g->path, tmp2); - - x = qse_dir_read (dp, &ent); - if (x <= -1) - { - if (g->flags & QSE_GLOB_TOLERANT) break; - else goto oops; - } - if (x == 0) break; - - if (qse_str_cat (&g->path, ent.name) == (qse_size_t)-1) goto oops; - - if (qse_strnfnmat (QSE_STR_CPTR(&g->path,tmp2), seg->ptr, seg->len, g->fnmat_flags) > 0) - { - if (seg->next) - { - #if defined(NO_RECURSION) - if (g->free) - { - r = g->free; - g->free = r->next; - } - else - { - r = QSE_MMGR_ALLOC (g->mmgr, QSE_SIZEOF(*r)); - if (r == QSE_NULL) goto oops; - } - - /* push key variables that must be restored - * into the stack. */ - r->tmp = tmp; - r->tmp2 = tmp2; - r->dp = dp; - r->seg = *seg; - - r->next = g->stack; - g->stack = r; - - /* move to the function entry point as if - * a recursive call has been made */ - goto entry; - - resume: - ; - - #else - segment_t save; - int x; - - save = *seg; - x = search (g, seg); - *seg = save; - if (x <= -1) goto oops; - #endif - } - else - { - if (g->cbimpl (QSE_STR_XSTR(&g->path), g->cbctx) <= -1) goto oops; - g->expanded = 1; - } - } - } - - qse_str_setlen (&g->path, tmp); - qse_dir_close (dp); dp = QSE_NULL; - } - } - - QSE_ASSERT (dp == QSE_NULL); - -#if defined(NO_RECURSION) - if (g->stack) - { - /* the stack is not empty. the emulated recusive call - * must have been made. restore the variables pushed - * and jump to the resumption point */ - r = g->stack; - g->stack = r->next; - - tmp = r->tmp; - tmp2 = r->tmp2; - dp = r->dp; - *seg = r->seg; - - /* link the stack node to the free list - * instead of freeing it here */ - r->next = g->free; - g->free = r; - - goto resume; - } - - while (g->free) - { - /* destory the free list */ - r = g->free; - g->free = r->next; - QSE_MMGR_FREE (g->mmgr, r); - } -#endif - - return 0; - -oops: - if (dp) qse_dir_close (dp); - -#if defined(NO_RECURSION) - while (g->stack) - { - r = g->stack; - g->stack = r->next; - qse_dir_close (r->dp); - QSE_MMGR_FREE (g->mmgr, r); - } - - while (g->free) - { - r = g->stack; - g->free = r->next; - QSE_MMGR_FREE (g->mmgr, r); - } -#endif - return -1; -} - -int qse_glob (const qse_char_t* pattern, qse_glob_cbimpl_t cbimpl, void* cbctx, int flags, qse_mmgr_t* mmgr, qse_cmgr_t* cmgr) -{ - segment_t seg; - glob_t g; - int x; - - QSE_MEMSET (&g, 0, QSE_SIZEOF(g)); - g.cbimpl = cbimpl; - g.cbctx = cbctx; - g.mmgr = mmgr; - g.cmgr = cmgr; - g.flags = flags; - -#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) - g.fnmat_flags |= QSE_STRFNMAT_IGNORECASE; - g.fnmat_flags |= QSE_STRFNMAT_NOESCAPE; -#else - if (flags & QSE_GLOB_IGNORECASE) g.fnmat_flags |= QSE_STRFNMAT_IGNORECASE; - if (flags & QSE_GLOB_NOESCAPE) g.fnmat_flags |= QSE_STRFNMAT_NOESCAPE; -#endif - if (flags & QSE_GLOB_PERIOD) g.fnmat_flags |= QSE_STRFNMAT_PERIOD; - - if (qse_str_init (&g.path, mmgr, 512) <= -1) return -1; - if (qse_str_init (&g.tbuf, mmgr, 256) <= -1) - { - qse_str_fini (&g.path); - return -1; - } -#if defined(QSE_CHAR_IS_MCHAR) || defined(_WIN32) - /* nothing */ -#else - if (qse_mbs_init (&g.mbuf, mmgr, 512) <= -1) - { - qse_str_fini (&g.path); - qse_str_fini (&g.path); - return -1; - } -#endif - - QSE_MEMSET (&seg, 0, QSE_SIZEOF(seg)); - seg.type = NONE; - seg.ptr = pattern; - seg.len = 0; - - x = search (&g, &seg); - -#if defined(QSE_CHAR_IS_MCHAR) || defined(_WIN32) - /* nothing */ -#else - qse_mbs_fini (&g.mbuf); -#endif - qse_str_fini (&g.tbuf); - qse_str_fini (&g.path); - - if (x <= -1) return -1; - return g.expanded; -} - +#include "glob.h" diff --git a/qse/lib/cmn/glob.h b/qse/lib/cmn/glob.h new file mode 100644 index 00000000..d9fc626e --- /dev/null +++ b/qse/lib/cmn/glob.h @@ -0,0 +1,590 @@ +/* + * $Id$ + * + Copyright (c) 2006-2014 Chung, Hyung-Hwan. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#if defined(NO_RECURSION) +typedef struct stack_node_t stack_node_t; +#endif + +struct glob_t +{ + cbimpl_t cbimpl; + void* cbctx; + + qse_mmgr_t* mmgr; + qse_cmgr_t* cmgr; + int flags; + + str_t path; + str_t tbuf; /* temporary buffer */ + +#if defined(DECLARE_MBUF) + qse_mbs_t mbuf; +#endif + + int expanded; + int fnmat_flags; + +#if defined(NO_RECURSION) + stack_node_t* stack; + stack_node_t* free; +#endif +}; + +typedef struct glob_t glob_t; + +struct segment_t +{ + segment_type_t type; + + const char_t* ptr; + qse_size_t len; + + char_t sep; /* preceeding separator */ + unsigned int wild: 1; /* indicate that it contains wildcards */ + unsigned int esc: 1; /* indicate that it contains escaped letters */ + unsigned int next: 1; /* indicate that it has the following segment */ +}; + +typedef struct segment_t segment_t; + +#if defined(NO_RECURSION) +struct stack_node_t +{ + qse_size_t tmp; + qse_size_t tmp2; + qse_dir_t* dp; + segment_t seg; + + stack_node_t* next; +}; +#endif + +#if defined(DECLARE_MBUF) +static qse_mchar_t* wcs_to_mbuf (glob_t* g, const qse_wchar_t* wcs, qse_mbs_t* mbs) +{ + qse_size_t ml, wl; + + if (qse_wcstombswithcmgr (wcs, &wl, QSE_NULL, &ml, g->cmgr) <= -1 || + qse_mbs_setlen (mbs, ml) == (qse_size_t)-1) return QSE_NULL; + + qse_wcstombswithcmgr (wcs, &wl, QSE_MBS_PTR(mbs), &ml, g->cmgr); + return QSE_MBS_PTR(mbs); +} +#endif + +static int path_exists (glob_t* g, const char_t* name) +{ +#if defined(_WIN32) + + /* ------------------------------------------------------------------- */ + #if !defined(INVALID_FILE_ATTRIBUTES) + #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) + #endif + #if defined(CHAR_IS_MCHAR) + return (GetFileAttributesA(name) != INVALID_FILE_ATTRIBUTES)? 1: 0; + #else + return (GetFileAttributesW(name) != INVALID_FILE_ATTRIBUTES)? 1: 0; + #endif + /* ------------------------------------------------------------------- */ + +#elif defined(__OS2__) + + /* ------------------------------------------------------------------- */ + FILESTATUS3 fs; + APIRET rc; + const qse_mchar_t* mptr; + +#if defined(CHAR_IS_MCHAR) + mptr = name; +#else + mptr = wcs_to_mbuf (g, name, &g->mbuf); + if (mptr == QSE_NULL) return -1; +#endif + + rc = DosQueryPathInfo (mptr, FIL_STANDARD, &fs, QSE_SIZEOF(fs)); + + return (rc == NO_ERROR)? 1: + (rc == ERROR_PATH_NOT_FOUND)? 0: -1; + /* ------------------------------------------------------------------- */ + +#elif defined(__DOS__) + + /* ------------------------------------------------------------------- */ + unsigned int x, attr; + const qse_mchar_t* mptr; + +#if defined(CHAR_IS_MCHAR) + mptr = name; +#else + mptr = wcs_to_mbuf (g, name, &g->mbuf); + if (mptr == QSE_NULL) return -1; +#endif + + x = _dos_getfileattr (mptr, &attr); + return (x == 0)? 1: + (errno == ENOENT)? 0: -1; + /* ------------------------------------------------------------------- */ + +#elif defined(macintosh) + HFileInfo fpb; + const qse_mchar_t* mptr; + +#if defined(CHAR_IS_MCHAR) + mptr = name; +#else + mptr = wcs_to_mbuf (g, name, &g->mbuf); + if (mptr == QSE_NULL) return -1; +#endif + + QSE_MEMSET (&fpb, 0, QSE_SIZEOF(fpb)); + fpb.ioNamePtr = (unsigned char*)mptr; + + return (PBGetCatInfoSync ((CInfoPBRec*)&fpb) == noErr)? 1: 0; +#else + + /* ------------------------------------------------------------------- */ +#if defined(HAVE_LSTAT) + qse_lstat_t st; +#else + qse_stat_t st; +#endif + const qse_mchar_t* mptr; + +#if defined(CHAR_IS_MCHAR) + mptr = name; +#else + mptr = wcs_to_mbuf (g, name, &g->mbuf); + if (mptr == QSE_NULL) return -1; +#endif + +#if defined(HAVE_LSTAT) + return (QSE_LSTAT (mptr, &st) <= -1)? 0: 1; +#else + /* use stat() if no lstat() is available. */ + return (QSE_STAT (mptr, &st) <= -1)? 0: 1; +#endif + + /* ------------------------------------------------------------------- */ +#endif +} + + +static int get_next_segment (glob_t* g, segment_t* seg) +{ + if (seg->type == NONE) + { + /* seg->ptr must point to the beginning of the pattern + * and seg->len must be zero when seg->type is NONE. */ + if (IS_NIL(seg->ptr[0])) + { + /* nothing to do */ + } + else if (IS_SEP(seg->ptr[0])) + { + seg->type = ROOT; + seg->len = 1; + seg->next = IS_NIL(seg->ptr[1])? 0: 1; + seg->sep = T('\0'); + seg->wild = 0; + seg->esc = 0; + } + #if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + else if (IS_DRIVE(seg->ptr)) + { + seg->type = ROOT; + seg->len = 2; + if (IS_SEP(seg->ptr[2])) seg->len++; + seg->next = IS_NIL(seg->ptr[seg->len])? 0: 1; + seg->sep = T('\0'); + seg->wild = 0; + seg->esc = 0; + } + #endif + else + { + int escaped = 0; + seg->type = NORMAL; + seg->sep = T('\0'); + seg->wild = 0; + seg->esc = 0; + do + { + if (escaped) escaped = 0; + else + { + if (IS_ESC(seg->ptr[seg->len])) + { + escaped = 1; + seg->esc = 1; + } + else if (IS_WILD(seg->ptr[seg->len])) seg->wild = 1; + } + + seg->len++; + } + while (!IS_SEP_OR_NIL(seg->ptr[seg->len])); + seg->next = IS_NIL(seg->ptr[seg->len])? 0: 1; + } + } + else if (seg->type == ROOT) + { + int escaped = 0; + seg->type = NORMAL; + seg->ptr = &seg->ptr[seg->len]; + seg->len = 0; + seg->sep = T('\0'); + seg->wild = 0; + seg->esc = 0; + + while (!IS_SEP_OR_NIL(seg->ptr[seg->len])) + { + if (escaped) escaped = 0; + else + { + if (IS_ESC(seg->ptr[seg->len])) + { + escaped = 1; + seg->esc = 1; + } + else if (IS_WILD(seg->ptr[seg->len])) seg->wild = 1; + } + seg->len++; + } + seg->next = IS_NIL(seg->ptr[seg->len])? 0: 1; + } + else + { + QSE_ASSERT (seg->type == NORMAL); + + seg->ptr = &seg->ptr[seg->len + 1]; + seg->len = 0; + seg->wild = 0; + seg->esc = 0; + if (IS_NIL(seg->ptr[-1])) + { + seg->type = NONE; + seg->next = 0; + seg->sep = T('\0'); + } + else + { + int escaped = 0; + seg->sep = seg->ptr[-1]; + while (!IS_SEP_OR_NIL(seg->ptr[seg->len])) + { + if (escaped) escaped = 0; + else + { + if (IS_ESC(seg->ptr[seg->len])) + { + escaped = 1; + seg->esc = 1; + } + else if (IS_WILD(seg->ptr[seg->len])) seg->wild = 1; + } + seg->len++; + } + seg->next = IS_NIL(seg->ptr[seg->len])? 0: 1; + } + } + + return seg->type; +} + +static int handle_non_wild_segments (glob_t* g, segment_t* seg) +{ + while (get_next_segment(g, seg) != NONE && !seg->wild) + { + QSE_ASSERT (seg->type != NONE && !seg->wild); + + if (seg->sep && str_ccat (&g->path, seg->sep) == (qse_size_t)-1) return -1; + if (seg->esc) + { + /* if the segment contains escape sequences, + * strip the escape letters off the segment */ + + cstr_t tmp; + qse_size_t i; + int escaped = 0; + + if (STR_CAPA(&g->tbuf) < seg->len && + str_setcapa (&g->tbuf, seg->len) == (qse_size_t)-1) return -1; + + tmp.ptr = STR_PTR(&g->tbuf); + tmp.len = 0; + + /* the following loop drops the last character + * if it is the escape character */ + for (i = 0; i < seg->len; i++) + { + if (escaped) + { + escaped = 0; + tmp.ptr[tmp.len++] = seg->ptr[i]; + } + else + { + if (IS_ESC(seg->ptr[i])) + escaped = 1; + else + tmp.ptr[tmp.len++] = seg->ptr[i]; + } + } + + if (str_ncat (&g->path, tmp.ptr, tmp.len) == (qse_size_t)-1) return -1; + } + else + { + /* if the segmetn doesn't contain escape sequences, + * append the segment to the path without special handling */ + if (str_ncat (&g->path, seg->ptr, seg->len) == (qse_size_t)-1) return -1; + } + + if (!seg->next && path_exists (g, STR_PTR(&g->path)) > 0) + { + /* reached the last segment. match if the path exists */ + if (g->cbimpl (STR_XSTR(&g->path), g->cbctx) <= -1) return -1; + g->expanded = 1; + } + } + + return 0; +} + +static int search (glob_t* g, segment_t* seg) +{ + qse_dir_t* dp; + qse_size_t tmp, tmp2; + qse_dir_ent_t ent; + int x; + +#if defined(NO_RECURSION) + stack_node_t* r; + +entry: +#endif + + dp = QSE_NULL; + + if (handle_non_wild_segments (g, seg) <= -1) goto oops; + + if (seg->wild) + { + int dir_flags = DIR_CHAR_FLAGS; + if (g->flags & QSE_GLOB_SKIPSPCDIR) dir_flags |= QSE_DIR_SKIPSPCDIR; + + dp = qse_dir_open ( + g->mmgr, 0, (const qse_char_t*)STR_PTR(&g->path), + dir_flags, QSE_NULL); + if (dp) + { + tmp = STR_LEN(&g->path); + + if (seg->sep && str_ccat (&g->path, seg->sep) == (qse_size_t)-1) goto oops; + tmp2 = STR_LEN(&g->path); + + while (1) + { + str_setlen (&g->path, tmp2); + + x = qse_dir_read (dp, &ent); + if (x <= -1) + { + if (g->flags & QSE_GLOB_TOLERANT) break; + else goto oops; + } + if (x == 0) break; + + if (str_cat (&g->path, (const char_t*)ent.name) == (qse_size_t)-1) goto oops; + + if (strnfnmat (STR_CPTR(&g->path,tmp2), seg->ptr, seg->len, g->fnmat_flags) > 0) + { + if (seg->next) + { + #if defined(NO_RECURSION) + if (g->free) + { + r = g->free; + g->free = r->next; + } + else + { + r = QSE_MMGR_ALLOC (g->mmgr, QSE_SIZEOF(*r)); + if (r == QSE_NULL) goto oops; + } + + /* push key variables that must be restored + * into the stack. */ + r->tmp = tmp; + r->tmp2 = tmp2; + r->dp = dp; + r->seg = *seg; + + r->next = g->stack; + g->stack = r; + + /* move to the function entry point as if + * a recursive call has been made */ + goto entry; + + resume: + ; + + #else + segment_t save; + int x; + + save = *seg; + x = search (g, seg); + *seg = save; + if (x <= -1) goto oops; + #endif + } + else + { + if (g->cbimpl (STR_XSTR(&g->path), g->cbctx) <= -1) goto oops; + g->expanded = 1; + } + } + } + + str_setlen (&g->path, tmp); + qse_dir_close (dp); dp = QSE_NULL; + } + } + + QSE_ASSERT (dp == QSE_NULL); + +#if defined(NO_RECURSION) + if (g->stack) + { + /* the stack is not empty. the emulated recusive call + * must have been made. restore the variables pushed + * and jump to the resumption point */ + r = g->stack; + g->stack = r->next; + + tmp = r->tmp; + tmp2 = r->tmp2; + dp = r->dp; + *seg = r->seg; + + /* link the stack node to the free list + * instead of freeing it here */ + r->next = g->free; + g->free = r; + + goto resume; + } + + while (g->free) + { + /* destory the free list */ + r = g->free; + g->free = r->next; + QSE_MMGR_FREE (g->mmgr, r); + } +#endif + + return 0; + +oops: + if (dp) qse_dir_close (dp); + +#if defined(NO_RECURSION) + while (g->stack) + { + r = g->stack; + g->stack = r->next; + qse_dir_close (r->dp); + QSE_MMGR_FREE (g->mmgr, r); + } + + while (g->free) + { + r = g->stack; + g->free = r->next; + QSE_MMGR_FREE (g->mmgr, r); + } +#endif + return -1; +} + +int glob (const char_t* pattern, cbimpl_t cbimpl, void* cbctx, int flags, qse_mmgr_t* mmgr, qse_cmgr_t* cmgr) +{ + segment_t seg; + glob_t g; + int x; + + QSE_MEMSET (&g, 0, QSE_SIZEOF(g)); + g.cbimpl = cbimpl; + g.cbctx = cbctx; + g.mmgr = mmgr; + g.cmgr = cmgr; + g.flags = flags; + +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + g.fnmat_flags |= QSE_STRFNMAT_IGNORECASE; + g.fnmat_flags |= QSE_STRFNMAT_NOESCAPE; +#else + if (flags & QSE_GLOB_IGNORECASE) g.fnmat_flags |= QSE_STRFNMAT_IGNORECASE; + if (flags & QSE_GLOB_NOESCAPE) g.fnmat_flags |= QSE_STRFNMAT_NOESCAPE; +#endif + if (flags & QSE_GLOB_PERIOD) g.fnmat_flags |= QSE_STRFNMAT_PERIOD; + + if (str_init (&g.path, mmgr, 512) <= -1) return -1; + if (str_init (&g.tbuf, mmgr, 256) <= -1) + { + str_fini (&g.path); + return -1; + } + +#if defined(DECLARE_MBUF) + if (qse_mbs_init (&g.mbuf, mmgr, 512) <= -1) + { + str_fini (&g.path); + str_fini (&g.path); + return -1; + } +#endif + + QSE_MEMSET (&seg, 0, QSE_SIZEOF(seg)); + seg.type = NONE; + seg.ptr = pattern; + seg.len = 0; + + x = search (&g, &seg); + +#if defined(DECLARE_MBUF) + qse_mbs_fini (&g.mbuf); +#endif + str_fini (&g.tbuf); + str_fini (&g.path); + + if (x <= -1) return -1; + return g.expanded; +} diff --git a/qse/lib/cmn/pio.c b/qse/lib/cmn/pio.c index 9ffe7e86..402a4662 100644 --- a/qse/lib/cmn/pio.c +++ b/qse/lib/cmn/pio.c @@ -163,7 +163,7 @@ static int make_param ( goto oops; } } -#else +#else if (flags & QSE_PIO_MBSCMD) { /* the cmd is flagged to be of qse_mchar_t