1066 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1066 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|     Copyright (c) 2006-2020 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.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * Do NOT edit glob.c. Edit glob.c.m4 instead.
 | |
|  *
 | |
|  * Generate gem-glob.c with m4
 | |
|  *   $ m4 gem-glob.c.m4 > gem-glob.c
 | |
|  */
 | |
| 
 | |
| #include "hawk-prv.h"
 | |
| #include <hawk-chr.h>
 | |
| #include <hawk-ecs.h>
 | |
| #include <hawk-dir.h>
 | |
| #include <hawk-glob.h>
 | |
| 
 | |
| #include "syscall.h"
 | |
| 
 | |
| #define NO_RECURSION 1
 | |
| 
 | |
| enum segment_type_t
 | |
| {
 | |
|         NONE,
 | |
|         ROOT,
 | |
|         NORMAL
 | |
| };
 | |
| typedef enum segment_type_t segment_type_t;
 | |
| 
 | |
| 
 | |
| #define IS_ESC(c) HAWK_FNMAT_IS_ESC(c)
 | |
| #define IS_SEP(c) HAWK_FNMAT_IS_SEP(c)
 | |
| #define IS_NIL(c) ((c) == '\0')
 | |
| #define IS_SEP_OR_NIL(c) (IS_SEP(c) || IS_NIL(c))
 | |
| 
 | |
| /* 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) == '*' || (c) == '?' || (c) == '[')
 | |
| 
 | |
| 
 | |
| static hawk_bch_t* wcs_to_mbuf (hawk_gem_t* g, const hawk_uch_t* wcs, hawk_becs_t* mbuf)
 | |
| {
 | |
| 	hawk_oow_t ml, wl;
 | |
| 
 | |
| 	if (hawk_gem_convutobcstr(g, wcs, &wl, HAWK_NULL, &ml) <= -1 ||
 | |
| 	    hawk_becs_setlen(mbuf, ml) == (hawk_oow_t)-1) return HAWK_NULL;
 | |
| 
 | |
| 	hawk_gem_convutobcstr (g, wcs, &wl, HAWK_BECS_PTR(mbuf), &ml);
 | |
| 	return HAWK_BECS_PTR(mbuf);
 | |
| }
 | |
| 
 | |
| #if defined(_WIN32) && !defined(INVALID_FILE_ATTRIBUTES)
 | |
| #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
 | |
| #endif
 | |
| 
 | |
| static int upath_exists (hawk_gem_t* g, const hawk_uch_t* name, hawk_becs_t* mbuf)
 | |
| {
 | |
| #if defined(_WIN32)
 | |
| 	return (GetFileAttributesW(name) != INVALID_FILE_ATTRIBUTES)? 1: 0;
 | |
| 
 | |
| #elif defined(__OS2__)
 | |
| 	FILESTATUS3 fs;
 | |
| 	APIRET rc;
 | |
| 	const hawk_bch_t* mptr;
 | |
| 
 | |
| 	mptr = wcs_to_mbuf(g, name, mbuf);
 | |
| 	if (HAWK_UNLIKELY(!mptr)) return -1;
 | |
| 
 | |
| 	rc = DosQueryPathInfo(mptr, FIL_STANDARD, &fs, HAWK_SIZEOF(fs));
 | |
| 	return (rc == NO_ERROR)? 1: ((rc == ERROR_PATH_NOT_FOUND)? 0: -1);
 | |
| 
 | |
| #elif defined(__DOS__)
 | |
| 	unsigned int x, attr;
 | |
| 	const hawk_bch_t* mptr;
 | |
| 
 | |
| 	mptr = wcs_to_mbuf(g, name, mbuf);
 | |
| 	if (HAWK_UNLIKELY(!mptr)) return -1;
 | |
| 
 | |
| 	x = _dos_getfileattr (mptr, &attr);
 | |
| 	return (x == 0)? 1: ((errno == ENOENT)? 0: -1);
 | |
| 
 | |
| #elif defined(macintosh)
 | |
| 	HFileInfo fpb;
 | |
| 	const hawk_bch_t* mptr;
 | |
| 
 | |
| 	mptr = wcs_to_mbuf(g, name, mbuf);
 | |
| 	if (HAWK_UNLIKELY(!mptr)) return -1;
 | |
| 
 | |
| 	HAWK_MEMSET (&fpb, 0, HAWK_SIZEOF(fpb));
 | |
| 	fpb.ioNamePtr = (unsigned char*)mptr;
 | |
| 
 | |
| 	return (PBGetCatInfoSync ((CInfoPBRec*)&fpb) == noErr)? 1: 0;
 | |
| #else
 | |
| 	struct stat st;
 | |
| 	const hawk_bch_t* mptr;
 | |
| 	int t;
 | |
| 
 | |
| 	mptr = wcs_to_mbuf(g, name, mbuf);
 | |
| 	if (HAWK_UNLIKELY(!mptr)) return -1;
 | |
| 
 | |
| 	#if defined(HAVE_LSTAT)
 | |
| 	t = HAWK_LSTAT(mptr, &st);
 | |
| 	#else
 | |
| 	t = HAWK_STAT(mptr, &st);/* use stat() if no lstat() is available. */
 | |
| 	#endif
 | |
| 	return (t == 0);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static int bpath_exists (hawk_gem_t* g, const hawk_bch_t* name, hawk_becs_t* mbuf)
 | |
| {
 | |
| #if defined(_WIN32)
 | |
| 	return (GetFileAttributesA(name) != INVALID_FILE_ATTRIBUTES)? 1: 0;
 | |
| #elif defined(__OS2__)
 | |
| 
 | |
| 	FILESTATUS3 fs;
 | |
| 	APIRET rc;
 | |
| 	rc = DosQueryPathInfo(name, FIL_STANDARD, &fs, HAWK_SIZEOF(fs));
 | |
| 	return (rc == NO_ERROR)? 1: ((rc == ERROR_PATH_NOT_FOUND)? 0: -1);
 | |
| 
 | |
| #elif defined(__DOS__)
 | |
| 	unsigned int x, attr;
 | |
| 	x = _dos_getfileattr(name, &attr);
 | |
| 	return (x == 0)? 1: ((errno == ENOENT)? 0: -1);
 | |
| 
 | |
| #elif defined(macintosh)
 | |
| 	HFileInfo fpb;
 | |
| 	HAWK_MEMSET (&fpb, 0, HAWK_SIZEOF(fpb));
 | |
| 	fpb.ioNamePtr = (unsigned char*)name;
 | |
| 	return (PBGetCatInfoSync((CInfoPBRec*)&fpb) == noErr)? 1: 0;
 | |
| #else
 | |
| 	struct stat st;
 | |
| 	int t;
 | |
| 	#if defined(HAVE_LSTAT)
 | |
| 	t = HAWK_LSTAT(name, &st);
 | |
| 	#else
 | |
| 	t = HAWK_STAT(name, &st); /* use stat() if no lstat() is available. */
 | |
| 	#endif
 | |
| 	return (t == 0);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #if defined(NO_RECURSION)
 | |
| typedef struct __u_stack_node_t __u_stack_node_t;
 | |
| #endif
 | |
| 
 | |
| struct __u_glob_t
 | |
| {
 | |
| 	hawk_gem_uglob_cb_t cbimpl;
 | |
| 	void* cbctx;
 | |
| 
 | |
| 	hawk_gem_t* gem;
 | |
| 	int flags;
 | |
| 
 | |
| 	hawk_uecs_t path;
 | |
| 	hawk_uecs_t tbuf; /* temporary buffer */
 | |
| 
 | |
| 	hawk_becs_t mbuf; /* not used if the base character type is hawk_bch_t */
 | |
| 
 | |
| 	int expanded;
 | |
| 	int fnmat_flags;
 | |
| 
 | |
| #if defined(NO_RECURSION)
 | |
| 	__u_stack_node_t* stack;
 | |
| 	__u_stack_node_t* free;
 | |
| #endif
 | |
| };
 | |
| 
 | |
| typedef struct __u_glob_t __u_glob_t;
 | |
| 
 | |
| struct __u_segment_t
 | |
| {
 | |
| 	segment_type_t type;
 | |
| 
 | |
| 	const hawk_uch_t* ptr;
 | |
| 	hawk_oow_t    len;
 | |
| 
 | |
| 	hawk_uch_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 __u_segment_t __u_segment_t;
 | |
| 
 | |
| #if defined(NO_RECURSION)
 | |
| struct __u_stack_node_t
 | |
| {
 | |
| 	hawk_oow_t tmp;
 | |
| 	hawk_oow_t tmp2;
 | |
| 	hawk_dir_t* dp;
 | |
| 	__u_segment_t seg;
 | |
| 	__u_stack_node_t* next;
 | |
| };
 | |
| #endif
 | |
| 
 | |
| static int __u_get_next_segment (__u_glob_t* g, __u_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 = '\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 = '\0';
 | |
| 			seg->wild = 0;
 | |
| 			seg->esc = 0;
 | |
| 		}
 | |
| 	#endif
 | |
| 		else
 | |
| 		{
 | |
| 			int escaped = 0;
 | |
| 			seg->type = NORMAL;
 | |
| 			seg->sep = '\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 = '\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
 | |
| 	{
 | |
| 		HAWK_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 = '\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 __u_handle_non_wild_segments (__u_glob_t* g, __u_segment_t* seg)
 | |
| {
 | |
| 	while (__u_get_next_segment(g, seg) != NONE && !seg->wild)
 | |
| 	{
 | |
| 		HAWK_ASSERT (seg->type != NONE && !seg->wild);
 | |
| 
 | |
| 		if (seg->sep && hawk_uecs_ccat (&g->path, seg->sep) == (hawk_oow_t)-1) return -1;
 | |
| 		if (seg->esc)
 | |
| 		{
 | |
| 			/* if the segment contains escape sequences,
 | |
| 			 * strip the escape letters off the segment */
 | |
| 
 | |
| 
 | |
| 			hawk_ucs_t tmp;
 | |
| 			hawk_oow_t i;
 | |
| 			int escaped = 0;
 | |
| 
 | |
| 			if (HAWK_UECS_CAPA(&g->tbuf) < seg->len &&
 | |
| 			    hawk_uecs_setcapa (&g->tbuf, seg->len) == (hawk_oow_t)-1) return -1;
 | |
| 
 | |
| 			tmp.ptr = HAWK_UECS_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 (hawk_uecs_ncat (&g->path, tmp.ptr, tmp.len) == (hawk_oow_t)-1) return -1;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			/* if the segment doesn't contain escape sequences,
 | |
| 			 * append the segment to the path without special handling */
 | |
| 			if (hawk_uecs_ncat (&g->path, seg->ptr, seg->len) == (hawk_oow_t)-1) return -1;
 | |
| 		}
 | |
| 
 | |
| 		if (!seg->next && upath_exists(g->gem, HAWK_UECS_PTR(&g->path), &g->mbuf) > 0)
 | |
| 		{
 | |
| 			/* reached the last segment. match if the path exists */
 | |
| 			if (g->cbimpl(HAWK_UECS_CS(&g->path), g->cbctx) <= -1) return -1;
 | |
| 			g->expanded = 1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int __u_search (__u_glob_t* g, __u_segment_t* seg)
 | |
| {
 | |
| 	hawk_dir_t* dp;
 | |
| 	hawk_oow_t tmp, tmp2;
 | |
| 	hawk_dir_ent_t ent;
 | |
| 	int x;
 | |
| 
 | |
| #if defined(NO_RECURSION)
 | |
| 	__u_stack_node_t* r;
 | |
| 
 | |
| entry:
 | |
| #endif
 | |
| 
 | |
| 	dp = HAWK_NULL;
 | |
| 
 | |
| 	if (__u_handle_non_wild_segments(g, seg) <= -1) goto oops;
 | |
| 
 | |
| 	if (seg->wild)
 | |
| 	{
 | |
| 		int dir_flags = 0;
 | |
| 		if (g->flags & HAWK_GLOB_SKIPSPCDIR) dir_flags |= HAWK_DIR_SKIPSPCDIR;
 | |
| 		if (HAWK_SIZEOF(hawk_uch_t) == HAWK_SIZEOF(hawk_bch_t)) dir_flags |= HAWK_DIR_BPATH;
 | |
| 
 | |
| 		dp = hawk_dir_open(g->gem, 0, (const hawk_ooch_t*)HAWK_UECS_PTR(&g->path), dir_flags);
 | |
| 		if (dp)
 | |
| 		{
 | |
| 			tmp = HAWK_UECS_LEN(&g->path);
 | |
| 
 | |
| 			if (seg->sep && hawk_uecs_ccat(&g->path, seg->sep) == (hawk_oow_t)-1) goto oops;
 | |
| 			tmp2 = HAWK_UECS_LEN(&g->path);
 | |
| 
 | |
| 			while (1)
 | |
| 			{
 | |
| 				hawk_uecs_setlen (&g->path, tmp2);
 | |
| 
 | |
| 				x = hawk_dir_read(dp, &ent);
 | |
| 				if (x <= -1)
 | |
| 				{
 | |
| 					if (g->flags & HAWK_GLOB_TOLERANT) break;
 | |
| 					else goto oops;
 | |
| 				}
 | |
| 				if (x == 0) break;
 | |
| 
 | |
| 				if (hawk_uecs_cat(&g->path, (const hawk_uch_t*)ent.name) == (hawk_oow_t)-1) goto oops;
 | |
| 
 | |
| 				if (hawk_fnmat_ucstr_uchars(HAWK_UECS_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 = hawk_gem_allocmem(g->gem, HAWK_SIZEOF(*r));
 | |
| 							if (r == HAWK_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
 | |
| 						__u_segment_t save;
 | |
| 						int x;
 | |
| 
 | |
| 						save = *seg;
 | |
| 						x = __u_search(g, seg);
 | |
| 						*seg = save;
 | |
| 						if (x <= -1) goto oops;
 | |
| 				#endif
 | |
| 					}
 | |
| 					else
 | |
| 					{
 | |
| 						if (g->cbimpl(HAWK_UECS_CS(&g->path), g->cbctx) <= -1) goto oops;
 | |
| 						g->expanded = 1;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			hawk_uecs_setlen (&g->path, tmp);
 | |
| 			hawk_dir_close (dp); dp = HAWK_NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	HAWK_ASSERT (dp == HAWK_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;
 | |
| 		hawk_gem_freemem (g->gem, r);
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| oops:
 | |
| 	if (dp) hawk_dir_close (dp);
 | |
| 
 | |
| #if defined(NO_RECURSION)
 | |
| 	while (g->stack)
 | |
| 	{
 | |
| 		r = g->stack;
 | |
| 		g->stack = r->next;
 | |
| 		hawk_dir_close (r->dp);
 | |
| 		hawk_gem_freemem (g->gem, r);
 | |
| 	}
 | |
| 
 | |
| 	while (g->free)
 | |
| 	{
 | |
| 		r = g->stack;
 | |
| 		g->free = r->next;
 | |
| 		hawk_gem_freemem (g->gem, r);
 | |
| 	}
 | |
| #endif
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| int hawk_gem_uglob (hawk_gem_t* gem, const hawk_uch_t* pattern, hawk_gem_uglob_cb_t cbimpl, void* cbctx, int flags)
 | |
| {
 | |
| 	__u_segment_t seg;
 | |
| 	__u_glob_t g;
 | |
| 	int x;
 | |
| 
 | |
| 	HAWK_MEMSET (&g, 0, HAWK_SIZEOF(g));
 | |
| 	g.gem = gem;
 | |
| 	g.cbimpl = cbimpl;
 | |
| 	g.cbctx = cbctx;
 | |
| 	g.flags = flags;
 | |
| 
 | |
| #if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
 | |
| 	g.fnmat_flags |= HAWK_FNMAT_IGNORECASE;
 | |
| 	g.fnmat_flags |= HAWK_FNMAT_NOESCAPE;
 | |
| #else
 | |
| 	if (flags & HAWK_GLOB_IGNORECASE) g.fnmat_flags |= HAWK_FNMAT_IGNORECASE;
 | |
| 	if (flags & HAWK_GLOB_NOESCAPE) g.fnmat_flags |= HAWK_FNMAT_NOESCAPE;
 | |
| #endif
 | |
| 	if (flags & HAWK_GLOB_PERIOD) g.fnmat_flags |= HAWK_FNMAT_PERIOD;
 | |
| 
 | |
| 	if (hawk_uecs_init(&g.path, g.gem, 512) <= -1) return -1;
 | |
| 	if (hawk_uecs_init(&g.tbuf, g.gem, 256) <= -1)
 | |
| 	{
 | |
| 		hawk_uecs_fini (&g.path);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (HAWK_SIZEOF(hawk_uch_t) != HAWK_SIZEOF(hawk_bch_t))
 | |
| 	{
 | |
| 		if (hawk_becs_init(&g.mbuf, g.gem, 512) <= -1)
 | |
| 		{
 | |
| 			hawk_uecs_fini (&g.path);
 | |
| 			hawk_uecs_fini (&g.path);
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	HAWK_MEMSET (&seg, 0, HAWK_SIZEOF(seg));
 | |
| 	seg.type = NONE;
 | |
| 	seg.ptr = pattern;
 | |
| 	seg.len = 0;
 | |
| 
 | |
| 	x = __u_search(&g, &seg);
 | |
| 
 | |
| 	if (HAWK_SIZEOF(hawk_uch_t) != HAWK_SIZEOF(hawk_uch_t)) hawk_becs_fini (&g.mbuf);
 | |
| 	hawk_uecs_fini (&g.tbuf);
 | |
| 	hawk_uecs_fini (&g.path);
 | |
| 
 | |
| 	if (x <= -1) return -1;
 | |
| 	return g.expanded;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| #if defined(NO_RECURSION)
 | |
| typedef struct __b_stack_node_t __b_stack_node_t;
 | |
| #endif
 | |
| 
 | |
| struct __b_glob_t
 | |
| {
 | |
| 	hawk_gem_bglob_cb_t cbimpl;
 | |
| 	void* cbctx;
 | |
| 
 | |
| 	hawk_gem_t* gem;
 | |
| 	int flags;
 | |
| 
 | |
| 	hawk_becs_t path;
 | |
| 	hawk_becs_t tbuf; /* temporary buffer */
 | |
| 
 | |
| 	hawk_becs_t mbuf; /* not used if the base character type is hawk_bch_t */
 | |
| 
 | |
| 	int expanded;
 | |
| 	int fnmat_flags;
 | |
| 
 | |
| #if defined(NO_RECURSION)
 | |
| 	__b_stack_node_t* stack;
 | |
| 	__b_stack_node_t* free;
 | |
| #endif
 | |
| };
 | |
| 
 | |
| typedef struct __b_glob_t __b_glob_t;
 | |
| 
 | |
| struct __b_segment_t
 | |
| {
 | |
| 	segment_type_t type;
 | |
| 
 | |
| 	const hawk_bch_t* ptr;
 | |
| 	hawk_oow_t    len;
 | |
| 
 | |
| 	hawk_bch_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 __b_segment_t __b_segment_t;
 | |
| 
 | |
| #if defined(NO_RECURSION)
 | |
| struct __b_stack_node_t
 | |
| {
 | |
| 	hawk_oow_t tmp;
 | |
| 	hawk_oow_t tmp2;
 | |
| 	hawk_dir_t* dp;
 | |
| 	__b_segment_t seg;
 | |
| 	__b_stack_node_t* next;
 | |
| };
 | |
| #endif
 | |
| 
 | |
| static int __b_get_next_segment (__b_glob_t* g, __b_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 = '\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 = '\0';
 | |
| 			seg->wild = 0;
 | |
| 			seg->esc = 0;
 | |
| 		}
 | |
| 	#endif
 | |
| 		else
 | |
| 		{
 | |
| 			int escaped = 0;
 | |
| 			seg->type = NORMAL;
 | |
| 			seg->sep = '\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 = '\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
 | |
| 	{
 | |
| 		HAWK_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 = '\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 __b_handle_non_wild_segments (__b_glob_t* g, __b_segment_t* seg)
 | |
| {
 | |
| 	while (__b_get_next_segment(g, seg) != NONE && !seg->wild)
 | |
| 	{
 | |
| 		HAWK_ASSERT (seg->type != NONE && !seg->wild);
 | |
| 
 | |
| 		if (seg->sep && hawk_becs_ccat (&g->path, seg->sep) == (hawk_oow_t)-1) return -1;
 | |
| 		if (seg->esc)
 | |
| 		{
 | |
| 			/* if the segment contains escape sequences,
 | |
| 			 * strip the escape letters off the segment */
 | |
| 
 | |
| 
 | |
| 			hawk_bcs_t tmp;
 | |
| 			hawk_oow_t i;
 | |
| 			int escaped = 0;
 | |
| 
 | |
| 			if (HAWK_BECS_CAPA(&g->tbuf) < seg->len &&
 | |
| 			    hawk_becs_setcapa (&g->tbuf, seg->len) == (hawk_oow_t)-1) return -1;
 | |
| 
 | |
| 			tmp.ptr = HAWK_BECS_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 (hawk_becs_ncat (&g->path, tmp.ptr, tmp.len) == (hawk_oow_t)-1) return -1;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			/* if the segment doesn't contain escape sequences,
 | |
| 			 * append the segment to the path without special handling */
 | |
| 			if (hawk_becs_ncat (&g->path, seg->ptr, seg->len) == (hawk_oow_t)-1) return -1;
 | |
| 		}
 | |
| 
 | |
| 		if (!seg->next && bpath_exists(g->gem, HAWK_BECS_PTR(&g->path), &g->mbuf) > 0)
 | |
| 		{
 | |
| 			/* reached the last segment. match if the path exists */
 | |
| 			if (g->cbimpl(HAWK_BECS_CS(&g->path), g->cbctx) <= -1) return -1;
 | |
| 			g->expanded = 1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int __b_search (__b_glob_t* g, __b_segment_t* seg)
 | |
| {
 | |
| 	hawk_dir_t* dp;
 | |
| 	hawk_oow_t tmp, tmp2;
 | |
| 	hawk_dir_ent_t ent;
 | |
| 	int x;
 | |
| 
 | |
| #if defined(NO_RECURSION)
 | |
| 	__b_stack_node_t* r;
 | |
| 
 | |
| entry:
 | |
| #endif
 | |
| 
 | |
| 	dp = HAWK_NULL;
 | |
| 
 | |
| 	if (__b_handle_non_wild_segments(g, seg) <= -1) goto oops;
 | |
| 
 | |
| 	if (seg->wild)
 | |
| 	{
 | |
| 		int dir_flags = 0;
 | |
| 		if (g->flags & HAWK_GLOB_SKIPSPCDIR) dir_flags |= HAWK_DIR_SKIPSPCDIR;
 | |
| 		dir_flags |= HAWK_DIR_BPATH;
 | |
| 
 | |
| 		dp = hawk_dir_open(g->gem, 0, (const hawk_ooch_t*)HAWK_BECS_PTR(&g->path), dir_flags);
 | |
| 		if (dp)
 | |
| 		{
 | |
| 			tmp = HAWK_BECS_LEN(&g->path);
 | |
| 
 | |
| 			if (seg->sep && hawk_becs_ccat(&g->path, seg->sep) == (hawk_oow_t)-1) goto oops;
 | |
| 			tmp2 = HAWK_BECS_LEN(&g->path);
 | |
| 
 | |
| 			while (1)
 | |
| 			{
 | |
| 				hawk_becs_setlen (&g->path, tmp2);
 | |
| 
 | |
| 				x = hawk_dir_read(dp, &ent);
 | |
| 				if (x <= -1)
 | |
| 				{
 | |
| 					if (g->flags & HAWK_GLOB_TOLERANT) break;
 | |
| 					else goto oops;
 | |
| 				}
 | |
| 				if (x == 0) break;
 | |
| 
 | |
| 				if (hawk_becs_cat(&g->path, (const hawk_bch_t*)ent.name) == (hawk_oow_t)-1) goto oops;
 | |
| 
 | |
| 				if (hawk_fnmat_bcstr_bchars(HAWK_BECS_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 = hawk_gem_allocmem(g->gem, HAWK_SIZEOF(*r));
 | |
| 							if (r == HAWK_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
 | |
| 						__b_segment_t save;
 | |
| 						int x;
 | |
| 
 | |
| 						save = *seg;
 | |
| 						x = __b_search(g, seg);
 | |
| 						*seg = save;
 | |
| 						if (x <= -1) goto oops;
 | |
| 				#endif
 | |
| 					}
 | |
| 					else
 | |
| 					{
 | |
| 						if (g->cbimpl(HAWK_BECS_CS(&g->path), g->cbctx) <= -1) goto oops;
 | |
| 						g->expanded = 1;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			hawk_becs_setlen (&g->path, tmp);
 | |
| 			hawk_dir_close (dp); dp = HAWK_NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	HAWK_ASSERT (dp == HAWK_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;
 | |
| 		hawk_gem_freemem (g->gem, r);
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| oops:
 | |
| 	if (dp) hawk_dir_close (dp);
 | |
| 
 | |
| #if defined(NO_RECURSION)
 | |
| 	while (g->stack)
 | |
| 	{
 | |
| 		r = g->stack;
 | |
| 		g->stack = r->next;
 | |
| 		hawk_dir_close (r->dp);
 | |
| 		hawk_gem_freemem (g->gem, r);
 | |
| 	}
 | |
| 
 | |
| 	while (g->free)
 | |
| 	{
 | |
| 		r = g->stack;
 | |
| 		g->free = r->next;
 | |
| 		hawk_gem_freemem (g->gem, r);
 | |
| 	}
 | |
| #endif
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| int hawk_gem_bglob (hawk_gem_t* gem, const hawk_bch_t* pattern, hawk_gem_bglob_cb_t cbimpl, void* cbctx, int flags)
 | |
| {
 | |
| 	__b_segment_t seg;
 | |
| 	__b_glob_t g;
 | |
| 	int x;
 | |
| 
 | |
| 	HAWK_MEMSET (&g, 0, HAWK_SIZEOF(g));
 | |
| 	g.gem = gem;
 | |
| 	g.cbimpl = cbimpl;
 | |
| 	g.cbctx = cbctx;
 | |
| 	g.flags = flags;
 | |
| 
 | |
| #if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
 | |
| 	g.fnmat_flags |= HAWK_FNMAT_IGNORECASE;
 | |
| 	g.fnmat_flags |= HAWK_FNMAT_NOESCAPE;
 | |
| #else
 | |
| 	if (flags & HAWK_GLOB_IGNORECASE) g.fnmat_flags |= HAWK_FNMAT_IGNORECASE;
 | |
| 	if (flags & HAWK_GLOB_NOESCAPE) g.fnmat_flags |= HAWK_FNMAT_NOESCAPE;
 | |
| #endif
 | |
| 	if (flags & HAWK_GLOB_PERIOD) g.fnmat_flags |= HAWK_FNMAT_PERIOD;
 | |
| 
 | |
| 	if (hawk_becs_init(&g.path, g.gem, 512) <= -1) return -1;
 | |
| 	if (hawk_becs_init(&g.tbuf, g.gem, 256) <= -1)
 | |
| 	{
 | |
| 		hawk_becs_fini (&g.path);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (HAWK_SIZEOF(hawk_bch_t) != HAWK_SIZEOF(hawk_bch_t))
 | |
| 	{
 | |
| 		if (hawk_becs_init(&g.mbuf, g.gem, 512) <= -1)
 | |
| 		{
 | |
| 			hawk_becs_fini (&g.path);
 | |
| 			hawk_becs_fini (&g.path);
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	HAWK_MEMSET (&seg, 0, HAWK_SIZEOF(seg));
 | |
| 	seg.type = NONE;
 | |
| 	seg.ptr = pattern;
 | |
| 	seg.len = 0;
 | |
| 
 | |
| 	x = __b_search(&g, &seg);
 | |
| 
 | |
| 	if (HAWK_SIZEOF(hawk_bch_t) != HAWK_SIZEOF(hawk_uch_t)) hawk_becs_fini (&g.mbuf);
 | |
| 	hawk_becs_fini (&g.tbuf);
 | |
| 	hawk_becs_fini (&g.path);
 | |
| 
 | |
| 	if (x <= -1) return -1;
 | |
| 	return g.expanded;
 | |
| }
 | |
| 
 | |
| 
 |