added qse_globmbs() and qse_globwcs()

This commit is contained in:
hyung-hwan 2014-12-14 15:55:03 +00:00
parent 77939a5940
commit ac61c3044a
8 changed files with 794 additions and 609 deletions

View File

@ -35,12 +35,17 @@
* in a path name.
*/
typedef int (*qse_glob_cbimpl_t) (
const qse_cstr_t* path,
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,
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

View File

@ -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'))

View File

@ -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

View File

@ -13,6 +13,7 @@ noinst_HEADERS = \
fmt-intmax.h \
fmt-out.h \
fs.h \
glob.h \
mem.h \
str-dyn.h \
str-fcpy.h \

View File

@ -397,6 +397,7 @@ noinst_HEADERS = \
fmt-intmax.h \
fmt-out.h \
fs.h \
glob.h \
mem.h \
str-dyn.h \
str-fcpy.h \

View File

@ -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"

590
qse/lib/cmn/glob.h Normal file
View File

@ -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;
}