2622 lines
		
	
	
		
			64 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2622 lines
		
	
	
		
			64 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.
 | |
|  */
 | |
| 
 | |
| #include "hawk-prv.h"
 | |
| 
 | |
| static int fnc_asort (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi);
 | |
| static int fnc_asorti (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi);
 | |
| static int fnc_close (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi);
 | |
| static int fnc_fflush (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi);
 | |
| static int fnc_int (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi);
 | |
| static int fnc_length (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi);
 | |
| static int fnc_match (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi);
 | |
| 
 | |
| #define A_MAX HAWK_TYPE_MAX(hawk_oow_t)
 | |
| 
 | |
| /* Argument Specifier
 | |
|  *
 | |
|  * Each character in the specifier indicates how a parameter
 | |
|  * of the corresponding postion should be passed to a function.
 | |
|  *
 | |
|  * - v: value. pass it after normal evaluation.
 | |
|  * - r: pass a variable by reference
 | |
|  * - x: regular expression as it is. not evaluated as /rex/ ~ $0.
 | |
|  *
 | |
|  * NOTE: If min is greater than max, the specifier indicate the
 | |
|  * name of the module  where the function is located.
 | |
|  */
 | |
| static hawk_fnc_t sysfnctab[] =
 | |
| {
 | |
| 	/* io functions */
 | |
| 	{ {HAWK_T("close"),    5}, 0, { {1,     2, HAWK_NULL},        fnc_close,  HAWK_RIO }, HAWK_NULL},
 | |
| 	{ {HAWK_T("fflush"),   6}, 0, { {0,     1, HAWK_NULL},        fnc_fflush,  HAWK_RIO }, HAWK_NULL},
 | |
| 
 | |
| 	/* type info/conversion */
 | |
| 	{ {HAWK_T("int"),      3}, 0, { {1,     1, HAWK_NULL},        fnc_int,              0 }, HAWK_NULL},
 | |
| 
 | |
| 	/* map(array) sort */
 | |
| 	{ {HAWK_T("asort"),    5}, 0, { {1,     3, HAWK_T("rrv")},    fnc_asort,            0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("asorti"),   6}, 0, { {1,     3, HAWK_T("rrv")},    fnc_asorti,           0 }, HAWK_NULL},
 | |
| 
 | |
| 	/* string functions */
 | |
| 	{ {HAWK_T("gensub"),   6}, 0, { {3,     4, HAWK_T("xvvv")},   hawk_fnc_gensub,   0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("gsub"),     4}, 0, { {2,     3, HAWK_T("xvr")},    hawk_fnc_gsub,     0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("index"),    5}, 0, { {2,     3, HAWK_NULL},        hawk_fnc_index,    0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("length"),   6}, 1, { {0,     1, HAWK_NULL},        fnc_length,        0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("match"),    5}, 0, { {2,     3, HAWK_T("vxr")},    fnc_match,         0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("split"),    5}, 0, { {2,     3, HAWK_T("vrx")},    hawk_fnc_split,    0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("sprintf"),  7}, 0, { {1, A_MAX, HAWK_NULL},        hawk_fnc_sprintf,  0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("sub"),      3}, 0, { {2,     3, HAWK_T("xvr")},    hawk_fnc_sub,      0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("substr"),   6}, 0, { {2,     3, HAWK_NULL},        hawk_fnc_substr,   0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("tolower"),  7}, 0, { {1,     1, HAWK_NULL},        hawk_fnc_tolower,  0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("toupper"),  7}, 0, { {1,     1, HAWK_NULL},        hawk_fnc_toupper,  0 }, HAWK_NULL},
 | |
| 
 | |
| 	/* math functions */
 | |
| 	{ {HAWK_T("sin"),      3}, 0, { {A_MAX, 0, HAWK_T("math") },  HAWK_NULL,             0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("cos"),      3}, 0, { {A_MAX, 0, HAWK_T("math") },  HAWK_NULL,             0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("tan"),      3}, 0, { {A_MAX, 0, HAWK_T("math") },  HAWK_NULL,             0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("atan"),     4}, 0, { {A_MAX, 0, HAWK_T("math") },  HAWK_NULL,             0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("atan2"),    5}, 0, { {A_MAX, 0, HAWK_T("math") },  HAWK_NULL,             0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("log"),      3}, 0, { {A_MAX, 0, HAWK_T("math") },  HAWK_NULL,             0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("log10"),    5}, 0, { {A_MAX, 0, HAWK_T("math") },  HAWK_NULL,             0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("exp"),      3}, 0, { {A_MAX, 0, HAWK_T("math") },  HAWK_NULL,             0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("sqrt"),     4}, 0, { {A_MAX, 0, HAWK_T("math") },  HAWK_NULL,             0 }, HAWK_NULL},
 | |
| 
 | |
| 	/* time functions */
 | |
| 	{ {HAWK_T("mktime"),   6}, 0, { {A_MAX, 0, HAWK_T("sys") },   HAWK_NULL,             0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("strftime"), 8}, 0, { {A_MAX, 0, HAWK_T("sys") },   HAWK_NULL,             0 }, HAWK_NULL},
 | |
| 	{ {HAWK_T("systime"),  7}, 0, { {A_MAX, 0, HAWK_T("sys") },   HAWK_NULL,             0 }, HAWK_NULL}
 | |
| };
 | |
| 
 | |
| static hawk_fnc_t* add_fnc (hawk_t* hawk, const hawk_ooch_t* name, const hawk_fnc_spec_t* spec)
 | |
| {
 | |
| 	hawk_fnc_t* fnc;
 | |
| 	hawk_oow_t fnc_size;
 | |
| 	hawk_oow_t speclen;
 | |
| 	hawk_oocs_t ncs;
 | |
| 
 | |
| 	ncs.ptr = (hawk_ooch_t*)name;
 | |
| 	ncs.len = hawk_count_oocstr(name);
 | |
| 	if (ncs.len <= 0)
 | |
| 	{
 | |
| 		hawk_seterrnum(hawk, HAWK_NULL, HAWK_EINVAL);
 | |
| 		return HAWK_NULL;
 | |
| 	}
 | |
| 
 | |
| 	/* Note it doesn't check if it conflicts with a keyword.
 | |
| 	 * such a function registered won't take effect because
 | |
| 	 * the word is treated as a keyword */
 | |
| 
 | |
| 	if (hawk_findfncwithoocs(hawk, &ncs) != HAWK_NULL)
 | |
| 	{
 | |
| 		hawk_seterrfmt(hawk, HAWK_NULL, HAWK_EEXIST, HAWK_T("unable to add existing function - %js"), name);
 | |
| 		return HAWK_NULL;
 | |
| 	}
 | |
| 
 | |
| 	speclen = spec->arg.spec? hawk_count_oocstr(spec->arg.spec): 0;
 | |
| 
 | |
| 	fnc_size = HAWK_SIZEOF(*fnc) + (ncs.len + 1 + speclen + 1) * HAWK_SIZEOF(hawk_ooch_t);
 | |
| 	fnc = (hawk_fnc_t*)hawk_callocmem(hawk, fnc_size);
 | |
| 	if (fnc)
 | |
| 	{
 | |
| 		hawk_ooch_t* tmp;
 | |
| 
 | |
| 		tmp = (hawk_ooch_t*)(fnc + 1);
 | |
| 		fnc->name.len = hawk_copy_oocstr_unlimited(tmp, ncs.ptr);
 | |
| 		fnc->name.ptr = tmp;
 | |
| 
 | |
| 		fnc->spec = *spec;
 | |
| 		if (spec->arg.spec)
 | |
| 		{
 | |
| 			tmp = fnc->name.ptr + fnc->name.len + 1;
 | |
| 			hawk_copy_oocstr_unlimited (tmp, spec->arg.spec);
 | |
| 			fnc->spec.arg.spec = tmp;
 | |
| 		}
 | |
| 
 | |
| 		if (!hawk_htb_insert(hawk->fnc.user, (hawk_ooch_t*)ncs.ptr, ncs.len, fnc, 0))
 | |
| 		{
 | |
| 			const hawk_ooch_t* bem = hawk_backuperrmsg(hawk);
 | |
| 			hawk_seterrfmt(hawk, HAWK_NULL, hawk_geterrnum(hawk), HAWK_T("unable to add function - %js - %js"), name, bem);
 | |
| 			hawk_freemem(hawk, fnc);
 | |
| 			fnc = HAWK_NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return fnc;
 | |
| }
 | |
| 
 | |
| hawk_fnc_t* hawk_addfncwithbcstr (hawk_t* hawk, const hawk_bch_t* name, const hawk_fnc_mspec_t* spec)
 | |
| {
 | |
| #if defined(HAWK_OOCH_IS_BCH)
 | |
| 	return add_fnc(hawk, name, spec);
 | |
| #else
 | |
| 	hawk_ucs_t wcs;
 | |
| 	hawk_fnc_t* fnc;
 | |
| 	hawk_fnc_spec_t wspec;
 | |
| 
 | |
| 	HAWK_STATIC_ASSERT (HAWK_SIZEOF(*spec) == HAWK_SIZEOF(wspec));
 | |
| 
 | |
| 	HAWK_MEMCPY (&wspec, spec, HAWK_SIZEOF(wspec));
 | |
| 	if (spec->arg.spec)
 | |
| 	{
 | |
| 		wcs.ptr = hawk_dupbtoucstr(hawk, spec->arg.spec, &wcs.len, 0);
 | |
| 		if (HAWK_UNLIKELY(!wcs.ptr)) return HAWK_NULL;
 | |
| 		wspec.arg.spec = wcs.ptr;
 | |
| 	}
 | |
| 
 | |
| 	wcs.ptr = hawk_dupbtoucstr(hawk, name, &wcs.len, 0);
 | |
| 	if (HAWK_UNLIKELY(!wcs.ptr))
 | |
| 	{
 | |
| 		if (wspec.arg.spec) hawk_freemem(hawk, (hawk_uch_t*)wspec.arg.spec);
 | |
| 		return HAWK_NULL;
 | |
| 	}
 | |
| 
 | |
| 	fnc = add_fnc(hawk, wcs.ptr, &wspec);
 | |
| 	hawk_freemem(hawk, wcs.ptr);
 | |
| 	if (wspec.arg.spec) hawk_freemem(hawk, (hawk_uch_t*)wspec.arg.spec);
 | |
| 	return fnc;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| hawk_fnc_t* hawk_addfncwithucstr (hawk_t* hawk, const hawk_uch_t* name, const hawk_fnc_wspec_t* spec)
 | |
| {
 | |
| #if defined(HAWK_OOCH_IS_BCH)
 | |
| 	hawk_bcs_t mbs;
 | |
| 	hawk_fnc_t* fnc;
 | |
| 	hawk_fnc_spec_t mspec;
 | |
| 
 | |
| 	HAWK_STATIC_ASSERT (HAWK_SIZEOF(*spec) == HAWK_SIZEOF(mspec));
 | |
| 
 | |
| 	HAWK_MEMCPY (&mspec, spec, HAWK_SIZEOF(mspec));
 | |
| 	if (spec->arg.spec)
 | |
| 	{
 | |
| 		mbs.ptr = hawk_duputobcstr(hawk, spec->arg.spec, &mbs.len);
 | |
| 		if (HAWK_UNLIKELY(!mbs.ptr)) return HAWK_NULL;
 | |
| 		mspec.arg.spec = mbs.ptr;
 | |
| 	}
 | |
| 
 | |
| 	mbs.ptr = hawk_duputobcstr(hawk, name, &mbs.len);
 | |
| 	if (HAWK_UNLIKELY(!mbs.ptr))
 | |
| 	{
 | |
| 		if (mspec.arg.spec) hawk_freemem(hawk, (hawk_bch_t*)mspec.arg.spec);
 | |
| 		return HAWK_NULL;
 | |
| 	}
 | |
| 
 | |
| 	fnc = add_fnc(hawk, mbs.ptr, &mspec);
 | |
| 	hawk_freemem(hawk, mbs.ptr);
 | |
| 	if (mspec.arg.spec) hawk_freemem(hawk, (hawk_bch_t*)mspec.arg.spec);
 | |
| 	return fnc;
 | |
| #else
 | |
| 	return add_fnc(hawk, name, spec);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| int hawk_delfncwithbcstr (hawk_t* hawk, const hawk_bch_t* name)
 | |
| {
 | |
| 	hawk_bcs_t ncs;
 | |
| 	hawk_ucs_t wcs;
 | |
| 
 | |
| 	ncs.ptr = (hawk_bch_t*)name;
 | |
| 	ncs.len = hawk_count_bcstr(name);
 | |
| 
 | |
| #if defined(HAWK_OOCH_IS_BCH)
 | |
| 	if (hawk_htb_delete(hawk->fnc.user, ncs.ptr, ncs.len) <= -1)
 | |
| 	{
 | |
| 		hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, HAWK_T("no such function - %hs"), name);
 | |
| 		return -1;
 | |
| 	}
 | |
| #else
 | |
| 	wcs.ptr = hawk_dupbtoucstr(hawk, ncs.ptr, &wcs.len, 0);
 | |
| 	if (!wcs.ptr) return -1;
 | |
| 	if (hawk_htb_delete(hawk->fnc.user, wcs.ptr, wcs.len) <= -1)
 | |
| 	{
 | |
| 		hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, HAWK_T("no such function - %hs"), name);
 | |
| 		hawk_freemem(hawk, wcs.ptr);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	hawk_freemem(hawk, wcs.ptr);
 | |
| #endif
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int hawk_delfncwithucstr (hawk_t* hawk, const hawk_uch_t* name)
 | |
| {
 | |
| 	hawk_ucs_t ncs;
 | |
| 	hawk_bcs_t mbs;
 | |
| 
 | |
| 	ncs.ptr = (hawk_uch_t*)name;
 | |
| 	ncs.len = hawk_count_ucstr(name);
 | |
| 
 | |
| #if defined(HAWK_OOCH_IS_BCH)
 | |
| 	mbs.ptr = hawk_duputobcstr(hawk, ncs.ptr, &mbs.len);
 | |
| 	if (!mbs.ptr) return -1;
 | |
| 	if (hawk_htb_delete(hawk->fnc.user, mbs.ptr, mbs.len) <= -1)
 | |
| 	{
 | |
| 		hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, HAWK_T("no such function - %ls"), name);
 | |
| 		hawk_freemem(hawk, mbs.ptr);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	hawk_freemem(hawk, mbs.ptr);
 | |
| #else
 | |
| 	if (hawk_htb_delete(hawk->fnc.user, ncs.ptr, ncs.len) <= -1)
 | |
| 	{
 | |
| 		hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, HAWK_T("no such function - %ls"), name);
 | |
| 		return -1;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void hawk_clrfnc (hawk_t* hawk)
 | |
| {
 | |
| 	hawk_htb_clear (hawk->fnc.user);
 | |
| }
 | |
| 
 | |
| static hawk_fnc_t* find_fnc (hawk_t* hawk, const hawk_oocs_t* name)
 | |
| {
 | |
| 	hawk_htb_pair_t* pair;
 | |
| 	int i;
 | |
| 
 | |
| 	/* search the system function table
 | |
| 	 * though some optimization like binary search can
 | |
| 	 * speed up the search, i don't do that since this
 | |
| 	 * function is called durting parse-time only.
 | |
| 	 */
 | |
| 	for (i = 0; i < HAWK_COUNTOF(sysfnctab); i++)
 | |
| 	{
 | |
| 		if ((hawk->opt.trait & sysfnctab[i].spec.trait) != sysfnctab[i].spec.trait) continue;
 | |
| 		if (hawk_comp_oochars(sysfnctab[i].name.ptr, sysfnctab[i].name.len, name->ptr, name->len, 0) == 0) return &sysfnctab[i];
 | |
| 	}
 | |
| 
 | |
| 	pair = hawk_htb_search(hawk->fnc.user, name->ptr, name->len);
 | |
| 	if (pair)
 | |
| 	{
 | |
| 		hawk_fnc_t* fnc;
 | |
| 		fnc = (hawk_fnc_t*)HAWK_HTB_VPTR(pair);
 | |
| 		if ((hawk->opt.trait & fnc->spec.trait) == fnc->spec.trait) return fnc;
 | |
| 	}
 | |
| 
 | |
| 	hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, HAWK_T("no such function - %js"), name);
 | |
| 	return HAWK_NULL;
 | |
| }
 | |
| 
 | |
| hawk_fnc_t* hawk_findfncwithbcs (hawk_t* hawk, const hawk_bcs_t* name)
 | |
| {
 | |
| #if defined(HAWK_OOCH_IS_BCH)
 | |
| 	return find_fnc(hawk, name);
 | |
| #else
 | |
| 	hawk_ucs_t wcs;
 | |
| 	hawk_fnc_t* fnc;
 | |
| 
 | |
| 	wcs.ptr = hawk_dupbtouchars(hawk, name->ptr, name->len, &wcs.len, 0);
 | |
| 	if (HAWK_UNLIKELY(!wcs.ptr)) return HAWK_NULL;
 | |
| 	fnc = find_fnc(hawk, &wcs);
 | |
| 	hawk_freemem(hawk, wcs.ptr);
 | |
| 	return fnc;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| hawk_fnc_t* hawk_findfncwithucs (hawk_t* hawk, const hawk_ucs_t* name)
 | |
| {
 | |
| #if defined(HAWK_OOCH_IS_BCH)
 | |
| 	hawk_bcs_t mbs;
 | |
| 	hawk_fnc_t* fnc;
 | |
| 
 | |
| 	mbs.ptr = hawk_duputobchars(hawk, name->ptr, name->len, &mbs.len);
 | |
| 	if (HAWK_UNLIKELY(!mbs.ptr)) return HAWK_NULL;
 | |
| 	fnc = find_fnc(hawk, &mbs);
 | |
| 	hawk_freemem(hawk, mbs.ptr);
 | |
| 	return fnc;
 | |
| #else
 | |
| 	return find_fnc(hawk, name);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static int fnc_close (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	hawk_oow_t nargs;
 | |
| 	hawk_val_t* v, * a0, * a1 = HAWK_NULL;
 | |
| 	int n;
 | |
| 
 | |
| 	hawk_ooch_t* name, * opt = HAWK_NULL;
 | |
| 	hawk_oow_t len, optlen = 0;
 | |
| 
 | |
| 	nargs = hawk_rtx_getnargs(rtx);
 | |
| 	HAWK_ASSERT (nargs == 1 || nargs == 2);
 | |
| 
 | |
| 	a0 = hawk_rtx_getarg(rtx, 0);
 | |
| 	if (nargs >= 2) a1 = hawk_rtx_getarg(rtx, 1);
 | |
| 	HAWK_ASSERT (a0 != HAWK_NULL);
 | |
| 
 | |
| 	name = hawk_rtx_getvaloocstr(rtx, a0, &len);
 | |
| 	if (HAWK_UNLIKELY(!name)) return -1;
 | |
| 
 | |
| 	if (a1)
 | |
| 	{
 | |
| 		opt = hawk_rtx_getvaloocstr(rtx, a1, &optlen);
 | |
| 		if (HAWK_UNLIKELY(!opt))
 | |
| 		{
 | |
| 			hawk_rtx_freevaloocstr(rtx, a0, name);
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (len == 0)
 | |
| 	{
 | |
| 		/* getline or print doesn't allow an empty string for the
 | |
| 		 * input or output file name. so close should not allow
 | |
| 		 * it either.
 | |
| 		 * another reason for this is if close is called explicitly
 | |
| 		 * with an empty string, it may close the console that uses
 | |
| 		 * an empty string for its identification because closeio
 | |
| 		 * closes any ios that match the name given unlike
 | |
| 		 * closeio_read or closeio_write. */
 | |
| 		n = -1;
 | |
| 		goto skip_close;
 | |
| 	}
 | |
| 
 | |
| 	while (len > 0)
 | |
| 	{
 | |
| 		if (name[--len] == HAWK_T('\0'))
 | |
| 		{
 | |
| 			/* the name contains a null charater.
 | |
| 			 * make close return -1 */
 | |
| 			n = -1;
 | |
| 			goto skip_close;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (opt)
 | |
| 	{
 | |
| 		/* r and w are the input and the output from the child process perspective
 | |
| 		 * to and from are the intput and the output in terms of main process */
 | |
| 		if (hawk_comp_oochars_oocstr(opt, optlen, HAWK_T("to"), 0) == 0)
 | |
| 		{
 | |
| 			optlen = 1;
 | |
| 			opt[0] = 'r'; /* close the input to the pipe */
 | |
| 		}
 | |
| 		else if (hawk_comp_oochars_oocstr(opt, optlen, HAWK_T("from"), 0) == 0)
 | |
| 		{
 | |
| 			optlen = 1;
 | |
| 			opt[0] = 'w'; /* close the output from the pipe */
 | |
| 		}
 | |
| 
 | |
| 		if (optlen != 1 || (opt[0] != HAWK_T('r') && opt[0] != HAWK_T('w')))
 | |
| 		{
 | |
| 			n = -1;
 | |
| 			goto skip_close;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	n = hawk_rtx_closeio(rtx, name, opt);
 | |
| 	/* failure to close is not a critical error. instead, that is
 | |
| 	 * flagged by the return value of close().
 | |
| 	if (n <= -1 && rtx->errinf.num != HAWK_EIONMNF)
 | |
| 	{
 | |
| 		if (a0->type != HAWK_VAL_STR) hawk_rtx_freemem(rtx, name);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	*/
 | |
| 
 | |
| skip_close:
 | |
| 	if (a1) hawk_rtx_freevaloocstr(rtx, a1, opt);
 | |
| 	hawk_rtx_freevaloocstr(rtx, a0, name);
 | |
| 
 | |
| 	v = hawk_rtx_makeintval(rtx, (hawk_int_t)n);
 | |
| 	if (!v) return -1;
 | |
| 
 | |
| 	hawk_rtx_setretval(rtx, v);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int flush_io (hawk_rtx_t* rtx, hawk_out_type_t out_type, const hawk_ooch_t* name, int n)
 | |
| {
 | |
| 	int n2;
 | |
| 	hawk_rio_type_t rio;
 | |
| 
 | |
| 	rio = hawk_rtx_outtoriotype(rtx, out_type);
 | |
| 	if (rtx->rio.handler[rio])
 | |
| 	{
 | |
| 		n2 = hawk_rtx_flushio(rtx, out_type, name);
 | |
| 		if (n2 <= -1)
 | |
| 		{
 | |
| 			/*
 | |
| 			if (rtx->errinf.num == HAWK_EIOIMPL) n = -1;
 | |
| 			else if (rtx->errinf.num == HAWK_EIONMNF)
 | |
| 			{
 | |
| 				if (n != 0) n = -2;
 | |
| 			}
 | |
| 			else n = -99;
 | |
| 			*/
 | |
| 			if (hawk_rtx_geterrnum(rtx) == HAWK_EIONMNF)
 | |
| 			{
 | |
| 				if (n != 0) n = -2;
 | |
| 			}
 | |
| 			else n = -1;
 | |
| 		}
 | |
| 		else if (n != -1) n = 0;
 | |
| 	}
 | |
| 
 | |
| 	return n;
 | |
| }
 | |
| 
 | |
| static int fnc_fflush (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	hawk_oow_t nargs;
 | |
| 	hawk_val_t* a0, * v;
 | |
| 	hawk_ooch_t* str0;
 | |
| 	hawk_oow_t len0;
 | |
| 	int n;
 | |
| 
 | |
| 	nargs = hawk_rtx_getnargs (rtx);
 | |
| 	HAWK_ASSERT (nargs == 0 || nargs == 1);
 | |
| 
 | |
| 	if (nargs == 0)
 | |
| 	{
 | |
| 		/* fflush() flushes the console output.
 | |
| 		 * fflush() should return -1 on errors.
 | |
| 		 *
 | |
| 		 * if no previous console output statement is seen,
 | |
| 		 * this function won't be able to find the entry.
 | |
| 		 * so it returns -1;
 | |
| 		 *
 | |
| 		 * BEGIN { flush(); } # flush() returns -1
 | |
| 		 * BEGIN { print 1; flush(); } # flush() returns 0
 | |
| 		 */
 | |
| 		n = hawk_rtx_flushio(rtx, HAWK_OUT_CONSOLE, HAWK_T(""));
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		hawk_ooch_t* ptr, * end;
 | |
| 
 | |
| 		a0 = hawk_rtx_getarg(rtx, 0);
 | |
| 		str0 = hawk_rtx_getvaloocstr(rtx, a0, &len0);
 | |
| 		if (!str0) return -1;
 | |
| 
 | |
| 		/* the target name contains a null character.
 | |
| 		 * make fflush return -1 */
 | |
| 		ptr = str0; end = str0 + len0;
 | |
| 		while (ptr < end)
 | |
| 		{
 | |
| 			if (*ptr == HAWK_T('\0'))
 | |
| 			{
 | |
| 				n = -1;
 | |
| 				goto skip_flush;
 | |
| 			}
 | |
| 
 | |
| 			ptr++;
 | |
| 		}
 | |
| 
 | |
| 		/* flush the given rio.
 | |
| 		 *
 | |
| 		 * flush("") flushes all output streams regardless of names.
 | |
| 		 * pass HAWK_NULL for the name in that case so that the
 | |
| 		 * callee matches any streams.
 | |
| 		 *
 | |
| 		 * fflush() doesn't specify the type of output streams
 | |
| 		 * so it attemps to flush all types of output streams.
 | |
| 		 *
 | |
| 		 * though not useful, it's possible to have multiple
 | |
| 		 * streams with the same name but of different types.
 | |
| 		 *
 | |
| 		 *  BEGIN {
 | |
| 		 *    print 1 | "/tmp/x";
 | |
| 		 *    print 1 > "/tmp/x";
 | |
| 		 *    fflush("/tmp/x");
 | |
| 		 *  }
 | |
| 		 */
 | |
| 
 | |
| 		n = flush_io(rtx, HAWK_OUT_FILE, ((len0 == 0)? HAWK_NULL: str0), 1);
 | |
| 		/*if (n == -99) return -1;*/
 | |
| 		n = flush_io(rtx, HAWK_OUT_APFILE, ((len0 == 0)? HAWK_NULL: str0), n);
 | |
| 		/*if (n == -99) return -1;*/
 | |
| 		n = flush_io(rtx, HAWK_OUT_PIPE, ((len0 == 0)? HAWK_NULL: str0), n);
 | |
| 		/*if (n == -99) return -1;*/
 | |
| 		n = flush_io(rtx, HAWK_OUT_RWPIPE, ((len0 == 0)? HAWK_NULL: str0), n);
 | |
| 		/*if (n == -99) return -1;*/
 | |
| 
 | |
| 		/* if n remains 1, no io handlers have been defined for
 | |
| 		 * file, pipe, and rwpipe. so make fflush return -1.
 | |
| 		 * if n is -2, no such named io has been found at all
 | |
| 		 * if n is -1, the io handler has returned an error */
 | |
| 		if (n != 0) n = -1;
 | |
| 
 | |
| 	skip_flush:
 | |
| 		hawk_rtx_freevaloocstr(rtx, a0, str0);
 | |
| 	}
 | |
| 
 | |
| 	v = hawk_rtx_makeintval(rtx, (hawk_int_t)n);
 | |
| 	if (!v) return -1;
 | |
| 
 | |
| 	hawk_rtx_setretval(rtx, v);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int index_or_rindex (hawk_rtx_t* rtx, int rindex)
 | |
| {
 | |
| 	/* this is similar to the built-in index() function but doesn't
 | |
| 	 * care about IGNORECASE. */
 | |
| 	hawk_oow_t nargs;
 | |
| 	hawk_val_t* a0, * a1;
 | |
| 	hawk_int_t idx, boundary = 1;
 | |
| 	hawk_val_type_t vtype;
 | |
| 
 | |
| 	nargs = hawk_rtx_getnargs(rtx);
 | |
| 	a0 = hawk_rtx_getarg(rtx, 0);
 | |
| 	a1 = hawk_rtx_getarg(rtx, 1);
 | |
| 
 | |
| 	/*
 | |
| 	index ("abc", "d", 3);
 | |
| 	rindex ("abcdefabcdx", "cd", 8);
 | |
| 	*/
 | |
| 
 | |
| 	if (nargs >= 3)
 | |
| 	{
 | |
| 		hawk_val_t* a2;
 | |
| 		int n;
 | |
| 
 | |
| 		a2 = hawk_rtx_getarg(rtx, 2);
 | |
| 		n = hawk_rtx_valtoint(rtx, a2, &boundary);
 | |
| 		if (n <= -1) return -1;
 | |
| 	}
 | |
| 
 | |
| 	vtype = HAWK_RTX_GETVALTYPE(rtx, a0);
 | |
| 	switch (vtype)
 | |
| 	{
 | |
| 		case HAWK_VAL_BCHR:
 | |
| 		case HAWK_VAL_MBS:
 | |
| 		case HAWK_VAL_BOB:
 | |
| 		{
 | |
| 			hawk_bch_t* str0, * str1, * ptr, bchr;
 | |
| 			hawk_oow_t len0, len1;
 | |
| 
 | |
| 			if (vtype == HAWK_VAL_BCHR)
 | |
| 			{
 | |
| 				bchr = HAWK_RTX_GETBCHRFROMVAL(rtx, a0);
 | |
| 				str0 = &bchr;
 | |
| 				len0 = 1;
 | |
| 			}
 | |
| 			/* this part isn't needed because hawk_val_mbs_t and hawk_val_mbs_t
 | |
| 			 * are almost the same except the type of the val field
 | |
| 			else if (vtype == HAWK_VAL_BOB)
 | |
| 			{
 | |
| 				str0 = (hawk_bch_t*)((hawk_val_bob_t*)a0)->val.ptr;
 | |
| 				len0 = ((hawk_val_bob_t*)a0)->val.len;
 | |
| 			}*/
 | |
| 			else
 | |
| 			{
 | |
| 				str0 = ((hawk_val_mbs_t*)a0)->val.ptr;
 | |
| 				len0 = ((hawk_val_mbs_t*)a0)->val.len;
 | |
| 			}
 | |
| 
 | |
| 			str1 = hawk_rtx_getvalbcstr(rtx, a1, &len1);
 | |
| 			if (HAWK_UNLIKELY(!str1)) return -1;
 | |
| 
 | |
| 			if (nargs < 3)
 | |
| 			{
 | |
| 				boundary = rindex? len0: 1;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				if (boundary == 0) boundary = 1;
 | |
| 				else if (boundary < 0) boundary = len0 + boundary + 1;
 | |
| 			}
 | |
| 
 | |
| 			if (boundary > len0 || boundary <= 0)
 | |
| 			{
 | |
| 				ptr = HAWK_NULL;
 | |
| 			}
 | |
| 			else if (rindex)
 | |
| 			{
 | |
| 				/* 'boundary' acts as an end position */
 | |
| 				ptr = hawk_rfind_bchars_in_bchars(&str0[0], boundary, str1, len1, rtx->gbl.ignorecase);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				/* 'boundary' acts as an start position */
 | |
| 				ptr = hawk_find_bchars_in_bchars(&str0[boundary-1], len0 - boundary + 1, str1, len1, rtx->gbl.ignorecase);
 | |
| 			}
 | |
| 
 | |
| 			idx = (ptr? ((hawk_int_t)(ptr - str0) + 1): 0);
 | |
| 
 | |
| 			hawk_rtx_freevalbcstr(rtx, a1, str1);
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		default:
 | |
| 		{
 | |
| 			hawk_ooch_t* str0, * str1, * ptr;
 | |
| 			hawk_oow_t len0, len1;
 | |
| 
 | |
| 			str0 = hawk_rtx_getvaloocstr(rtx, a0, &len0);
 | |
| 			if (HAWK_UNLIKELY(!str0)) return -1;
 | |
| 
 | |
| 			str1 = hawk_rtx_getvaloocstr(rtx, a1, &len1);
 | |
| 			if (!str1)
 | |
| 			{
 | |
| 				hawk_rtx_freevaloocstr(rtx, a0, str0);
 | |
| 				return -1;
 | |
| 			}
 | |
| 
 | |
| 			if (nargs < 3)
 | |
| 			{
 | |
| 				boundary = rindex? len0: 1;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				if (boundary == 0) boundary = 1;
 | |
| 				else if (boundary < 0) boundary = len0 + boundary + 1;
 | |
| 			}
 | |
| 
 | |
| 			if (boundary > len0 || boundary <= 0)
 | |
| 			{
 | |
| 				ptr = HAWK_NULL;
 | |
| 			}
 | |
| 			else if (rindex)
 | |
| 			{
 | |
| 				/* 'boundary' acts as an end position */
 | |
| 				ptr = hawk_rfind_oochars_in_oochars(&str0[0], boundary, str1, len1, rtx->gbl.ignorecase);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				/* 'boundary' acts as an start position */
 | |
| 				ptr = hawk_find_oochars_in_oochars(&str0[boundary-1], len0 - boundary + 1, str1, len1, rtx->gbl.ignorecase);
 | |
| 			}
 | |
| 
 | |
| 			idx = (ptr? ((hawk_int_t)(ptr - str0) + 1): 0);
 | |
| 
 | |
| 			hawk_rtx_freevaloocstr(rtx, a1, str1);
 | |
| 			hawk_rtx_freevaloocstr(rtx, a0, str0);
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	a0 = hawk_rtx_makeintval(rtx, idx);
 | |
| 	if (HAWK_UNLIKELY(!a0)) return -1;
 | |
| 
 | |
| 	hawk_rtx_setretval(rtx, a0);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int hawk_fnc_index (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	return index_or_rindex(rtx, 0);
 | |
| }
 | |
| 
 | |
| int hawk_fnc_rindex (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	return index_or_rindex(rtx, 1);
 | |
| }
 | |
| 
 | |
| int hawk_fnc_length (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi, int mode)
 | |
| {
 | |
| 	hawk_oow_t nargs;
 | |
| 	hawk_val_t* v;
 | |
| 	hawk_val_type_t vtype;
 | |
| 	hawk_ooch_t* str;
 | |
| 	hawk_oow_t len;
 | |
| 
 | |
| 	nargs = hawk_rtx_getnargs (rtx);
 | |
| 	HAWK_ASSERT (nargs >= 0 && nargs <= 1);
 | |
| 
 | |
| 	if (nargs == 0)
 | |
| 	{
 | |
| 		/* get the length of $0 */
 | |
| 		len = HAWK_OOECS_LEN(&rtx->inrec.line);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		v = hawk_rtx_getarg(rtx, 0);
 | |
| 		vtype = HAWK_RTX_GETVALTYPE(rtx, v);
 | |
| 
 | |
| 		switch (vtype)
 | |
| 		{
 | |
| 			case HAWK_VAL_MAP:
 | |
| 				/* map size */
 | |
| 				len = HAWK_MAP_SIZE(((hawk_val_map_t*)v)->map);
 | |
| 				break;
 | |
| 
 | |
| 			case HAWK_VAL_ARR:
 | |
| 				/* mode 0 - returns the number of set items instead of the last index + 1 */
 | |
| 				/* mode 1 - returns the last index + 1 */
 | |
| 				len = (mode == 0)? HAWK_ARR_TALLY(((hawk_val_arr_t*)v)->arr): HAWK_ARR_SIZE(((hawk_val_arr_t*)v)->arr);
 | |
| 				break;
 | |
| 
 | |
| 			case HAWK_VAL_BCHR:
 | |
| 				len = 1;
 | |
| 				break;
 | |
| 
 | |
| 			case HAWK_VAL_MBS:
 | |
| 				len = ((hawk_val_mbs_t*)v)->val.len;
 | |
| 				break;
 | |
| 
 | |
| 			case HAWK_VAL_BOB:
 | |
| 				len = ((hawk_val_bob_t*)v)->val.len;
 | |
| 				break;
 | |
| 
 | |
| 			case HAWK_VAL_CHAR:
 | |
| 				len = 1;
 | |
| 				break;
 | |
| 
 | |
| 			case HAWK_VAL_STR:
 | |
| 				/* string length */
 | |
| 				len = ((hawk_val_str_t*)v)->val.len;
 | |
| 				break;
 | |
| 
 | |
| 			default:
 | |
| 				/* convert to string and get length */
 | |
| 				str = hawk_rtx_valtooocstrdup(rtx, v, &len);
 | |
| 				if (HAWK_UNLIKELY(!str)) return -1;
 | |
| 				hawk_rtx_freemem(rtx, str);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	v = hawk_rtx_makeintval(rtx, len);
 | |
| 	if (HAWK_UNLIKELY(!v)) return -1;
 | |
| 
 | |
| 	hawk_rtx_setretval(rtx, v);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int fnc_length (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	return hawk_fnc_length(rtx, fi, 0);
 | |
| }
 | |
| 
 | |
| int hawk_fnc_substr (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	hawk_oow_t nargs;
 | |
| 	hawk_val_t* a0, * a1, * a2, * r;
 | |
| 	hawk_int_t lindex, lcount;
 | |
| 	int n;
 | |
| 
 | |
| 	nargs = hawk_rtx_getnargs(rtx);
 | |
| 	HAWK_ASSERT (nargs >= 2 && nargs <= 3);
 | |
| 
 | |
| 	a0 = hawk_rtx_getarg(rtx, 0);
 | |
| 	a1 = hawk_rtx_getarg(rtx, 1);
 | |
| 	a2 = (nargs >= 3)? hawk_rtx_getarg(rtx, 2): HAWK_NULL;
 | |
| 
 | |
| 	n = hawk_rtx_valtoint(rtx, a1, &lindex);
 | |
| 	if (n <= -1) return -1;
 | |
| 
 | |
| 	if (a2)
 | |
| 	{
 | |
| 		n = hawk_rtx_valtoint(rtx, a2, &lcount);
 | |
| 		if (n <= -1) return -1;
 | |
| 		if (lcount < 0) lcount = 0;
 | |
| 	}
 | |
| 	else lcount = HAWK_TYPE_MAX(hawk_int_t);
 | |
| 
 | |
| 	lindex = lindex - 1;
 | |
| 	if (lindex < 0) lindex = 0;
 | |
| 
 | |
| 	switch (HAWK_RTX_GETVALTYPE(rtx, a0))
 | |
| 	{
 | |
| 		case HAWK_VAL_BCHR:
 | |
| 		case HAWK_VAL_MBS:
 | |
| 		case HAWK_VAL_BOB:
 | |
| 		{
 | |
| 			hawk_bch_t* str;
 | |
| 			hawk_oow_t len;
 | |
| 
 | |
| 			str = hawk_rtx_getvalbcstr(rtx, a0, &len);
 | |
| 			if (HAWK_UNLIKELY(!str)) return -1;
 | |
| 
 | |
| 			if (lindex >= (hawk_int_t)len) lindex = (hawk_int_t)len;
 | |
| 			if (lcount > (hawk_int_t)len - lindex) lcount = (hawk_int_t)len - lindex;
 | |
| 
 | |
| 			r = hawk_rtx_makembsvalwithbchars(rtx, &str[lindex], (hawk_oow_t)lcount);
 | |
| 			hawk_rtx_freevalbcstr(rtx, a0, str);
 | |
| 			if (HAWK_UNLIKELY(!r)) return -1;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		default:
 | |
| 		{
 | |
| 			hawk_ooch_t* str;
 | |
| 			hawk_oow_t len;
 | |
| 
 | |
| 			str = hawk_rtx_getvaloocstr(rtx, a0, &len);
 | |
| 			if (HAWK_UNLIKELY(!str)) return -1;
 | |
| 
 | |
| 			if (lindex >= (hawk_int_t)len) lindex = (hawk_int_t)len;
 | |
| 			if (lcount > (hawk_int_t)len - lindex) lcount = (hawk_int_t)len - lindex;
 | |
| 
 | |
| 			r = hawk_rtx_makestrvalwithoochars(rtx, &str[lindex], (hawk_oow_t)lcount);
 | |
| 			hawk_rtx_freevaloocstr(rtx, a0, str);
 | |
| 			if (HAWK_UNLIKELY(!r)) return -1;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	hawk_rtx_setretval(rtx, r);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int fnc_split (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi, int use_array)
 | |
| {
 | |
| 	hawk_oow_t nargs;
 | |
| 	hawk_val_t* a0, * a2, * t0, * t1, * t2;
 | |
| 
 | |
| 	hawk_oocs_t str;
 | |
| 	hawk_oocs_t fs;
 | |
| 	hawk_ooch_t* fs_free = HAWK_NULL;
 | |
| 	hawk_ooch_t* p;
 | |
| 
 | |
| 	hawk_oow_t org_len;
 | |
| 	hawk_tre_t* fs_rex = HAWK_NULL;
 | |
| 	hawk_tre_t* fs_rex_free = HAWK_NULL;
 | |
| 
 | |
| 	hawk_oocs_t tok;
 | |
| 	hawk_int_t nflds;
 | |
| 	int is_byte_str;
 | |
| 	int x;
 | |
| 	int do_fld;
 | |
| 	int switch_fs_to_bchr;
 | |
| 
 | |
| 	nargs = hawk_rtx_getnargs(rtx);
 | |
| 	a0 = hawk_rtx_getarg(rtx, 0);
 | |
| 	a2 = (nargs >= 3)? hawk_rtx_getarg(rtx, 2): HAWK_NULL;
 | |
| 
 | |
| 	do_fld = 0;
 | |
| 	switch_fs_to_bchr = 0;
 | |
| 	is_byte_str = 0;
 | |
| 	str.ptr = HAWK_NULL;
 | |
| 	str.len = 0;
 | |
| 
 | |
| 	/* field seperator */
 | |
| 	t0 = a2? a2: hawk_rtx_getgbl(rtx, HAWK_GBL_FS); /* if a2 is not available, get the value from FS */
 | |
| 
 | |
| 	if (HAWK_RTX_GETVALTYPE(rtx, t0) == HAWK_VAL_NIL)
 | |
| 	{
 | |
| 		fs.ptr = HAWK_T(" ");
 | |
| 		fs.len = 1;
 | |
| 	}
 | |
| 	else if (HAWK_RTX_GETVALTYPE(rtx, t0) == HAWK_VAL_REX)
 | |
| 	{
 | |
| 		/* regular expression */
 | |
| 		fs_rex = ((hawk_val_rex_t*)t0)->code[rtx->gbl.ignorecase];
 | |
| 
 | |
| 		/* make the tokenizing loop below to take fs_rex by setting fs_len greater than 1*/
 | |
| 		fs.ptr = HAWK_NULL;
 | |
| 		fs.len = 2;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		/* no distinction between byte-string and normal string for the  lack of hawk_rtx_buildrex() with a byte string.
 | |
| 		 * TODO: distingish byte strings from normal strings once the regular expresson builder supports them */
 | |
| 
 | |
| 		fs.ptr = hawk_rtx_getvaloocstr(rtx, t0, &fs.len);
 | |
| 		if (HAWK_UNLIKELY(!fs.ptr)) goto oops;
 | |
| 
 | |
| 		fs_free = fs.ptr;
 | |
| 
 | |
| 		if (fs.len == 5 && fs.ptr[0] == '?')
 | |
| 		{
 | |
| 			do_fld = 1;
 | |
| 		}
 | |
| 		else if (fs.len > 1)
 | |
| 		{
 | |
| 			if (a2)
 | |
| 			{
 | |
| 				x = rtx->gbl.ignorecase?
 | |
| 					hawk_rtx_buildrex(rtx, fs.ptr, fs.len, HAWK_NULL, &fs_rex):
 | |
| 					hawk_rtx_buildrex(rtx, fs.ptr, fs.len, &fs_rex, HAWK_NULL);
 | |
| 				if (x <= -1) goto oops;
 | |
| 
 | |
| 				fs_rex_free = fs_rex;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				fs_rex = rtx->gbl.fs[rtx->gbl.ignorecase];
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			switch_fs_to_bchr = 1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* the first parameter - string to split */
 | |
| 	switch (HAWK_RTX_GETVALTYPE(rtx, a0))
 | |
| 	{
 | |
| 		case HAWK_VAL_BCHR:
 | |
| 		case HAWK_VAL_MBS:
 | |
| 		case HAWK_VAL_BOB:
 | |
| 			is_byte_str = 1;
 | |
| 			str.ptr = do_fld? (hawk_ooch_t*)hawk_rtx_valtobcstrdup(rtx, a0, &str.len):
 | |
| 			                  (hawk_ooch_t*)hawk_rtx_getvalbcstr(rtx, a0, &str.len);
 | |
| 			break;
 | |
| 
 | |
| 		default:
 | |
| 			is_byte_str = 0;
 | |
| 			str.ptr = do_fld? hawk_rtx_valtooocstrdup(rtx, a0, &str.len):
 | |
| 			                  hawk_rtx_getvaloocstr(rtx, a0, &str.len);
 | |
| 			break;
 | |
| 	}
 | |
| 	if (HAWK_UNLIKELY(!str.ptr)) goto oops;
 | |
| 
 | |
| 
 | |
| 	if (is_byte_str && switch_fs_to_bchr)
 | |
| 	{
 | |
| 		HAWK_ASSERT (fs_free = fs.ptr);
 | |
| 
 | |
| 		hawk_rtx_freevaloocstr(rtx, t0, fs_free);
 | |
| 
 | |
| 		fs.ptr = (hawk_ooch_t*)hawk_rtx_getvalbcstr(rtx, t0, &fs.len);
 | |
| 		if (HAWK_UNLIKELY(!fs.ptr)) goto oops;
 | |
| 
 | |
| 		fs_free = fs.ptr;
 | |
| 	}
 | |
| 
 | |
| 	t1 = use_array? hawk_rtx_makearrval(rtx, 16): hawk_rtx_makemapval(rtx);
 | |
| 	if (HAWK_UNLIKELY(!t1)) goto oops;
 | |
| 
 | |
| 	hawk_rtx_refupval(rtx, t1);
 | |
| 	x = hawk_rtx_setrefval(rtx, (hawk_val_ref_t*)hawk_rtx_getarg(rtx, 1), t1);
 | |
| 	hawk_rtx_refdownval(rtx, t1);
 | |
| 	if (HAWK_UNLIKELY(x <= -1)) goto oops;
 | |
| 
 | |
| 	/* fill the map with actual values */
 | |
| 	p = str.ptr; org_len = str.len; nflds = 0;
 | |
| 
 | |
| 	while (p)
 | |
| 	{
 | |
| 		if (fs_rex)
 | |
| 		{
 | |
| 			p = is_byte_str?
 | |
| 				(hawk_ooch_t*)hawk_rtx_tokbcharsbyrex(rtx, (hawk_bch_t*)str.ptr, org_len, (hawk_bch_t*)p, str.len, fs_rex, (hawk_bcs_t*)&tok):
 | |
| 				hawk_rtx_tokoocharsbyrex(rtx, str.ptr, org_len, p, str.len, fs_rex, &tok);
 | |
| 			if (p && hawk_rtx_geterrnum(rtx) != HAWK_ENOERR) goto oops;
 | |
| 		}
 | |
| 		else if (do_fld)
 | |
| 		{
 | |
| 			/* [NOTE] even if is_byte_str is true, the field seperator is of the ooch type.
 | |
| 			 *        there may be some data truncation and related issues */
 | |
| 			/* ? sep esc lq rq */
 | |
| 			p = is_byte_str?
 | |
| 				(hawk_ooch_t*)hawk_rtx_fldbchars(rtx, (hawk_bch_t*)p, str.len, (hawk_bchu_t)fs.ptr[1], (hawk_bchu_t)fs.ptr[2], (hawk_bchu_t)fs.ptr[3], (hawk_bchu_t)fs.ptr[4], (hawk_bcs_t*)&tok):
 | |
| 				hawk_rtx_fldoochars(rtx, p, str.len, fs.ptr[1], fs.ptr[2], fs.ptr[3], fs.ptr[4], &tok);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			p = is_byte_str?
 | |
| 				(hawk_ooch_t*)hawk_rtx_tokbcharswithbchars(rtx, (hawk_bch_t*)p, str.len, (hawk_bch_t*)fs.ptr, fs.len, (hawk_bcs_t*)&tok):
 | |
| 				hawk_rtx_tokoocharswithoochars(rtx, p, str.len, fs.ptr, fs.len, &tok);
 | |
| 		}
 | |
| 
 | |
| 		if (nflds == 0 && !p && tok.len == 0)
 | |
| 		{
 | |
| 			/* no field at all*/
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		HAWK_ASSERT ((tok.ptr && tok.len > 0) || tok.len == 0);
 | |
| 
 | |
| 		/* create the field string - however, the split function must
 | |
| 		 * create a numeric value if the string is a number */
 | |
| 		/*t2 = hawk_rtx_makestrvalwithoocs(rtx, &tok);*/
 | |
| 		/*t2 = hawk_rtx_makenstrvalwithoocs(rtx, &tok); */
 | |
| 		t2 = is_byte_str? hawk_rtx_makenumormbsvalwithbchars(rtx, (hawk_bch_t*)tok.ptr, tok.len):
 | |
| 		                  hawk_rtx_makenumorstrvalwithoochars(rtx, tok.ptr, tok.len);
 | |
| 		if (HAWK_UNLIKELY(!t2)) goto oops;
 | |
| 
 | |
| 		if (use_array)
 | |
| 		{
 | |
| 			if (!hawk_rtx_setarrvalfld(rtx, t1, ++nflds, t2))
 | |
| 			{
 | |
| 				hawk_rtx_refupval(rtx, t2);
 | |
| 				hawk_rtx_refdownval(rtx, t2);
 | |
| 				goto oops;
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			/* put it into the map */
 | |
| 			hawk_ooch_t key_buf[HAWK_SIZEOF(hawk_int_t)*8+2];
 | |
| 			hawk_oow_t key_len;
 | |
| 
 | |
| 			key_len = hawk_int_to_oocstr(++nflds, 10, HAWK_NULL, key_buf, HAWK_COUNTOF(key_buf));
 | |
| 			HAWK_ASSERT (key_len != (hawk_oow_t)-1);
 | |
| 
 | |
| 			if (!hawk_rtx_setmapvalfld(rtx, t1, key_buf, key_len, t2))
 | |
| 			{
 | |
| 				hawk_rtx_refupval(rtx, t2);
 | |
| 				hawk_rtx_refdownval(rtx, t2);
 | |
| 				goto oops;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (is_byte_str)
 | |
| 			str.len = org_len - ((hawk_bch_t*)p - (hawk_bch_t*)str.ptr);
 | |
| 		else
 | |
| 			str.len = org_len - (p - str.ptr);
 | |
| 	}
 | |
| 
 | |
| 	if (do_fld) { hawk_rtx_freemem(rtx, str.ptr); }
 | |
| 	else if (is_byte_str) { hawk_rtx_freevalbcstr(rtx, a0, (hawk_bch_t*)str.ptr); }
 | |
| 	else { hawk_rtx_freevaloocstr(rtx, a0, str.ptr); }
 | |
| 
 | |
| 	if (fs_free)
 | |
| 	{
 | |
| 		if (is_byte_str && switch_fs_to_bchr)
 | |
| 			hawk_rtx_freevalbcstr(rtx, t0, (hawk_bch_t*)fs_free);
 | |
| 		else
 | |
| 			hawk_rtx_freevaloocstr(rtx, t0, fs_free);
 | |
| 	}
 | |
| 
 | |
| 	if (fs_rex_free)
 | |
| 	{
 | |
| 		if (rtx->gbl.ignorecase)
 | |
| 			hawk_rtx_freerex(rtx, HAWK_NULL, fs_rex_free);
 | |
| 		else
 | |
| 			hawk_rtx_freerex(rtx, fs_rex_free, HAWK_NULL);
 | |
| 	}
 | |
| 
 | |
| 	t1 = hawk_rtx_makeintval(rtx, nflds);
 | |
| 	if (HAWK_UNLIKELY(!t1)) return -1;
 | |
| 
 | |
| 	hawk_rtx_setretval(rtx, t1);
 | |
| 	return 0;
 | |
| 
 | |
| oops:
 | |
| 	if (str.ptr)
 | |
| 	{
 | |
| 		if (do_fld) { hawk_rtx_freemem(rtx, str.ptr); }
 | |
| 		else if (is_byte_str) { hawk_rtx_freevalbcstr(rtx, a0, (hawk_bch_t*)str.ptr); }
 | |
| 		else { hawk_rtx_freevaloocstr(rtx, a0, str.ptr); }
 | |
| 	}
 | |
| 
 | |
| 	if (fs_free)
 | |
| 	{
 | |
| 		if (is_byte_str && switch_fs_to_bchr)
 | |
| 			hawk_rtx_freevalbcstr(rtx, t0, (hawk_bch_t*)fs_free);
 | |
| 		else
 | |
| 			hawk_rtx_freevaloocstr(rtx, t0, fs_free);
 | |
| 	}
 | |
| 
 | |
| 	if (fs_rex_free)
 | |
| 	{
 | |
| 		if (rtx->gbl.ignorecase)
 | |
| 			hawk_rtx_freerex(rtx, HAWK_NULL, fs_rex_free);
 | |
| 		else
 | |
| 			hawk_rtx_freerex(rtx, fs_rex_free, HAWK_NULL);
 | |
| 	}
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| int hawk_fnc_split (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	return fnc_split(rtx, fi, 0);
 | |
| }
 | |
| 
 | |
| int hawk_fnc_splita (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	return fnc_split(rtx, fi, 1);
 | |
| }
 | |
| 
 | |
| int hawk_fnc_tolower (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	hawk_oow_t i;
 | |
| 	hawk_val_t* a0, * r;
 | |
| 
 | |
| 	a0 = hawk_rtx_getarg(rtx, 0);
 | |
| 	switch (HAWK_RTX_GETVALTYPE(rtx, a0))
 | |
| 	{
 | |
| 		case HAWK_VAL_BCHR:
 | |
| 		{
 | |
| 			hawk_bch_t tmp = HAWK_RTX_GETBCHRFROMVAL(rtx, a0);
 | |
| 			tmp = hawk_to_bch_lower(tmp);
 | |
| 			r = hawk_rtx_makebchrval(rtx, tmp);
 | |
| 			if (HAWK_UNLIKELY(!r)) return -1;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		case HAWK_VAL_MBS:
 | |
| 		case HAWK_VAL_BOB:
 | |
| 		{
 | |
| 			hawk_bcs_t str;
 | |
| 			str.ptr = hawk_rtx_getvalbcstr(rtx, a0, &str.len);
 | |
| 			if (!str.ptr) return -1;
 | |
| 			r = hawk_rtx_makembsvalwithbcs(rtx, &str);
 | |
| 			hawk_rtx_freevalbcstr(rtx, a0, str.ptr);
 | |
| 			if (HAWK_UNLIKELY(!r)) return -1;
 | |
| 			str.ptr = ((hawk_val_mbs_t*)r)->val.ptr;
 | |
| 			str.len = ((hawk_val_mbs_t*)r)->val.len;
 | |
| 			for (i = 0; i < str.len; i++) str.ptr[i] = hawk_to_bch_lower(str.ptr[i]);
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		case HAWK_VAL_CHAR:
 | |
| 		{
 | |
| 			hawk_ooch_t tmp = HAWK_RTX_GETCHARFROMVAL(rtx, a0);
 | |
| 			tmp = hawk_to_ooch_lower(tmp);
 | |
| 			r = hawk_rtx_makecharval(rtx, tmp);
 | |
| 			if (HAWK_UNLIKELY(!r)) return -1;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		default:
 | |
| 		{
 | |
| 			hawk_oocs_t str;
 | |
| 			str.ptr = hawk_rtx_getvaloocstr(rtx, a0, &str.len);
 | |
| 			if (!str.ptr) return -1;
 | |
| 			r = hawk_rtx_makestrvalwithoocs(rtx, &str);
 | |
| 			hawk_rtx_freevaloocstr(rtx, a0, str.ptr);
 | |
| 			if (HAWK_UNLIKELY(!r)) return -1;
 | |
| 			str.ptr = ((hawk_val_str_t*)r)->val.ptr;
 | |
| 			str.len = ((hawk_val_str_t*)r)->val.len;
 | |
| 			for (i = 0; i < str.len; i++) str.ptr[i] = hawk_to_ooch_lower(str.ptr[i]);
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	hawk_rtx_setretval(rtx, r);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int hawk_fnc_toupper (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	hawk_oow_t i;
 | |
| 	hawk_val_t* a0, * r;
 | |
| 
 | |
| 	a0 = hawk_rtx_getarg(rtx, 0);
 | |
| 	switch (HAWK_RTX_GETVALTYPE(rtx, a0))
 | |
| 	{
 | |
| 		case  HAWK_VAL_BCHR:
 | |
| 		{
 | |
| 			hawk_bch_t tmp = HAWK_RTX_GETCHARFROMVAL(rtx, a0);
 | |
| 			tmp = hawk_to_bch_upper(tmp);
 | |
| 			r = hawk_rtx_makebchrval(rtx, tmp);
 | |
| 			if (HAWK_UNLIKELY(!r)) return -1;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		case HAWK_VAL_MBS:
 | |
| 		case HAWK_VAL_BOB:
 | |
| 		{
 | |
| 			hawk_bcs_t str;
 | |
| 			str.ptr = hawk_rtx_getvalbcstr(rtx, a0, &str.len);
 | |
| 			if (!str.ptr) return -1;
 | |
| 			r = hawk_rtx_makembsvalwithbcs(rtx, &str);
 | |
| 			hawk_rtx_freevalbcstr(rtx, a0, str.ptr);
 | |
| 			if (HAWK_UNLIKELY(!r)) return -1;
 | |
| 			str.ptr = ((hawk_val_mbs_t*)r)->val.ptr;
 | |
| 			str.len = ((hawk_val_mbs_t*)r)->val.len;
 | |
| 			for (i = 0; i < str.len; i++) str.ptr[i] = hawk_to_bch_upper(str.ptr[i]);
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		case  HAWK_VAL_CHAR:
 | |
| 		{
 | |
| 			hawk_ooch_t tmp = HAWK_RTX_GETCHARFROMVAL(rtx, a0);
 | |
| 			tmp = hawk_to_ooch_upper(tmp);
 | |
| 			r = hawk_rtx_makecharval(rtx, tmp);
 | |
| 			if (HAWK_UNLIKELY(!r)) return -1;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		default:
 | |
| 		{
 | |
| 			hawk_oocs_t str;
 | |
| 			str.ptr = hawk_rtx_getvaloocstr(rtx, a0, &str.len);
 | |
| 			if (!str.ptr) return -1;
 | |
| 			r = hawk_rtx_makestrvalwithoocs(rtx, &str);
 | |
| 			hawk_rtx_freevaloocstr(rtx, a0, str.ptr);
 | |
| 			if (HAWK_UNLIKELY(!r)) return -1;
 | |
| 			str.ptr = ((hawk_val_str_t*)r)->val.ptr;
 | |
| 			str.len = ((hawk_val_str_t*)r)->val.len;
 | |
| 			for (i = 0; i < str.len; i++) str.ptr[i] = hawk_to_ooch_upper(str.ptr[i]);
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	hawk_rtx_setretval(rtx, r);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int __substitute_oocs (hawk_rtx_t* rtx, hawk_oow_t* max_count, hawk_tre_t* rex, hawk_oocs_t* s1, hawk_oocs_t* s2, hawk_ooecs_t* new, int extended, hawk_oow_t op_pos)
 | |
| {
 | |
| 	hawk_oocs_t mat, pmat, cur;
 | |
| 	hawk_oocs_t submat[9];
 | |
| 	hawk_oow_t sub_count, op_count, match_limit;
 | |
| 	hawk_ooch_t* s2_end;
 | |
| 
 | |
| 	s2_end = s2->ptr + s2->len;
 | |
| 	cur.ptr = s2->ptr;
 | |
| 	cur.len = s2->len;
 | |
| 	sub_count = 0;
 | |
| 	op_count = 0;
 | |
| 	match_limit = *max_count;
 | |
| 
 | |
| 	pmat.ptr = HAWK_NULL;
 | |
| 	pmat.len = 0;
 | |
| 
 | |
| 	/* perform test when cur_ptr == s2_end also because
 | |
| 	 * end of string($) needs to be tested */
 | |
| 	while (cur.ptr <= s2_end)
 | |
| 	{
 | |
| 		int n;
 | |
| 		hawk_oow_t m, i;
 | |
| 
 | |
| 		if (sub_count < match_limit)
 | |
| 		{
 | |
| 			n = hawk_rtx_matchrexwithoocs(rtx, rex, s2, &cur, &mat, (extended? submat: HAWK_NULL));
 | |
| 			if (HAWK_UNLIKELY(n <= -1)) goto oops;
 | |
| 		}
 | |
| 		else n = 0;
 | |
| 
 | |
| 		if (n == 0)
 | |
| 		{
 | |
| 			/* no more match found */
 | |
| 			if (hawk_ooecs_ncat(new, cur.ptr, cur.len) == (hawk_oow_t)-1) goto oops;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		if (mat.len == 0 && pmat.ptr != HAWK_NULL && mat.ptr == pmat.ptr + pmat.len)
 | |
| 		{
 | |
| 			/* match length is 0 and the match is still at the
 | |
| 			 * end of the previous match */
 | |
| 			goto skip_one_char;
 | |
| 		}
 | |
| 
 | |
| 		if (hawk_ooecs_ncat(new, cur.ptr, mat.ptr - cur.ptr) == (hawk_oow_t)-1) goto oops;
 | |
| 
 | |
| 		if (extended && op_pos > 0 && (op_pos - 1) != op_count)
 | |
| 		{
 | |
| 			/* don't perform substitution. just copy the original string to the output */
 | |
| 			if (hawk_ooecs_ncat(new, mat.ptr, mat.len) == (hawk_oow_t)-1) goto oops;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			for (i = 0; i < s1->len; i++)
 | |
| 			{
 | |
| 				if (s1->ptr[i] == '\\' && (i + 1) < s1->len)
 | |
| 				{
 | |
| 					if (extended) /* for gensub */
 | |
| 					{
 | |
| 						hawk_ooch_t ic = s1->ptr[i + 1];
 | |
| 						if (ic == '0')
 | |
| 						{
 | |
| 							m = hawk_ooecs_ncat(new, mat.ptr, mat.len);
 | |
| 						}
 | |
| 						else if (ic >= '1' && ic <= '9')
 | |
| 						{
 | |
| 							hawk_oow_t idx = (ic - '0') - 1;
 | |
| 							m = hawk_ooecs_ncat(new, submat[idx].ptr, submat[idx].len);
 | |
| 						}
 | |
| 						else goto escape;
 | |
| 					}
 | |
| 					else
 | |
| 					{
 | |
| 					escape:
 | |
| 						m = hawk_ooecs_ccat(new, s1->ptr[i + 1]);
 | |
| 					}
 | |
| 					i++; /* skip the backslash */
 | |
| 				}
 | |
| 				else if (s1->ptr[i] == '&')
 | |
| 				{
 | |
| 					m = hawk_ooecs_ncat(new, mat.ptr, mat.len);
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					m = hawk_ooecs_ccat(new, s1->ptr[i]);
 | |
| 				}
 | |
| 				if (HAWK_UNLIKELY(m == (hawk_oow_t)-1)) goto oops;
 | |
| 			}
 | |
| 
 | |
| 			sub_count++; /* number of actual substitutions */
 | |
| 		}
 | |
| 		op_count++; /* actual susstitutions + skipped substitutions. not needed if extended is false */
 | |
| 		cur.len = cur.len - ((mat.ptr - cur.ptr) + mat.len);
 | |
| 		cur.ptr = mat.ptr + mat.len;
 | |
| 
 | |
| 		pmat = mat;
 | |
| 
 | |
| 		if (mat.len == 0)
 | |
| 		{
 | |
| 		skip_one_char:
 | |
| 			/* special treatment is needed if match length is 0 */
 | |
| 			if (cur.ptr < s2_end) /* $ matches at s2_end. with this check, '\0' or whatever character after the end may get appended redundantly */
 | |
| 			{
 | |
| 				m = hawk_ooecs_ncat(new, cur.ptr, 1);
 | |
| 				if (HAWK_UNLIKELY(m == (hawk_oow_t)-1)) goto oops;
 | |
| 			}
 | |
| 			cur.ptr++; cur.len--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	*max_count = sub_count;
 | |
| 	return 0;
 | |
| 
 | |
| oops:
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static int __substitute_bcs (hawk_rtx_t* rtx, hawk_oow_t* max_count, hawk_tre_t* rex, hawk_bcs_t* s1, hawk_bcs_t* s2, hawk_becs_t* new, int extended, hawk_oow_t op_pos)
 | |
| {
 | |
| 	hawk_bcs_t mat, pmat, cur;
 | |
| 	hawk_bcs_t submat[9];
 | |
| 	hawk_oow_t sub_count, op_count, match_limit;
 | |
| 	hawk_bch_t* s2_end;
 | |
| 
 | |
| 	s2_end = s2->ptr + s2->len;
 | |
| 	cur.ptr = s2->ptr;
 | |
| 	cur.len = s2->len;
 | |
| 	sub_count = 0;
 | |
| 	op_count = 0;
 | |
| 	match_limit = *max_count;
 | |
| 
 | |
| 	pmat.ptr = HAWK_NULL;
 | |
| 	pmat.len = 0;
 | |
| 
 | |
| 	/* perform test when cur_ptr == s2_end also because
 | |
| 	 * end of string($) needs to be tested */
 | |
| 	while (cur.ptr <= s2_end)
 | |
| 	{
 | |
| 		int n;
 | |
| 		hawk_oow_t m, i;
 | |
| 
 | |
| 		if (sub_count < match_limit)
 | |
| 		{
 | |
| 			n = hawk_rtx_matchrexwithbcs(rtx, rex, s2, &cur, &mat, (extended? submat: HAWK_NULL));
 | |
| 			if (HAWK_UNLIKELY(n <= -1)) goto oops;
 | |
| 		}
 | |
| 		else n = 0;
 | |
| 
 | |
| 		if (n == 0)
 | |
| 		{
 | |
| 			/* no more match found */
 | |
| 			if (hawk_becs_ncat(new, cur.ptr, cur.len) == (hawk_oow_t)-1) goto oops;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		if (mat.len == 0 && pmat.ptr != HAWK_NULL && mat.ptr == pmat.ptr + pmat.len)
 | |
| 		{
 | |
| 			/* match length is 0 and the match is still at the
 | |
| 			 * end of the previous match */
 | |
| 			goto skip_one_char;
 | |
| 		}
 | |
| 
 | |
| 		if (hawk_becs_ncat(new, cur.ptr, mat.ptr - cur.ptr) == (hawk_oow_t)-1) goto oops;
 | |
| 
 | |
| 		if (extended && op_pos > 0 && (op_pos - 1) != op_count)
 | |
| 		{
 | |
| 			/* don't perform substitution. just copy the original string to the output */
 | |
| 			if (hawk_becs_ncat(new, mat.ptr, mat.len) == (hawk_oow_t)-1) goto oops;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			for (i = 0; i < s1->len; i++)
 | |
| 			{
 | |
| 				if (s1->ptr[i] == '\\' && (i + 1) < s1->len)
 | |
| 				{
 | |
| 					if (extended) /* for gensub */
 | |
| 					{
 | |
| 						hawk_bch_t ic = s1->ptr[i + 1];
 | |
| 						if (ic == '0')
 | |
| 						{
 | |
| 							m = hawk_becs_ncat(new, mat.ptr, mat.len);
 | |
| 						}
 | |
| 						else if (ic >= '1' && ic <= '9')
 | |
| 						{
 | |
| 							hawk_oow_t idx = (ic - '0') - 1;
 | |
| 							m = hawk_becs_ncat(new, submat[idx].ptr, submat[idx].len);
 | |
| 						}
 | |
| 						else goto escape;
 | |
| 					}
 | |
| 					else
 | |
| 					{
 | |
| 					escape:
 | |
| 						m = hawk_becs_ccat(new, s1->ptr[i + 1]);
 | |
| 					}
 | |
| 					i++; /* skip the backslash */
 | |
| 				}
 | |
| 				else if (s1->ptr[i] == '&')
 | |
| 				{
 | |
| 					m = hawk_becs_ncat(new, mat.ptr, mat.len);
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					m = hawk_becs_ccat(new, s1->ptr[i]);
 | |
| 				}
 | |
| 				if (HAWK_UNLIKELY(m == (hawk_oow_t)-1)) goto oops;
 | |
| 			}
 | |
| 			sub_count++;
 | |
| 		}
 | |
| 		op_count++;
 | |
| 		cur.len = cur.len - ((mat.ptr - cur.ptr) + mat.len);
 | |
| 		cur.ptr = mat.ptr + mat.len;
 | |
| 
 | |
| 		pmat = mat;
 | |
| 
 | |
| 		if (mat.len == 0)
 | |
| 		{
 | |
| 		skip_one_char:
 | |
| 			/* special treatment is needed if match length is 0 */
 | |
| 			if (cur.ptr < s2_end) /* $ matches at s2_end. with this check, '\0' or whatever character after the end may get appended redundantly */
 | |
| 			{
 | |
| 				m = hawk_becs_ncat(new, cur.ptr, 1);
 | |
| 				if (HAWK_UNLIKELY(m == (hawk_oow_t)-1)) goto oops;
 | |
| 			}
 | |
| 			cur.ptr++; cur.len--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	*max_count = sub_count;
 | |
| 	return 0;
 | |
| 
 | |
| oops:
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static int __substitute (hawk_rtx_t* rtx, hawk_oow_t max_count)
 | |
| {
 | |
| 	hawk_oow_t nargs;
 | |
| 	hawk_val_t* a0, * a1, * r2, * v;
 | |
| 	hawk_val_type_t a0_vtype;
 | |
| 
 | |
| 	hawk_oocs_t s0;
 | |
| 	hawk_ptl_t s1;
 | |
| 	hawk_ptl_t s2;
 | |
| 
 | |
| 	int s1_free = 0;
 | |
| 	int s2_free = 0;
 | |
| 
 | |
| 	hawk_tre_t* rex = HAWK_NULL;
 | |
| 	hawk_tre_t* rex_free = HAWK_NULL;
 | |
| 	hawk_oow_t sub_count;
 | |
| 
 | |
| 	s0.ptr = HAWK_NULL;
 | |
| 	s0.len = 0;
 | |
| 
 | |
| 	s1.ptr = HAWK_NULL;
 | |
| 	s1.len = 0;
 | |
| 
 | |
| 	s2.ptr = HAWK_NULL;
 | |
| 	s2.len = 0;
 | |
| 
 | |
| 	nargs = hawk_rtx_getnargs(rtx);
 | |
| 	a0 = hawk_rtx_getarg(rtx, 0);
 | |
| 	a1 = hawk_rtx_getarg(rtx, 1);
 | |
| 
 | |
| 	a0_vtype = HAWK_RTX_GETVALTYPE(rtx, a0);
 | |
| 
 | |
| 	/* the first argument - pattern */
 | |
| 	if (a0_vtype == HAWK_VAL_REX)
 | |
| 	{
 | |
| 		rex = ((hawk_val_rex_t*)a0)->code[rtx->gbl.ignorecase];
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		s0.ptr = hawk_rtx_getvaloocstr(rtx, a0, &s0.len);
 | |
| 		if (HAWK_UNLIKELY(!s0.ptr)) goto oops;
 | |
| 	}
 | |
| 
 | |
| 	/* the optional third argument - string to manipulate */
 | |
| 	if (nargs < 3)
 | |
| 	{
 | |
| 		/* is this correct? any needs to use inrec.d0? */
 | |
| 		s2.ptr = HAWK_OOECS_PTR(&rtx->inrec.line);
 | |
| 		s2.len = HAWK_OOECS_LEN(&rtx->inrec.line);
 | |
| 
 | |
| 		/* the second argument - substitute */
 | |
| 		s1.ptr = hawk_rtx_getvaloocstr(rtx, a1, &s1.len);
 | |
| 		s1_free = 1;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		r2 = hawk_rtx_getrefval(rtx, (hawk_val_ref_t*)hawk_rtx_getarg(rtx, 2));
 | |
| 
 | |
| 		switch (HAWK_RTX_GETVALTYPE(rtx, r2))
 | |
| 		{
 | |
| 			case HAWK_VAL_BCHR:
 | |
| 			case HAWK_VAL_MBS:
 | |
| 			case HAWK_VAL_BOB:
 | |
| 				s2.ptr = hawk_rtx_getvalbcstr(rtx, r2, &s2.len);
 | |
| 				s2_free = 2;
 | |
| 
 | |
| 				/* the second argument - substitute */
 | |
| 				s1.ptr = hawk_rtx_getvalbcstr(rtx, a1, &s1.len);
 | |
| 				s1_free = 2;
 | |
| 				break;
 | |
| 
 | |
| 			default:
 | |
| 				s2.ptr = hawk_rtx_getvaloocstr(rtx, r2, &s2.len);
 | |
| 				s2_free = 1;
 | |
| 
 | |
| 				/* the second argument - substitute */
 | |
| 				s1.ptr = hawk_rtx_getvaloocstr(rtx, a1, &s1.len);
 | |
| 				s1_free = 1;
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (HAWK_UNLIKELY(!s1.ptr || !s2.ptr)) goto oops;
 | |
| 
 | |
| 	if (a0_vtype != HAWK_VAL_REX)
 | |
| 	{
 | |
| 		int x;
 | |
| 
 | |
| 		x = rtx->gbl.ignorecase?
 | |
| 			hawk_rtx_buildrex(rtx, s0.ptr, s0.len, HAWK_NULL, &rex):
 | |
| 			hawk_rtx_buildrex(rtx, s0.ptr, s0.len, &rex, HAWK_NULL);
 | |
| 		if (HAWK_UNLIKELY(x <= -1)) goto oops;
 | |
| 
 | |
| 		rex_free = rex;
 | |
| 	}
 | |
| 
 | |
| 	sub_count = max_count;
 | |
| 	if (s2_free == 2)
 | |
| 	{
 | |
| 		hawk_becs_clear(&rtx->fnc.bout);
 | |
| 		if (__substitute_bcs(rtx, &sub_count, rex, (hawk_bcs_t*)&s1, (hawk_bcs_t*)&s2, &rtx->fnc.bout, 0, 0) <= -1) goto oops;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		hawk_ooecs_clear(&rtx->fnc.oout);
 | |
| 		if (__substitute_oocs(rtx, &sub_count, rex, (hawk_oocs_t*)&s1, (hawk_oocs_t*)&s2, &rtx->fnc.oout, 0, 0) <= -1) goto oops;
 | |
| 	}
 | |
| 
 | |
| 	if (rex_free)
 | |
| 	{
 | |
| 		if (rtx->gbl.ignorecase)
 | |
| 			hawk_rtx_freerex(rtx, HAWK_NULL, rex_free);
 | |
| 		else
 | |
| 			hawk_rtx_freerex(rtx, rex_free, HAWK_NULL);
 | |
| 		rex_free = HAWK_NULL;
 | |
| 	}
 | |
| 
 | |
| 	switch (s2_free)
 | |
| 	{
 | |
| 		case 1:
 | |
| 			hawk_rtx_freevaloocstr(rtx, r2, s2.ptr);
 | |
| 			break;
 | |
| 		case 2:
 | |
| 			hawk_rtx_freevalbcstr(rtx, r2, s2.ptr);
 | |
| 			break;
 | |
| 	}
 | |
| 	s2.ptr = HAWK_NULL;
 | |
| 
 | |
| 	switch (s1_free)
 | |
| 	{
 | |
| 		case 1:
 | |
| 			hawk_rtx_freevaloocstr(rtx, a1, s1.ptr);
 | |
| 			break;
 | |
| 		case 2:
 | |
| 			hawk_rtx_freevalbcstr(rtx, a1, s1.ptr);
 | |
| 			break;
 | |
| 	}
 | |
| 	s1.ptr = HAWK_NULL;
 | |
| 
 | |
| 	if (s0.ptr)
 | |
| 	{
 | |
| 		hawk_rtx_freevaloocstr(rtx, a0, s0.ptr);
 | |
| 		s0.ptr = HAWK_NULL;
 | |
| 	}
 | |
| 
 | |
| 	if (sub_count > 0)
 | |
| 	{
 | |
| 		int n;
 | |
| 		if (nargs < 3)
 | |
| 		{
 | |
| 			n = hawk_rtx_setrec(rtx, 0, HAWK_OOECS_OOCS(&rtx->fnc.oout), 0);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			v = (s2_free == 2)?
 | |
| 				hawk_rtx_makembsvalwithbcs(rtx, HAWK_BECS_BCS(&rtx->fnc.bout)):
 | |
| 				hawk_rtx_makestrvalwithoocs(rtx, HAWK_OOECS_OOCS(&rtx->fnc.oout));
 | |
| 			if (HAWK_UNLIKELY(!v)) goto oops;
 | |
| 
 | |
| 			hawk_rtx_refupval(rtx, v);
 | |
| 			n = hawk_rtx_setrefval(rtx, (hawk_val_ref_t*)hawk_rtx_getarg(rtx, 2), v);
 | |
| 			hawk_rtx_refdownval(rtx, v);
 | |
| 		}
 | |
| 		if (HAWK_UNLIKELY(n <= -1)) goto oops;
 | |
| 	}
 | |
| 
 | |
| 	v = hawk_rtx_makeintval(rtx, sub_count);
 | |
| 	if (HAWK_UNLIKELY(!v)) return -1;
 | |
| 
 | |
| 	hawk_rtx_setretval(rtx, v);
 | |
| 	return 0;
 | |
| 
 | |
| oops:
 | |
| 	if (rex_free)
 | |
| 	{
 | |
| 		if (rtx->gbl.ignorecase)
 | |
| 			hawk_rtx_freerex(rtx, HAWK_NULL, rex_free);
 | |
| 		else
 | |
| 			hawk_rtx_freerex(rtx, rex_free, HAWK_NULL);
 | |
| 	}
 | |
| 
 | |
| 	if (s2.ptr)
 | |
| 	{
 | |
| 		switch (s2_free)
 | |
| 		{
 | |
| 			case 1:
 | |
| 				hawk_rtx_freevaloocstr(rtx, r2, s2.ptr);
 | |
| 				break;
 | |
| 			case 2:
 | |
| 				hawk_rtx_freevalbcstr(rtx, r2, s2.ptr);
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (s1.ptr)
 | |
| 	{
 | |
| 		switch (s1_free)
 | |
| 		{
 | |
| 			case 1:
 | |
| 				hawk_rtx_freevaloocstr(rtx, a1, s1.ptr);
 | |
| 				break;
 | |
| 			case 2:
 | |
| 				hawk_rtx_freevalbcstr(rtx, a1, s1.ptr);
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (s0.ptr) hawk_rtx_freevaloocstr(rtx, a0, s0.ptr);
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| int hawk_fnc_gsub (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	/* gsub(/a/, "#");
 | |
| 	 * x="abac"; gsub(/a/, "#", x); */
 | |
| 	return __substitute(rtx, HAWK_TYPE_MAX(hawk_oow_t));
 | |
| }
 | |
| 
 | |
| int hawk_fnc_sub (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	/* sub(/a/, "#");
 | |
| 	 * x="abac"; sub(/a/, "#", x); */
 | |
| 	return __substitute(rtx, 1);
 | |
| }
 | |
| 
 | |
| int hawk_fnc_gensub (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	/* x=gensub(/(abc)/, "\\1\\1", "g");
 | |
| 	 * x=gensub(/(abc)/, "\\1\\1", "g", $0);
 | |
| 	 * x=gensub(/(tiger|deer)/, "\\1-\\1", "g", "the tiger pounced on the deer");
 | |
| 	 */
 | |
| 
 | |
| 	hawk_oow_t nargs;
 | |
| 	hawk_val_t* a0, * a1, * a2, * a3, * v;
 | |
| 	hawk_val_type_t a0_vtype;
 | |
| 
 | |
| 	hawk_oocs_t s0;
 | |
| 	hawk_ptl_t s1;
 | |
| 	hawk_ptl_t s2;
 | |
| 
 | |
| 	int s1_free = 0;
 | |
| 	int s2_free = 0;
 | |
| 
 | |
| 	hawk_tre_t* rex = HAWK_NULL;
 | |
| 	hawk_tre_t* rex_free = HAWK_NULL;
 | |
| 	hawk_oow_t op_pos;
 | |
| 	hawk_oow_t max_count;
 | |
| 	hawk_oow_t sub_count;
 | |
| 
 | |
| 	s0.ptr = HAWK_NULL;
 | |
| 	s0.len = 0;
 | |
| 
 | |
| 	s1.ptr = HAWK_NULL;
 | |
| 	s1.len = 0;
 | |
| 
 | |
| 	s2.ptr = HAWK_NULL;
 | |
| 	s2.len = 0;
 | |
| 
 | |
| 	nargs = hawk_rtx_getnargs(rtx);
 | |
| 	a0 = hawk_rtx_getarg(rtx, 0); /* pattern */
 | |
| 	a1 = hawk_rtx_getarg(rtx, 1); /* substitute */
 | |
| 	a2 = hawk_rtx_getarg(rtx, 2); /* mode or position */
 | |
| 
 | |
| 	a0_vtype = HAWK_RTX_GETVALTYPE(rtx, a0);
 | |
| 
 | |
| 	/* the first argument - pattern */
 | |
| 	if (a0_vtype == HAWK_VAL_REX)
 | |
| 	{
 | |
| 		rex = ((hawk_val_rex_t*)a0)->code[rtx->gbl.ignorecase];
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		s0.ptr = hawk_rtx_getvaloocstr(rtx, a0, &s0.len);
 | |
| 		if (HAWK_UNLIKELY(!s0.ptr)) goto oops;
 | |
| 	}
 | |
| 
 | |
| 	max_count = 1;
 | |
| 	op_pos = 0;
 | |
| 	switch (HAWK_RTX_GETVALTYPE(rtx, a2))
 | |
| 	{
 | |
| 		case HAWK_VAL_BCHR:
 | |
| 		{
 | |
| 			hawk_bch_t ch;
 | |
| 			ch = HAWK_RTX_GETBCHRFROMVAL(rtx, a2);
 | |
| 			if (ch == 'g' || ch == 'G') max_count = HAWK_TYPE_MAX(hawk_oow_t);
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		case HAWK_VAL_MBS:
 | |
| 		case HAWK_VAL_BOB: /* no separate code as hawk_val_mbs_t and hawk_val_bob_t are similar for the ptr field type */
 | |
| 		{
 | |
| 			if (((hawk_val_mbs_t*)a2)->val.len >= 1)
 | |
| 			{
 | |
| 				hawk_bch_t ch;
 | |
| 				ch = ((hawk_val_mbs_t*)a2)->val.ptr[0];
 | |
| 				if (ch == 'g' || ch == 'G') max_count = HAWK_TYPE_MAX(hawk_oow_t);
 | |
| 			}
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		case HAWK_VAL_CHAR:
 | |
| 		{
 | |
| 			hawk_ooch_t ch;
 | |
| 			ch = HAWK_RTX_GETCHARFROMVAL(rtx, a2);
 | |
| 			if (ch == 'g' || ch == 'G') max_count = HAWK_TYPE_MAX(hawk_oow_t);
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		case HAWK_VAL_STR:
 | |
| 			if (((hawk_val_str_t*)a2)->val.len >= 1)
 | |
| 			{
 | |
| 				hawk_ooch_t ch;
 | |
| 				ch = ((hawk_val_str_t*)a2)->val.ptr[0];
 | |
| 				if (ch == 'g' || ch == 'G') max_count = HAWK_TYPE_MAX(hawk_oow_t);
 | |
| 			}
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	if (max_count != HAWK_TYPE_MAX(hawk_oow_t))
 | |
| 	{
 | |
| 		hawk_int_t l;
 | |
| 		hawk_flt_t r;
 | |
| 		int n;
 | |
| 
 | |
| 		n = hawk_rtx_valtonum(rtx, a2, &l, &r);
 | |
| 		if (n == 0)
 | |
| 		{
 | |
| 			if (l > 0)
 | |
| 			{
 | |
| 				op_pos = (hawk_oow_t)l;
 | |
| 				max_count = 1;
 | |
| 			}
 | |
| 		}
 | |
| 		else if (n > 0)
 | |
| 		{
 | |
| 			if (r > 0.0)
 | |
| 			{
 | |
| 				op_pos = (hawk_oow_t)r;
 | |
| 				max_count = 1;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* the optional fourth argument - string to manipulate */
 | |
| 	if (nargs < 4)
 | |
| 	{
 | |
| 		/* is this correct? any needs to use inrec.d0? */
 | |
| 		s2.ptr = HAWK_OOECS_PTR(&rtx->inrec.line);
 | |
| 		s2.len = HAWK_OOECS_LEN(&rtx->inrec.line);
 | |
| 
 | |
| 		/* the second argument - substitute */
 | |
| 		s1.ptr = hawk_rtx_getvaloocstr(rtx, a1, &s1.len);
 | |
| 		s1_free = 1;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		a3 = hawk_rtx_getarg(rtx, 3);
 | |
| 
 | |
| 		switch (HAWK_RTX_GETVALTYPE(rtx, a3))
 | |
| 		{
 | |
| 			case HAWK_VAL_BCHR:
 | |
| 			case HAWK_VAL_MBS:
 | |
| 			case HAWK_VAL_BOB:
 | |
| 				s2.ptr = hawk_rtx_getvalbcstr(rtx, a3, &s2.len);
 | |
| 				s2_free = 2;
 | |
| 
 | |
| 				/* the second argument - substitute */
 | |
| 				s1.ptr = hawk_rtx_getvalbcstr(rtx, a1, &s1.len);
 | |
| 				s1_free = 2;
 | |
| 				break;
 | |
| 
 | |
| 			default:
 | |
| 				s2.ptr = hawk_rtx_getvaloocstr(rtx, a3, &s2.len);
 | |
| 				s2_free = 1;
 | |
| 
 | |
| 				/* the second argument - substituttion */
 | |
| 				s1.ptr = hawk_rtx_getvaloocstr(rtx, a1, &s1.len);
 | |
| 				s1_free = 1;
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (HAWK_UNLIKELY(!s1.ptr || !s2.ptr)) goto oops;
 | |
| 
 | |
| 	if (a0_vtype != HAWK_VAL_REX)
 | |
| 	{
 | |
| 		int x;
 | |
| 
 | |
| 		x = rtx->gbl.ignorecase?
 | |
| 			hawk_rtx_buildrex(rtx, s0.ptr, s0.len, HAWK_NULL, &rex):
 | |
| 			hawk_rtx_buildrex(rtx, s0.ptr, s0.len, &rex, HAWK_NULL);
 | |
| 		if (HAWK_UNLIKELY(x <= -1)) goto oops;
 | |
| 
 | |
| 		rex_free = rex;
 | |
| 	}
 | |
| 
 | |
| 	sub_count = max_count;
 | |
| 	if (s2_free == 2)
 | |
| 	{
 | |
| 		hawk_becs_clear(&rtx->fnc.bout);
 | |
| 		if (__substitute_bcs(rtx, &sub_count, rex, (hawk_bcs_t*)&s1, (hawk_bcs_t*)&s2, &rtx->fnc.bout, 1, op_pos) <= -1) goto oops;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		hawk_ooecs_clear(&rtx->fnc.oout);
 | |
| 		if (__substitute_oocs(rtx, &sub_count, rex, (hawk_oocs_t*)&s1, (hawk_oocs_t*)&s2, &rtx->fnc.oout, 1, op_pos) <= -1) goto oops;
 | |
| 	}
 | |
| 
 | |
| 	if (rex_free)
 | |
| 	{
 | |
| 		if (rtx->gbl.ignorecase)
 | |
| 			hawk_rtx_freerex(rtx, HAWK_NULL, rex_free);
 | |
| 		else
 | |
| 			hawk_rtx_freerex(rtx, rex_free, HAWK_NULL);
 | |
| 		rex_free = HAWK_NULL;
 | |
| 	}
 | |
| 
 | |
| 	switch (s2_free)
 | |
| 	{
 | |
| 		case 1:
 | |
| 			hawk_rtx_freevaloocstr(rtx, a3, s2.ptr);
 | |
| 			break;
 | |
| 		case 2:
 | |
| 			hawk_rtx_freevalbcstr(rtx, a3, s2.ptr);
 | |
| 			break;
 | |
| 	}
 | |
| 	s2.ptr = HAWK_NULL;
 | |
| 
 | |
| 	switch (s1_free)
 | |
| 	{
 | |
| 		case 1:
 | |
| 			hawk_rtx_freevaloocstr(rtx, a1, s1.ptr);
 | |
| 			break;
 | |
| 		case 2:
 | |
| 			hawk_rtx_freevalbcstr(rtx, a1, s1.ptr);
 | |
| 			break;
 | |
| 	}
 | |
| 	s1.ptr = HAWK_NULL;
 | |
| 
 | |
| 	if (s0.ptr)
 | |
| 	{
 | |
| 		hawk_rtx_freevaloocstr(rtx, a0, s0.ptr);
 | |
| 		s0.ptr = HAWK_NULL;
 | |
| 	}
 | |
| 
 | |
| 	v = (s2_free == 2)?
 | |
| 		hawk_rtx_makembsvalwithbcs(rtx, HAWK_BECS_BCS(&rtx->fnc.bout)):
 | |
| 		hawk_rtx_makestrvalwithoocs(rtx, HAWK_OOECS_OOCS(&rtx->fnc.oout));
 | |
| 	if (HAWK_UNLIKELY(!v)) goto oops;
 | |
| 
 | |
| 	hawk_rtx_setretval(rtx, v);
 | |
| 	return 0;
 | |
| 
 | |
| oops:
 | |
| 	if (rex_free)
 | |
| 	{
 | |
| 		if (rtx->gbl.ignorecase)
 | |
| 			hawk_rtx_freerex(rtx, HAWK_NULL, rex_free);
 | |
| 		else
 | |
| 			hawk_rtx_freerex(rtx, rex_free, HAWK_NULL);
 | |
| 	}
 | |
| 
 | |
| 	if (s2.ptr)
 | |
| 	{
 | |
| 		switch (s2_free)
 | |
| 		{
 | |
| 			case 1:
 | |
| 				hawk_rtx_freevaloocstr(rtx, a3, s2.ptr);
 | |
| 				break;
 | |
| 			case 2:
 | |
| 				hawk_rtx_freevalbcstr(rtx, a3, s2.ptr);
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (s1.ptr)
 | |
| 	{
 | |
| 		switch (s1_free)
 | |
| 		{
 | |
| 			case 1:
 | |
| 				hawk_rtx_freevaloocstr(rtx, a1, s1.ptr);
 | |
| 				break;
 | |
| 			case 2:
 | |
| 				hawk_rtx_freevalbcstr(rtx, a1, s1.ptr);
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (s0.ptr) hawk_rtx_freevaloocstr(rtx, a0, s0.ptr);
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static int __fnc_match (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi, int support_start_index)
 | |
| {
 | |
| 	hawk_oow_t nargs;
 | |
| 	hawk_val_t* a0, * a1;
 | |
| 	hawk_val_type_t a0_type;
 | |
| 
 | |
| 	union
 | |
| 	{
 | |
| 		hawk_ooch_t* o;
 | |
| 		hawk_bch_t* b;
 | |
| 	} str0;
 | |
| 	hawk_oow_t len0;
 | |
| 	hawk_int_t idx, start = 1;
 | |
| 	hawk_val_t* x0 = HAWK_NULL, * x1 = HAWK_NULL, * x2 = HAWK_NULL;
 | |
| 
 | |
| 	union
 | |
| 	{
 | |
| 		hawk_oocs_t o;
 | |
| 		hawk_bcs_t b;
 | |
| 	} mat;
 | |
| 
 | |
| 	union
 | |
| 	{
 | |
| 		hawk_oocs_t o[9];
 | |
| 		hawk_bcs_t b[9];
 | |
| 	} submat;
 | |
| 
 | |
| 	int n;
 | |
| 
 | |
| 	nargs = hawk_rtx_getnargs(rtx);
 | |
| 	HAWK_ASSERT (nargs >= 2 && nargs <= 4);
 | |
| 
 | |
| 	a0 = hawk_rtx_getarg(rtx, 0);
 | |
| 	a1 = hawk_rtx_getarg(rtx, 1);
 | |
| 
 | |
| 	if (support_start_index && nargs >= 3)
 | |
| 	{
 | |
| 		hawk_val_t* a2;
 | |
| 		a2 = hawk_rtx_getarg(rtx, 2); /* when start index is support, this is the thrid argument */
 | |
| 		if (hawk_rtx_valtoint(rtx, a2, &start) <= -1) return -1;
 | |
| 	}
 | |
| 
 | |
| 	HAWK_MEMSET (&submat, 0, HAWK_SIZEOF(submat));
 | |
| 
 | |
| 	a0_type = HAWK_RTX_GETVALTYPE(rtx, a0);
 | |
| 	if (a0_type == HAWK_VAL_MBS)
 | |
| 	{
 | |
| 		hawk_bcs_t tmp;
 | |
| 
 | |
| 		str0.b = hawk_rtx_getvalbcstr(rtx, a0, &len0);
 | |
| 		if (HAWK_UNLIKELY(!str0.b)) return -1;
 | |
| 
 | |
| 		if (start == 0) start = 1;
 | |
| 		else if (start < 0) start = len0 + start + 1;
 | |
| 		if (start > len0 || start <= 0) n = 0;
 | |
| 
 | |
| 		tmp.ptr = str0.b + start - 1;
 | |
| 		tmp.len = len0 - start + 1;
 | |
| 
 | |
| 		n = hawk_rtx_matchvalwithbcs(rtx, a1, &tmp, &tmp, &mat.b, (nargs >= support_start_index + 3? submat.b: HAWK_NULL));
 | |
| 		hawk_rtx_freevalbcstr(rtx, a0, str0.b);
 | |
| 
 | |
| 		if (n <= -1) return -1;
 | |
| 
 | |
| 		/* RSTART: 0 on no match */
 | |
| 		idx = (n == 0)? 0: ((hawk_int_t)(mat.b.ptr - str0.b) + 1);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		hawk_oocs_t tmp;
 | |
| 
 | |
| 		str0.o = hawk_rtx_getvaloocstr(rtx, a0, &len0);
 | |
| 		if (HAWK_UNLIKELY(!str0.o)) return -1;
 | |
| 
 | |
| 		if (start == 0) start = 1;
 | |
| 		else if (start < 0) start = len0 + start + 1;
 | |
| 		if (start > len0 || start <= 0) n = 0;
 | |
| 
 | |
| 		tmp.ptr = str0.o + start - 1;
 | |
| 		tmp.len = len0 - start + 1;
 | |
| 
 | |
| 		n = hawk_rtx_matchvalwithoocs(rtx, a1, &tmp, &tmp, &mat.o, (nargs >= support_start_index + 3? submat.o: HAWK_NULL));
 | |
| 		hawk_rtx_freevaloocstr(rtx, a0, str0.o);
 | |
| 
 | |
| 		if (n <= -1) return -1;
 | |
| 
 | |
| 		/* RSTART: 0 on no match */
 | |
| 		idx = (n == 0)? 0: ((hawk_int_t)(mat.o.ptr - str0.o) + 1);
 | |
| 	}
 | |
| 
 | |
| 	x0 = hawk_rtx_makeintval(rtx, idx);
 | |
| 	if (!x0) goto oops;
 | |
| 	hawk_rtx_refupval(rtx, x0);
 | |
| 
 | |
| 	/* RLENGTH: -1 on no match */
 | |
| 	HAWK_ASSERT (&mat.o.len == &mat.b.len);
 | |
| 	x1 = hawk_rtx_makeintval(rtx, ((n == 0)? (hawk_int_t)-1: (hawk_int_t)mat.o.len)); /* just use mat.o.len regardless of a0_type */
 | |
| 	if (!x1) goto oops;
 | |
| 	hawk_rtx_refupval(rtx, x1);
 | |
| 
 | |
| 	if (nargs >= (3 + support_start_index))
 | |
| 	{
 | |
| 		const hawk_oocs_t* subsep;
 | |
| 		hawk_int_t submatcount;
 | |
| 		hawk_oow_t i, xlen;
 | |
| 		hawk_val_t* tv;
 | |
| 
 | |
| 		hawk_ooecs_clear (&rtx->fnc.oout);
 | |
| 		subsep = hawk_rtx_getsubsep(rtx);
 | |
| 
 | |
| 		x2 = hawk_rtx_makemapval(rtx);
 | |
| 		if (HAWK_UNLIKELY(!x2)) goto oops;
 | |
| 		hawk_rtx_refupval(rtx, x2);
 | |
| 
 | |
| 		/* add the full match value to the array at index "0" */
 | |
| 		if (hawk_ooecs_fmt(&rtx->fnc.oout, HAWK_T("%d"), 0) == (hawk_oow_t)-1) goto oops;
 | |
| 		tv = (a0_type == HAWK_VAL_MBS)?
 | |
| 			hawk_rtx_makembsvalwithbchars(rtx, mat.b.ptr, mat.b.len):
 | |
| 			hawk_rtx_makestrvalwithoochars(rtx, mat.o.ptr, mat.o.len);
 | |
| 		if (HAWK_UNLIKELY(!tv)) goto oops;
 | |
| 		if (!hawk_rtx_setmapvalfld(rtx, x2, HAWK_OOECS_PTR(&rtx->fnc.oout), HAWK_OOECS_LEN(&rtx->fnc.oout), tv))
 | |
| 		{
 | |
| 			hawk_rtx_refupval(rtx, tv);
 | |
| 			hawk_rtx_refdownval(rtx, tv);
 | |
| 			goto oops;
 | |
| 		}
 | |
| 
 | |
| 		/* 0,start */
 | |
| 		if (hawk_ooecs_ncat(&rtx->fnc.oout, subsep->ptr, subsep->len) == (hawk_oow_t)-1) goto oops;
 | |
| 		xlen = HAWK_OOECS_LEN(&rtx->fnc.oout);
 | |
| 		if (hawk_ooecs_ncat(&rtx->fnc.oout, HAWK_T("start"), 5) == (hawk_oow_t)-1) goto oops;
 | |
| 
 | |
| 		tv = hawk_rtx_makeintval(rtx, ((a0_type == HAWK_VAL_MBS)? mat.b.ptr - str0.b + 1: mat.o.ptr - str0.o + 1));
 | |
| 		if (HAWK_UNLIKELY(!tv)) goto oops;
 | |
| 		if (!hawk_rtx_setmapvalfld(rtx, x2, HAWK_OOECS_PTR(&rtx->fnc.oout), HAWK_OOECS_LEN(&rtx->fnc.oout), tv))
 | |
| 		{
 | |
| 			hawk_rtx_refupval(rtx, tv);
 | |
| 			hawk_rtx_refdownval(rtx, tv);
 | |
| 			goto oops;
 | |
| 		}
 | |
| 
 | |
| 		/* 0,length */
 | |
| 		if (hawk_ooecs_setlen(&rtx->fnc.oout, xlen) == (hawk_oow_t)-1 ||
 | |
| 		    hawk_ooecs_ncat(&rtx->fnc.oout, HAWK_T("length"), 6) == (hawk_oow_t)-1) goto oops;
 | |
| 
 | |
| 		tv = hawk_rtx_makeintval(rtx, ((a0_type == HAWK_VAL_MBS)? mat.b.len: mat.o.len));
 | |
| 		if (HAWK_UNLIKELY(!tv)) goto oops;
 | |
| 		if (!hawk_rtx_setmapvalfld(rtx, x2, HAWK_OOECS_PTR(&rtx->fnc.oout), HAWK_OOECS_LEN(&rtx->fnc.oout), tv))
 | |
| 		{
 | |
| 			hawk_rtx_refupval(rtx, tv);
 | |
| 			hawk_rtx_refdownval(rtx, tv);
 | |
| 			goto oops;
 | |
| 		}
 | |
| 
 | |
| 		submatcount = 0;
 | |
| 		for (i = 0; i < HAWK_COUNTOF(submat.o); i++)
 | |
| 		{
 | |
| 			HAWK_ASSERT ((void*)&submat.o[i] == (void*)&submat.b[i]);
 | |
| 			if (!submat.o[i].ptr) break;
 | |
| 
 | |
| 			submatcount++;
 | |
| 
 | |
| 			if (hawk_ooecs_fmt(&rtx->fnc.oout, HAWK_T("%d"), (int)submatcount) == (hawk_oow_t)-1) goto oops;
 | |
| 
 | |
| 			tv = (a0_type == HAWK_VAL_MBS)?
 | |
| 				hawk_rtx_makembsvalwithbchars(rtx, submat.b[i].ptr, submat.b[i].len):
 | |
| 				hawk_rtx_makestrvalwithoochars(rtx, submat.o[i].ptr, submat.o[i].len);
 | |
| 			if (HAWK_UNLIKELY(!tv)) goto oops;
 | |
| 			if (!hawk_rtx_setmapvalfld(rtx, x2, HAWK_OOECS_PTR(&rtx->fnc.oout), HAWK_OOECS_LEN(&rtx->fnc.oout), tv))
 | |
| 			{
 | |
| 				hawk_rtx_refupval(rtx, tv);
 | |
| 				hawk_rtx_refdownval(rtx, tv);
 | |
| 				goto oops;
 | |
| 			}
 | |
| 
 | |
| 			if (hawk_ooecs_ncat(&rtx->fnc.oout, subsep->ptr, subsep->len) == (hawk_oow_t)-1) goto oops;
 | |
| 			xlen = HAWK_OOECS_LEN(&rtx->fnc.oout);
 | |
| 			if (hawk_ooecs_ncat(&rtx->fnc.oout, HAWK_T("start"), 5) == (hawk_oow_t)-1) goto oops;
 | |
| 
 | |
| 			tv = hawk_rtx_makeintval(rtx, ((a0_type == HAWK_VAL_MBS)? submat.b[i].ptr - str0.b + 1: submat.o[i].ptr - str0.o + 1));
 | |
| 			if (HAWK_UNLIKELY(!tv)) goto oops;
 | |
| 			if (!hawk_rtx_setmapvalfld(rtx, x2, HAWK_OOECS_PTR(&rtx->fnc.oout), HAWK_OOECS_LEN(&rtx->fnc.oout), tv))
 | |
| 			{
 | |
| 				hawk_rtx_refupval(rtx, tv);
 | |
| 				hawk_rtx_refdownval(rtx, tv);
 | |
| 				goto oops;
 | |
| 			}
 | |
| 
 | |
| 			if (hawk_ooecs_setlen(&rtx->fnc.oout, xlen) == (hawk_oow_t)-1 ||
 | |
| 			    hawk_ooecs_ncat(&rtx->fnc.oout, HAWK_T("length"), 6) == (hawk_oow_t)-1) goto oops;
 | |
| 
 | |
| 			tv = hawk_rtx_makeintval(rtx, ((a0_type == HAWK_VAL_MBS)? submat.b[i].len: submat.o[i].len));
 | |
| 			if (HAWK_UNLIKELY(!tv)) goto oops;
 | |
| 			if (!hawk_rtx_setmapvalfld(rtx, x2, HAWK_OOECS_PTR(&rtx->fnc.oout), HAWK_OOECS_LEN(&rtx->fnc.oout), tv))
 | |
| 			{
 | |
| 				hawk_rtx_refupval(rtx, tv);
 | |
| 				hawk_rtx_refdownval(rtx, tv);
 | |
| 				goto oops;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/* the caller of this function must be able to get the submatch count by
 | |
| 		 * dividing the array size by 2 */
 | |
| 		if (hawk_rtx_setrefval(rtx, (hawk_val_ref_t*)hawk_rtx_getarg(rtx, 2 + support_start_index), x2) <= -1) goto oops;
 | |
| 	}
 | |
| 
 | |
| 	if (hawk_rtx_setgbl(rtx, HAWK_GBL_RSTART, x0) <= -1 ||
 | |
| 	    hawk_rtx_setgbl(rtx, HAWK_GBL_RLENGTH, x1) <= -1)
 | |
| 	{
 | |
| 		goto oops;
 | |
| 	}
 | |
| 
 | |
| 	hawk_rtx_setretval(rtx, x0);
 | |
| 
 | |
| 	if (x2) hawk_rtx_refdownval(rtx, x2);
 | |
| 	hawk_rtx_refdownval(rtx, x1);
 | |
| 	hawk_rtx_refdownval(rtx, x0);
 | |
| 	return 0;
 | |
| 
 | |
| oops:
 | |
| 	if (x2) hawk_rtx_refdownval(rtx, x2);
 | |
| 	if (x1) hawk_rtx_refdownval(rtx, x1);
 | |
| 	if (x0) hawk_rtx_refdownval(rtx, x0);
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static int fnc_match (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	/* this variant supports three parameters only.
 | |
| 	 * the start index is not supported.
 | |
| 	 * you must use str::match() for it instead. */
 | |
| 	return __fnc_match(rtx, fi, 0);
 | |
| }
 | |
| 
 | |
| int hawk_fnc_match (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	/*
 | |
| 	str::match(@b"abcdefg", "cde");
 | |
| 	str::match("abcdefg", "cde");
 | |
| 	str::match("abcdefgdefx", "def", 7);
 | |
| 	------------------------------------
 | |
| 	str::match("ab\uB098cdefgdefx", /(def)g(.+)/, 1, x);
 | |
| 	q = length(x) / 3;
 | |
| 	for (i = 1; i < q; i++) print x[i,"start"], x[i,"length"];
 | |
| 	print RSTART, RLENGTH;
 | |
| 	* ------------------------------------
 | |
| 	str::match(@b"ab\xB0\x98cdefgdefx", /(def)g(.+)/, 1, x);
 | |
| 	q = length(x) / 3;
 | |
| 	for (i = 1; i < q; i++) print x[i,"start"], x[i,"length"];
 | |
| 	print RSTART, RLENGTH;
 | |
| 	*/
 | |
| 	return __fnc_match(rtx, fi, 1);
 | |
| }
 | |
| 
 | |
| int hawk_fnc_sprintf (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	hawk_oow_t nargs;
 | |
| 	hawk_val_t* a0;
 | |
| 
 | |
| 	nargs = hawk_rtx_getnargs(rtx);
 | |
| 	HAWK_ASSERT (nargs > 0);
 | |
| 
 | |
| 	a0 = hawk_rtx_getarg(rtx, 0);
 | |
| 	switch (HAWK_RTX_GETVALTYPE(rtx, a0))
 | |
| 	{
 | |
| 		case HAWK_VAL_BCHR:
 | |
| 		case HAWK_VAL_MBS:
 | |
| 		case HAWK_VAL_BOB:
 | |
| 		{
 | |
| 			hawk_becs_t fbu;
 | |
| 			int fbu_inited = 0;
 | |
| 			hawk_bcs_t cs0;
 | |
| 			hawk_bcs_t x;
 | |
| 
 | |
| 			if (hawk_becs_init(&fbu, hawk_rtx_getgem(rtx), 256) <= -1) goto oops_mbs;
 | |
| 			fbu_inited = 1;
 | |
| 
 | |
| 			cs0.ptr = hawk_rtx_getvalbcstr(rtx, a0, &cs0.len);
 | |
| 			if (HAWK_UNLIKELY(!cs0.ptr)) goto oops_mbs;
 | |
| 
 | |
| 			x.ptr = hawk_rtx_formatmbs(rtx, &rtx->fnc.bout, &fbu, cs0.ptr, cs0.len, nargs, HAWK_NULL, &x.len);
 | |
| 			hawk_rtx_freevalbcstr(rtx, a0, cs0.ptr);
 | |
| 			if (HAWK_UNLIKELY(!x.ptr)) goto oops_mbs;
 | |
| 
 | |
| 			a0 = hawk_rtx_makembsvalwithbcs(rtx, &x);
 | |
| 			if (HAWK_UNLIKELY(!a0)) goto oops_mbs;
 | |
| 
 | |
| 			hawk_becs_fini (&fbu);
 | |
| 			hawk_rtx_setretval(rtx, a0);
 | |
| 			return 0;
 | |
| 
 | |
| 		oops_mbs:
 | |
| 			if (fbu_inited) hawk_becs_fini (&fbu);
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		default:
 | |
| 		{
 | |
| 			hawk_ooecs_t fbu;
 | |
| 			int fbu_inited = 0;
 | |
| 			hawk_oocs_t cs0;
 | |
| 			hawk_oocs_t x;
 | |
| 
 | |
| 			if (hawk_ooecs_init(&fbu, hawk_rtx_getgem(rtx), 256) <= -1) goto oops;
 | |
| 			fbu_inited = 1;
 | |
| 
 | |
| 			cs0.ptr = hawk_rtx_getvaloocstr(rtx, a0, &cs0.len);
 | |
| 			if (HAWK_UNLIKELY(!cs0.ptr)) goto oops;
 | |
| 
 | |
| 			x.ptr = hawk_rtx_format(rtx, &rtx->fnc.oout, &fbu, cs0.ptr, cs0.len, nargs, HAWK_NULL, &x.len);
 | |
| 			hawk_rtx_freevaloocstr(rtx, a0, cs0.ptr);
 | |
| 			if (HAWK_UNLIKELY(!x.ptr)) goto oops;
 | |
| 
 | |
| 			a0 = hawk_rtx_makestrvalwithoocs(rtx, &x);
 | |
| 			if (HAWK_UNLIKELY(!a0)) goto oops;
 | |
| 
 | |
| 			hawk_ooecs_fini (&fbu);
 | |
| 			hawk_rtx_setretval(rtx, a0);
 | |
| 			return 0;
 | |
| 
 | |
| 		oops:
 | |
| 			if (fbu_inited) hawk_ooecs_fini (&fbu);
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int fnc_int (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	hawk_val_t* a0;
 | |
| 	hawk_int_t lv;
 | |
| 	hawk_val_t* r;
 | |
| 	int n;
 | |
| 
 | |
| 	a0 = hawk_rtx_getarg(rtx, 0);
 | |
| 
 | |
| 	n = hawk_rtx_valtoint(rtx, a0, &lv);
 | |
| 	if (n <= -1) return -1;
 | |
| 
 | |
| 	r = hawk_rtx_makeintval(rtx, lv);
 | |
| 	if (!r) return -1;
 | |
| 
 | |
| 	hawk_rtx_setretval(rtx, r);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static HAWK_INLINE int asort_compare (const void* x1, const void* x2, void* ctx, int* cv)
 | |
| {
 | |
| 	int n;
 | |
| 	if (hawk_rtx_cmpval((hawk_rtx_t*)ctx, *(hawk_val_t**)x1, *(hawk_val_t**)x2, &n) <= -1) return -1;
 | |
| 	*cv = n;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| struct cud_t
 | |
| {
 | |
| 	hawk_rtx_t* rtx;
 | |
| 	hawk_fun_t* fun;
 | |
| };
 | |
| 
 | |
| static HAWK_INLINE int asort_compare_ud (const void* x1, const void* x2, void* ctx, int* cv)
 | |
| {
 | |
| 	struct cud_t* cud = (struct cud_t*)ctx;
 | |
| 	hawk_val_t* r, * args[2];
 | |
| 	hawk_int_t rv;
 | |
| 
 | |
| 	args[0] = *(hawk_val_t**)x1;
 | |
| 	args[1] = *(hawk_val_t**)x2;
 | |
| 	r = hawk_rtx_callfun(cud->rtx, cud->fun, args, 2);
 | |
| 	if (HAWK_UNLIKELY(!r)) return -1;
 | |
| 	if (hawk_rtx_valtoint(cud->rtx, r,  &rv) <= -1) return -1;
 | |
| 	*cv = rv;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static HAWK_INLINE int __fnc_asort (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi, int sort_keys)
 | |
| {
 | |
| 	/*
 | |
| 	 a["tt"]=20; a[2]=10; a[3]=99;
 | |
| 	 asort(a);
 | |
| 	 for (i in a) print i, a[i];
 | |
| 	 ---------------------------------------------
 | |
| 	 a["tt"]=20; a[2]=10; a[3]=99;
 | |
| 	 asort(a, b);
 | |
| 	 for (i in b) print i, b[i];
 | |
| 	 ----------------------------------------------
 | |
| 	 function cmp(a, b) { return b - a;  }
 | |
| 	 BEGIN {
 | |
| 	   a["tt"]=20; a[2]=10; a[3]=99; a[4]=66;
 | |
| 	   asort(a, b, cmp);
 | |
| 	   for (i in b) print i, b[i];
 | |
| 	 }
 | |
| 	 */
 | |
| 
 | |
| 	hawk_oow_t nargs;
 | |
| 	hawk_val_t* a0, * a0_val;
 | |
| 	hawk_val_type_t a0_type, v_type;
 | |
| 	hawk_val_t* r, * rrv = HAWK_NULL;
 | |
| 	hawk_int_t rv = 0; /* as if no element in the map */
 | |
| 	hawk_fun_t* fun = HAWK_NULL;
 | |
| 
 | |
| 	nargs = hawk_rtx_getnargs(rtx);
 | |
| 
 | |
| 	a0 = hawk_rtx_getarg(rtx, 0);
 | |
| 	a0_type = HAWK_RTX_GETVALTYPE(rtx, a0);
 | |
| 	HAWK_ASSERT (a0_type == HAWK_VAL_REF);
 | |
| 
 | |
| 	if (nargs >= 3)
 | |
| 	{
 | |
| 		fun = hawk_rtx_valtofun(rtx, hawk_rtx_getarg(rtx, 2));
 | |
| 		if (HAWK_UNLIKELY(!fun))
 | |
| 		{
 | |
| 			if (hawk_rtx_geterrnum(rtx) == HAWK_EINVAL)
 | |
| 				hawk_rtx_seterrfmt(rtx, HAWK_NULL, HAWK_EINVAL, HAWK_T("comparator not a function"));
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		if (fun->nargs < 2)
 | |
| 		{
 | |
| 			/* the comparison accepts less than 2 arguments */
 | |
| 			hawk_rtx_seterrfmt(rtx, HAWK_NULL, HAWK_EINVAL, HAWK_T("%.*js not accepting 2 arguments"), fun->name.len, fun->name.ptr);
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	v_type = hawk_rtx_getrefvaltype(rtx, (hawk_val_ref_t*)a0);
 | |
| 	switch (v_type)
 | |
| 	{
 | |
| 		case HAWK_VAL_NIL:
 | |
| 			goto done; /* treat it as an empty value */
 | |
| 
 | |
| 		case HAWK_VAL_MAP:
 | |
| 			goto val_map;
 | |
| 
 | |
| 		case HAWK_VAL_ARR:
 | |
| 			goto val_arr;
 | |
| 
 | |
| 		default:
 | |
| 			hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_ENOTIDXACC);
 | |
| 			return -1;
 | |
| 	}
 | |
| 
 | |
| val_map:
 | |
| 	{
 | |
| 		hawk_val_map_itr_t itr;
 | |
| 		hawk_oow_t i, msz;
 | |
| 		hawk_val_t** va;
 | |
| 		int x;
 | |
| 
 | |
| 		a0_val = hawk_rtx_getrefval(rtx, (hawk_val_ref_t*)a0);
 | |
| 		HAWK_ASSERT (HAWK_RTX_GETVALTYPE(rtx, a0_val) == HAWK_VAL_MAP);
 | |
| 
 | |
| 		if (!hawk_rtx_getfirstmapvalitr(rtx, a0_val, &itr)) goto done; /* map empty */
 | |
| 
 | |
| 		msz = hawk_map_getsize(((hawk_val_map_t*)a0_val)->map);
 | |
| 		HAWK_ASSERT (msz > 0);
 | |
| 
 | |
| 		va = (hawk_val_t**)hawk_rtx_allocmem(rtx, msz * HAWK_SIZEOF(*va));
 | |
| 		if (HAWK_UNLIKELY(!va)) return -1;
 | |
| 		i = 0;
 | |
| 		if (sort_keys)
 | |
| 		{
 | |
| 			do
 | |
| 			{
 | |
| 				const hawk_oocs_t* key = HAWK_VAL_MAP_ITR_KEY(&itr);
 | |
| 				va[i] = hawk_rtx_makestrvalwithoocs(rtx, key);
 | |
| 				if (HAWK_UNLIKELY(!va[i]))
 | |
| 				{
 | |
| 					while (i > 0)
 | |
| 					{
 | |
| 						--i;
 | |
| 						hawk_rtx_refdownval(rtx, va[i]);
 | |
| 					}
 | |
| 					hawk_rtx_freemem(rtx, va);
 | |
| 					return -1;
 | |
| 				}
 | |
| 				hawk_rtx_refupval(rtx, va[i]);
 | |
| 				i++;
 | |
| 			}
 | |
| 			while (hawk_rtx_getnextmapvalitr(rtx, a0_val, &itr));
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			do
 | |
| 			{
 | |
| 				va[i] = (hawk_val_t*)HAWK_VAL_MAP_ITR_VAL(&itr);
 | |
| 				hawk_rtx_refupval(rtx, va[i]);
 | |
| 				i++;
 | |
| 			}
 | |
| 			while (hawk_rtx_getnextmapvalitr(rtx, a0_val, &itr));
 | |
| 		}
 | |
| 
 | |
| 		if (fun)
 | |
| 		{
 | |
| 			struct cud_t cud;
 | |
| 			cud.rtx = rtx;
 | |
| 			cud.fun = fun;
 | |
| 			x = hawk_qsortx(va, msz, HAWK_SIZEOF(*va), asort_compare_ud, &cud);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			x = hawk_qsortx(va, msz, HAWK_SIZEOF(*va), asort_compare, rtx);
 | |
| 		}
 | |
| 
 | |
| 		if (x <= -1 || !(rrv = hawk_rtx_makemapval(rtx)))
 | |
| 		{
 | |
| 			for (i = 0; i < msz; i++) hawk_rtx_refdownval(rtx, va[i]);
 | |
| 			hawk_rtx_freemem(rtx, va);
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		for (i = 0; i < msz; i++)
 | |
| 		{
 | |
| 			hawk_ooch_t ridx[128]; /* TODO: make it dynamic? can overflow? */
 | |
| 			hawk_oow_t ridx_len;
 | |
| 
 | |
| 			ridx_len = hawk_fmt_uintmax_to_oocstr(
 | |
| 				ridx,
 | |
| 				HAWK_COUNTOF(ridx),
 | |
| 				i + 1, /* basically 1-based */
 | |
| 				10 | HAWK_FMT_UINTMAX_NOTRUNC | HAWK_FMT_UINTMAX_NONULL,
 | |
| 				-1,
 | |
| 				HAWK_T('\0'),
 | |
| 				HAWK_NULL
 | |
| 			);
 | |
| 
 | |
| 			if (!hawk_rtx_setmapvalfld(rtx, rrv, ridx, ridx_len, va[i]))
 | |
| 			{
 | |
| 				/* decrement the reference count of the values not added to the map */
 | |
| 				do
 | |
| 				{
 | |
| 					hawk_rtx_refdownval(rtx, va[i]);
 | |
| 					i++;
 | |
| 				}
 | |
| 				while (i < msz);
 | |
| 				hawk_rtx_freeval(rtx, rrv, 0); /* this derefs the elements added. */
 | |
| 				hawk_rtx_freemem(rtx, va);
 | |
| 				return -1;
 | |
| 			}
 | |
| 
 | |
| 			hawk_rtx_refdownval(rtx, va[i]); /* deref it as it has been added to the map */
 | |
| 		}
 | |
| 
 | |
| 		rv = msz;
 | |
| 		hawk_rtx_freemem(rtx, va);
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| val_arr:
 | |
| 	{
 | |
| 		hawk_arr_t* arr;
 | |
| 		hawk_oow_t i, j, msz, ssz;
 | |
| 		hawk_val_t** va;
 | |
| 		int x;
 | |
| 
 | |
| 		a0_val = hawk_rtx_getrefval(rtx, (hawk_val_ref_t*)a0);
 | |
| 		HAWK_ASSERT (HAWK_RTX_GETVALTYPE(rtx, a0_val) == HAWK_VAL_ARR);
 | |
| 		arr = ((hawk_val_arr_t*)a0_val)->arr;
 | |
| 		msz = HAWK_ARR_TALLY(arr);
 | |
| 		if (msz == 0) goto done; /* array empty */
 | |
| 
 | |
| 		ssz = HAWK_ARR_SIZE(arr);
 | |
| 		HAWK_ASSERT (msz <= ssz);
 | |
| 		HAWK_ASSERT (msz <= HAWK_INT_MAX);
 | |
| 		HAWK_ASSERT (ssz <= HAWK_INT_MAX);
 | |
| 
 | |
| 		va = (hawk_val_t**)hawk_rtx_allocmem(rtx, msz * HAWK_SIZEOF(*va));
 | |
| 		if (HAWK_UNLIKELY(!va)) return -1;
 | |
| 		for (i = 0, j = 0; j < ssz; j++)
 | |
| 		{
 | |
| 			if (HAWK_ARR_SLOT(arr, j))
 | |
| 			{
 | |
| 				va[i] = sort_keys? hawk_rtx_makeintval(rtx, j): HAWK_ARR_DPTR(arr, j);
 | |
| 				hawk_rtx_refupval(rtx, va[i]);
 | |
| 				i++;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (fun)
 | |
| 		{
 | |
| 			struct cud_t cud;
 | |
| 			cud.rtx = rtx;
 | |
| 			cud.fun = fun;
 | |
| 			x = hawk_qsortx(va, msz, HAWK_SIZEOF(*va), asort_compare_ud, &cud);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			x = hawk_qsortx(va, msz, HAWK_SIZEOF(*va), asort_compare, rtx);
 | |
| 		}
 | |
| 
 | |
| 		if (x <= -1 || !(rrv = hawk_rtx_makearrval(rtx, -1)))
 | |
| 		{
 | |
| 			for (i = 0; i < msz; i++) hawk_rtx_refdownval(rtx, va[i]);
 | |
| 			hawk_rtx_freemem(rtx, va);
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		for (i = 0; i < msz; i++)
 | |
| 		{
 | |
| 			if (!hawk_rtx_setarrvalfld(rtx, rrv, i + 1, va[i])) /* i + 1 for 1-based indexing*/
 | |
| 			{
 | |
| 				/* decrement the reference count of the values not added to the map */
 | |
| 				do
 | |
| 				{
 | |
| 					hawk_rtx_refdownval(rtx, va[i]);
 | |
| 					i++;
 | |
| 				}
 | |
| 				while (i < msz);
 | |
| 				hawk_rtx_freeval(rtx, rrv, 0); /* this derefs the elements added. */
 | |
| 				hawk_rtx_freemem(rtx, va);
 | |
| 				return -1;
 | |
| 			}
 | |
| 
 | |
| 			hawk_rtx_refdownval(rtx, va[i]); /* deref it as it has been added to the map */
 | |
| 		}
 | |
| 
 | |
| 		rv = msz;
 | |
| 		hawk_rtx_freemem(rtx, va);
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| done:
 | |
| 	r = hawk_rtx_makeintval(rtx, rv);
 | |
| 	if (HAWK_UNLIKELY(!r)) return -1;
 | |
| 
 | |
| 	if (rrv)
 | |
| 	{
 | |
| 		int x;
 | |
| 		/* rrv can be NULL when a jump has been made for an empty source
 | |
| 		 * at the beginning of this fucntion */
 | |
| 		hawk_rtx_refupval(rtx, rrv);
 | |
| 		x = hawk_rtx_setrefval(rtx, (hawk_val_ref_t*)hawk_rtx_getarg(rtx, (nargs >= 2)), rrv);
 | |
| 		hawk_rtx_refdownval(rtx, rrv);
 | |
| 		if (x <= -1)
 | |
| 		{
 | |
| 			hawk_rtx_freeval(rtx, r, 0);
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	hawk_rtx_setretval(rtx, r);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int fnc_asort (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	return __fnc_asort(rtx, fi, 0);
 | |
| }
 | |
| 
 | |
| static int fnc_asorti (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
 | |
| {
 | |
| 	return __fnc_asort(rtx, fi, 1);
 | |
| }
 |