hawk/hawk/lib/fnc.c

1796 lines
44 KiB
C

/*
* $Id$
*
Copyright (c) 2006-2019 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_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_typename (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi);
static int fnc_isnil (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi);
static int fnc_ismap (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi);
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);
#define A_MAX HAWK_TYPE_MAX(int)
/* 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},
{ {HAWK_T("isnil"), 5}, 0, { {1, 1, HAWK_NULL}, fnc_isnil, 0 }, HAWK_NULL},
{ {HAWK_T("ismap"), 5}, 0, { {1, 1, HAWK_NULL}, fnc_ismap, 0 }, HAWK_NULL},
{ {HAWK_T("typename"), 8}, 0, { {1, 1, HAWK_NULL}, fnc_typename, 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("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}, hawk_fnc_length, 0 }, HAWK_NULL},
{ {HAWK_T("match"), 5}, 0, { {2, 4, HAWK_T("vxvr")}, hawk_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) == HAWK_NULL)
{
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* awk, const hawk_bch_t* name, const hawk_fnc_mspec_t* spec)
{
#if defined(HAWK_OOCH_IS_BCH)
return add_fnc(awk, 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(awk, spec->arg.spec, &wcs.len, 0);
if (!wcs.ptr) return HAWK_NULL;
wspec.arg.spec = wcs.ptr;
}
wcs.ptr = hawk_dupbtoucstr(awk, name, &wcs.len, 0);
if (!wcs.ptr)
{
if (wspec.arg.spec) hawk_freemem (awk, (hawk_uch_t*)wspec.arg.spec);
return HAWK_NULL;
}
fnc = add_fnc(awk, wcs.ptr, &wspec);
hawk_freemem (awk, wcs.ptr);
if (wspec.arg.spec) hawk_freemem (awk, (hawk_uch_t*)wspec.arg.spec);
return fnc;
#endif
}
hawk_fnc_t* hawk_addfncwithucstr (hawk_t* awk, 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(awk, spec->arg.spec, &mbs.len);
if (!mbs.ptr) return HAWK_NULL;
mspec.arg.spec = mbs.ptr;
}
mbs.ptr = hawk_duputobcstr(awk, name, &mbs.len);
if (!mbs.ptr)
{
if (mspec.arg.spec) hawk_freemem (awk, (hawk_bch_t*)mspec.arg.spec);
return HAWK_NULL;
}
fnc = add_fnc(awk, mbs.ptr, &mspec);
hawk_freemem (awk, mbs.ptr);
if (mspec.arg.spec) hawk_freemem (awk, (hawk_bch_t*)mspec.arg.spec);
return fnc;
#else
return add_fnc(awk, 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* awk)
{
hawk_htb_clear (awk->fnc.user);
}
static hawk_fnc_t* find_fnc (hawk_t* awk, 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 ((awk->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(awk->fnc.user, name->ptr, name->len);
if (pair)
{
hawk_fnc_t* fnc;
fnc = (hawk_fnc_t*)HAWK_HTB_VPTR(pair);
if ((awk->opt.trait & fnc->spec.trait) == fnc->spec.trait) return fnc;
}
hawk_seterrfmt (awk, HAWK_NULL, HAWK_ENOENT, HAWK_T("no such function - %js"), name);
return HAWK_NULL;
}
hawk_fnc_t* hawk_findfncwithbcs (hawk_t* awk, const hawk_bcs_t* name)
{
#if defined(HAWK_OOCH_IS_BCH)
return find_fnc(awk, name);
#else
hawk_ucs_t wcs;
hawk_fnc_t* fnc;
wcs.ptr = hawk_dupbtouchars(awk, name->ptr, name->len, &wcs.len, 0);
if (!wcs.ptr) return HAWK_NULL;
fnc = find_fnc(awk, &wcs);
hawk_freemem (awk, wcs.ptr);
return fnc;
#endif
}
hawk_fnc_t* hawk_findfncwithucs (hawk_t* awk, const hawk_ucs_t* name)
{
#if defined(HAWK_OOCH_IS_BCH)
hawk_bcs_t mbs;
hawk_fnc_t* fnc;
mbs.ptr = hawk_duputobchars(awk, name->ptr, name->len, &mbs.len);
if (!mbs.ptr) return HAWK_NULL;
fnc = find_fnc(awk, &mbs);
hawk_freemem (awk, mbs.ptr);
return fnc;
#else
return find_fnc(awk, 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 (name == HAWK_NULL) return -1;
if (a1)
{
opt = hawk_rtx_getvaloocstr(rtx, a1, &optlen);
if (opt == HAWK_NULL)
{
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)
{
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 == HAWK_NULL) return -1;
hawk_rtx_setretval (rtx, v);
return 0;
}
static int flush_io (hawk_rtx_t* rtx, int rio, const hawk_ooch_t* name, int n)
{
int n2;
if (rtx->rio.handler[rio] != HAWK_NULL)
{
n2 = hawk_rtx_flushio (rtx, rio, 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 == HAWK_NULL) 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 == HAWK_NULL) 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;
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;
}
if (HAWK_RTX_GETVALTYPE(rtx, a0) == HAWK_VAL_MBS)
{
hawk_bch_t* str0, * str1, * ptr;
hawk_oow_t len0, len1;
str0 = ((hawk_val_mbs_t*)a0)->val.ptr;
len0 = ((hawk_val_mbs_t*)a0)->val.len;
str1 = hawk_rtx_getvalbcstr(rtx, a1, &len1);
if (!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_NULL)? 0: ((hawk_int_t)(ptr - str0) + 1);
hawk_rtx_freevalbcstr (rtx, a1, str1);
}
else
{
hawk_ooch_t* str0, * str1, * ptr;
hawk_oow_t len0, len1;
str0 = hawk_rtx_getvaloocstr(rtx, a0, &len0);
if (!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_NULL)? 0: ((hawk_int_t)(ptr - str0) + 1);
hawk_rtx_freevaloocstr (rtx, a1, str1);
hawk_rtx_freevaloocstr (rtx, a0, str0);
}
a0 = hawk_rtx_makeintval(rtx, idx);
if (a0 == HAWK_NULL) 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)
{
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_HTB_SIZE(((hawk_val_map_t*)v)->map);
break;
case HAWK_VAL_STR:
/* string length */
len = ((hawk_val_str_t*)v)->val.len;
break;
case HAWK_VAL_MBS:
len = ((hawk_val_mbs_t*)v)->val.len;
break;
default:
/* convert to string and get length */
str = hawk_rtx_valtooocstrdup(rtx, v, &len);
if (!str) return -1;
hawk_rtx_freemem (rtx, str);
}
}
v = hawk_rtx_makeintval(rtx, len);
if (!v) return -1;
hawk_rtx_setretval (rtx, v);
return 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;
if (HAWK_RTX_GETVALTYPE(rtx, a0) == HAWK_VAL_MBS)
{
hawk_bch_t* str;
hawk_oow_t len;
str = ((hawk_val_mbs_t*)a0)->val.ptr;
len = ((hawk_val_mbs_t*)a0)->val.len;
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_makembsval(rtx, &str[lindex], (hawk_oow_t)lcount);
if (!r) return -1;
}
else
{
hawk_ooch_t* str;
hawk_oow_t len;
str = hawk_rtx_getvaloocstr(rtx, a0, &len);
if (!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 (!r) return -1;
}
hawk_rtx_setretval (rtx, r);
return 0;
}
int hawk_fnc_split (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
{
hawk_oow_t nargs;
hawk_val_t* a0, * a1, * a2, * t1, * t2;
hawk_val_type_t a1_vtype, a2_vtype, t1_vtype;
hawk_oocs_t str;
hawk_oocs_t fs;
hawk_ooch_t* fs_free = HAWK_NULL;
const hawk_ooch_t* p;
hawk_oow_t str_left, 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;
hawk_errnum_t errnum;
int x;
str.ptr = HAWK_NULL;
str.len = 0;
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;
a1_vtype = HAWK_RTX_GETVALTYPE (rtx, a1);
HAWK_ASSERT (a1_vtype == HAWK_VAL_REF);
str.ptr = hawk_rtx_getvaloocstr(rtx, a0, &str.len);
if (str.ptr == HAWK_NULL) goto oops;
if (a2 == HAWK_NULL)
{
/* get the value from FS */
t1 = hawk_rtx_getgbl(rtx, HAWK_GBL_FS);
t1_vtype = HAWK_RTX_GETVALTYPE(rtx, t1);
if (t1_vtype == HAWK_VAL_NIL)
{
fs.ptr = HAWK_T(" ");
fs.len = 1;
}
else if (t1_vtype == HAWK_VAL_STR)
{
fs.ptr = ((hawk_val_str_t*)t1)->val.ptr;
fs.len = ((hawk_val_str_t*)t1)->val.len;
}
else
{
fs.ptr = hawk_rtx_valtooocstrdup(rtx, t1, &fs.len);
if (fs.ptr == HAWK_NULL) goto oops;
fs_free = (hawk_ooch_t*)fs.ptr;
}
if (fs.len > 1) fs_rex = rtx->gbl.fs[rtx->gbl.ignorecase];
}
else
{
a2_vtype = HAWK_RTX_GETVALTYPE (rtx, a2);
if (a2_vtype == HAWK_VAL_REX)
{
/* the third parameter is a regular expression */
fs_rex = ((hawk_val_rex_t*)a2)->code[rtx->gbl.ignorecase];
/* make the loop below to take fs_rex by
* setting fs_len greater than 1*/
fs.ptr = HAWK_NULL;
fs.len = 2;
}
else
{
if (a2_vtype == HAWK_VAL_STR)
{
fs.ptr = ((hawk_val_str_t*)a2)->val.ptr;
fs.len = ((hawk_val_str_t*)a2)->val.len;
}
else
{
fs.ptr = hawk_rtx_valtooocstrdup(rtx, a2, &fs.len);
if (fs.ptr == HAWK_NULL) goto oops;
fs_free = (hawk_ooch_t*)fs.ptr;
}
if (fs.len > 1)
{
int x;
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;
}
}
}
t1 = hawk_rtx_makemapval(rtx);
if (t1 == HAWK_NULL) goto oops;
hawk_rtx_refupval (rtx, t1);
x = hawk_rtx_setrefval (rtx, (hawk_val_ref_t*)a1, t1);
hawk_rtx_refdownval (rtx, t1);
if (x <= -1) goto oops;
/* fill the map with actual values */
p = str.ptr; str_left = str.len; org_len = str.len;
nflds = 0;
while (p != HAWK_NULL)
{
hawk_ooch_t key_buf[HAWK_SIZEOF(hawk_int_t)*8+2];
hawk_oow_t key_len;
if (fs.len <= 1)
{
p = hawk_rtx_strxntok(rtx, p, str.len, fs.ptr, fs.len, &tok);
}
else
{
p = hawk_rtx_strxntokbyrex(rtx, str.ptr, org_len, p, str.len, fs_rex, &tok);
if (p == HAWK_NULL && hawk_rtx_geterrnum(rtx) != HAWK_ENOERR)
{
goto oops;
}
}
if (nflds == 0 && p == HAWK_NULL && tok.len == 0)
{
/* no field at all*/
break;
}
HAWK_ASSERT ((tok.ptr != HAWK_NULL && tok.len > 0) || tok.len == 0);
/* create the field string - however, the split function must
* create a numeric string if the string is a number */
/*t2 = hawk_rtx_makestrvalwithoocs (rtx, &tok);*/
t2 = hawk_rtx_makenstrvalwithoocs (rtx, &tok);
if (t2 == HAWK_NULL) goto oops;
/* put it into the map */
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_NULL)
{
hawk_rtx_refupval (rtx, t2);
hawk_rtx_refdownval (rtx, t2);
goto oops;
}
str.len = str_left - (p - str.ptr);
}
/*if (str_free) hawk_rtx_freemem (rtx, str_free);*/
hawk_rtx_freevaloocstr (rtx, a0, str.ptr);
if (fs_free) hawk_rtx_freemem (rtx, 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);
}
/*nflds--;*/
t1 = hawk_rtx_makeintval (rtx, nflds);
if (t1 == HAWK_NULL) return -1;
hawk_rtx_setretval (rtx, t1);
return 0;
oops:
/*if (str_free) hawk_rtx_freemem (rtx, str_free);*/
if (str.ptr) hawk_rtx_freevaloocstr (rtx, a0, str.ptr);
if (fs_free) hawk_rtx_freemem (rtx, 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_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);
if (HAWK_RTX_GETVALTYPE(rtx, a0) == HAWK_VAL_MBS)
{
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 (!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]);
}
else
{
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 (!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]);
}
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);
if (HAWK_RTX_GETVALTYPE(rtx, a0) == HAWK_VAL_MBS)
{
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 (!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]);
}
else
{
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 (!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]);
}
hawk_rtx_setretval (rtx, r);
return 0;
}
static int __substitute (hawk_rtx_t* rtx, hawk_int_t max_count)
{
hawk_oow_t nargs;
hawk_val_t* a0, * a1, * a2, * v;
hawk_val_type_t a0_vtype;
hawk_oocs_t s0, s2;
hawk_oocs_t s1;
const hawk_ooch_t* s2_end;
hawk_ooch_t* s0_free = HAWK_NULL;
hawk_ooch_t* s2_free = HAWK_NULL;
hawk_tre_t* rex = HAWK_NULL;
hawk_tre_t* rex_free = HAWK_NULL;
hawk_ooecs_t new;
int new_inited = 0;
hawk_oocs_t mat, pmat, cur;
hawk_int_t sub_count;
s1.ptr = HAWK_NULL;
s1.len = 0;
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;
a0_vtype = HAWK_RTX_GETVALTYPE (rtx, a0);
HAWK_ASSERT (a2 == HAWK_NULL || HAWK_RTX_GETVALTYPE(rtx, a2) == HAWK_VAL_REF);
if (a0_vtype == HAWK_VAL_REX)
{
rex = ((hawk_val_rex_t*)a0)->code[rtx->gbl.ignorecase];
}
else if (a0_vtype == HAWK_VAL_STR)
{
s0.ptr = ((hawk_val_str_t*)a0)->val.ptr;
s0.len = ((hawk_val_str_t*)a0)->val.len;
}
else
{
s0.ptr = hawk_rtx_valtooocstrdup(rtx, a0, &s0.len);
if (s0.ptr == HAWK_NULL) goto oops;
s0_free = (hawk_ooch_t*)s0.ptr;
}
s1.ptr = hawk_rtx_getvaloocstr(rtx, a1, &s1.len);
if (s1.ptr == HAWK_NULL) goto oops;
if (a2 == HAWK_NULL)
{
/* 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);
}
else
{
s2.ptr = hawk_rtx_valtooocstrdup(rtx, a2, &s2.len);
if (s2.ptr == HAWK_NULL) goto oops;
s2_free = (hawk_ooch_t*)s2.ptr;
}
if (hawk_ooecs_init(&new, hawk_rtx_getgem(rtx), s2.len) <= -1) goto oops;
new_inited = 1;
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 (x <= -1) goto oops;
rex_free = rex;
}
s2_end = s2.ptr + s2.len;
cur.ptr = s2.ptr;
cur.len = s2.len;
sub_count = 0;
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 (max_count == 0 || sub_count < max_count)
{
n = hawk_rtx_matchrex(rtx, rex, &s2, &cur, &mat, HAWK_NULL);
if (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;
for (i = 0; i < s1.len; i++)
{
if ((i+1) < s1.len && s1.ptr[i] == HAWK_T('\\') && s1.ptr[i+1] == HAWK_T('&'))
{
m = hawk_ooecs_ccat (&new, HAWK_T('&'));
i++;
}
else if (s1.ptr[i] == HAWK_T('&'))
{
m = hawk_ooecs_ncat (&new, mat.ptr, mat.len);
}
else
{
m = hawk_ooecs_ccat (&new, s1.ptr[i]);
}
if (m == (hawk_oow_t)-1) goto oops;
}
sub_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 */
m = hawk_ooecs_ncat(&new, cur.ptr, 1);
if (m == (hawk_oow_t)-1) goto oops;
cur.ptr++; cur.len--;
}
}
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;
}
if (sub_count > 0)
{
if (a2 == HAWK_NULL)
{
int n;
n = hawk_rtx_setrec(rtx, 0, HAWK_OOECS_OOCS(&new));
if (n <= -1) goto oops;
}
else
{
int n;
v = hawk_rtx_makestrvalwithoocs(rtx, HAWK_OOECS_OOCS(&new));
if (v == HAWK_NULL) goto oops;
hawk_rtx_refupval (rtx, v);
n = hawk_rtx_setrefval(rtx, (hawk_val_ref_t*)a2, v);
hawk_rtx_refdownval (rtx, v);
if (n <= -1) goto oops;
}
}
hawk_ooecs_fini (&new);
if (s2_free) hawk_rtx_freemem (rtx, s2_free);
hawk_rtx_freevaloocstr (rtx, a1, s1.ptr);
if (s0_free) hawk_rtx_freemem (rtx, s0_free);
v = hawk_rtx_makeintval (rtx, sub_count);
if (v == HAWK_NULL) 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 (new_inited) hawk_ooecs_fini (&new);
if (s2_free) hawk_rtx_freemem (rtx, s2_free);
if (s1.ptr) hawk_rtx_freevaloocstr (rtx, a1, s1.ptr);
if (s0_free) hawk_rtx_freemem (rtx, s0_free);
return -1;
}
int hawk_fnc_gsub (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
{
return __substitute(rtx, 0);
}
int hawk_fnc_sub (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
{
return __substitute(rtx, 1);
}
int hawk_fnc_match (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
{
hawk_oow_t nargs;
hawk_val_t* a0, * a1, * a3;
hawk_ooch_t* str0;
hawk_oow_t len0;
hawk_int_t idx, start = 1;
hawk_val_t* x0 = HAWK_NULL, * x1 = HAWK_NULL, * x2 = HAWK_NULL;
int n;
hawk_oocs_t mat, submat[9];
hawk_ooecs_t* tmpbuf = HAWK_NULL;
nargs = hawk_rtx_getnargs(rtx);
HAWK_ASSERT (nargs >= 2 && nargs <= 4);
a0 = hawk_rtx_getarg(rtx, 0);
a1 = hawk_rtx_getarg(rtx, 1);
if (nargs >= 3)
{
hawk_val_t* a2;
a2 = hawk_rtx_getarg(rtx, 2);
/* if the 3rd parameter is not an array,
* it is treated as a match start index */
n = hawk_rtx_valtoint(rtx, a2, &start);
if (n <= -1) return -1;
if (nargs >= 4) a3 = hawk_rtx_getarg(rtx, 3);
}
#if 0
if (HAWK_RTX_GETVALTYPE(rtx, a0) == HAWK_VAL_MBS)
{
str0 = ((hawk_val_mbs_t*)a0)->val.ptr;
len0 = ((hawk_val_mbs_t*)a0)->val.len;
}
#endif
str0 = hawk_rtx_getvaloocstr(rtx, a0, &len0);
if (str0 == HAWK_NULL) return -1;
if (start == 0) start = 1;
else if (start < 0) start = len0 + start + 1;
HAWK_MEMSET (submat, 0, HAWK_SIZEOF(submat));
if (start > len0 || start <= 0) n = 0;
else
{
hawk_oocs_t tmp;
/*TODO: must use str0,len0? */
tmp.ptr = str0 + start - 1;
tmp.len = len0 - start + 1;
n = hawk_rtx_matchval(rtx, a1, &tmp, &tmp, &mat, (nargs >= 4? submat: HAWK_NULL));
if (n <= -1) return -1;
}
hawk_rtx_freevaloocstr (rtx, a0, str0);
/* RSTART: 0 on no match */
idx = (n == 0)? 0: ((hawk_int_t)(mat.ptr - str0) + 1);
x0 = hawk_rtx_makeintval(rtx, idx);
if (!x0) goto oops;
hawk_rtx_refupval (rtx, x0);
/* RLENGTH: -1 on no match */
x1 = hawk_rtx_makeintval(rtx, ((n == 0)? (hawk_int_t)-1: (hawk_int_t)mat.len));
if (!x1) goto oops;
hawk_rtx_refupval (rtx, x1);
if (nargs >= 4)
{
const hawk_oocs_t* subsep;
hawk_int_t submatcount;
hawk_oow_t i, xlen;
hawk_val_t* tv;
tmpbuf = hawk_ooecs_open(hawk_rtx_getgem(rtx), 0, 64);
if (!tmpbuf) goto oops;
x2 = hawk_rtx_makemapval(rtx);
if (!x2) goto oops;
hawk_rtx_refupval (rtx, x2);
submatcount =0;
subsep = hawk_rtx_getsubsep (rtx);
for (i = 0; i < HAWK_COUNTOF(submat); i++)
{
if (!submat[i].ptr) break;
submatcount++;
if (hawk_ooecs_fmt(tmpbuf, HAWK_T("%d"), (int)submatcount) == (hawk_oow_t)-1 ||
hawk_ooecs_ncat(tmpbuf, subsep->ptr, subsep->len) == (hawk_oow_t)-1) goto oops;
xlen = HAWK_OOECS_LEN(tmpbuf);
if (hawk_ooecs_ncat(tmpbuf, HAWK_T("start"), 5) == (hawk_oow_t)-1) goto oops;
tv = hawk_rtx_makeintval(rtx, submat[i].ptr - str0 + 1);
if (!tv) goto oops;
if (!hawk_rtx_setmapvalfld(rtx, x2, HAWK_OOECS_PTR(tmpbuf), HAWK_OOECS_LEN(tmpbuf), tv))
{
hawk_rtx_refupval (rtx, tv);
hawk_rtx_refdownval (rtx, tv);
goto oops;
}
if (hawk_ooecs_setlen(tmpbuf, xlen) == (hawk_oow_t)-1 ||
hawk_ooecs_ncat(tmpbuf, HAWK_T("length"), 6) == (hawk_oow_t)-1) goto oops;
tv = hawk_rtx_makeintval(rtx, submat[i].len);
if (!tv) goto oops;
if (!hawk_rtx_setmapvalfld(rtx, x2, HAWK_OOECS_PTR(tmpbuf), HAWK_OOECS_LEN(tmpbuf), 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*)a3, 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 (tmpbuf) hawk_ooecs_close (tmpbuf);
if (x2) hawk_rtx_refdownval (rtx, x2);
hawk_rtx_refdownval (rtx, x1);
hawk_rtx_refdownval (rtx, x0);
return 0;
oops:
if (tmpbuf) hawk_ooecs_close (tmpbuf);
if (x2) hawk_rtx_refdownval (rtx, x2);
if (x1) hawk_rtx_refdownval (rtx, x1);
if (x0) hawk_rtx_refdownval (rtx, x0);
return -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);
if (HAWK_RTX_GETVALTYPE(rtx, a0) == HAWK_VAL_MBS)
{
hawk_becs_t out, fbu;
int out_inited = 0, fbu_inited = 0;
hawk_bcs_t cs0;
hawk_bcs_t x;
if (hawk_becs_init(&out, hawk_rtx_getgem(rtx), 256) <= -1) goto oops_mbs;
out_inited = 1;
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 (cs0.ptr == HAWK_NULL) goto oops_mbs;
x.ptr = hawk_rtx_formatmbs(rtx, &out, &fbu, cs0.ptr, cs0.len, nargs, HAWK_NULL, &x.len);
hawk_rtx_freevalbcstr (rtx, a0, cs0.ptr);
if (!x.ptr) goto oops_mbs;
a0 = hawk_rtx_makembsvalwithbcs(rtx, &x);
if (a0 == HAWK_NULL) goto oops_mbs;
hawk_becs_fini (&fbu);
hawk_becs_fini (&out);
hawk_rtx_setretval (rtx, a0);
return 0;
oops_mbs:
if (fbu_inited) hawk_becs_fini (&fbu);
if (out_inited) hawk_becs_fini (&out);
return -1;
}
else
{
hawk_ooecs_t out, fbu;
int out_inited = 0, fbu_inited = 0;
hawk_oocs_t cs0;
hawk_oocs_t x;
if (hawk_ooecs_init(&out, hawk_rtx_getgem(rtx), 256) <= -1) goto oops;
out_inited = 1;
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 (cs0.ptr == HAWK_NULL) goto oops;
x.ptr = hawk_rtx_format(rtx, &out, &fbu, cs0.ptr, cs0.len, nargs, HAWK_NULL, &x.len);
hawk_rtx_freevaloocstr (rtx, a0, cs0.ptr);
if (!x.ptr) goto oops;
a0 = hawk_rtx_makestrvalwithoocs(rtx, &x);
if (a0 == HAWK_NULL) goto oops;
hawk_ooecs_fini (&fbu);
hawk_ooecs_fini (&out);
hawk_rtx_setretval (rtx, a0);
return 0;
oops:
if (fbu_inited) hawk_ooecs_fini (&fbu);
if (out_inited) hawk_ooecs_fini (&out);
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 == HAWK_NULL) return -1;
hawk_rtx_setretval (rtx, r);
return 0;
}
static int fnc_typename (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
{
hawk_val_t* a0;
hawk_val_t* r;
const hawk_ooch_t* name;
a0 = hawk_rtx_getarg(rtx, 0);
name = hawk_rtx_getvaltypename(rtx, a0);
r = hawk_rtx_makestrvalwithoocstr(rtx, name);
if (r == HAWK_NULL) return -1;
hawk_rtx_setretval (rtx, r);
return 0;
}
static int fnc_isnil (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
{
hawk_val_t* a0;
hawk_val_t* r;
a0 = hawk_rtx_getarg(rtx, 0);
r = hawk_rtx_makeintval(rtx, HAWK_RTX_GETVALTYPE(rtx, a0) == HAWK_VAL_NIL);
if (r == HAWK_NULL) return -1;
hawk_rtx_setretval (rtx, r);
return 0;
}
static int fnc_ismap (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi)
{
hawk_val_t* a0;
hawk_val_t* r;
a0 = hawk_rtx_getarg(rtx, 0);
r = hawk_rtx_makeintval(rtx, HAWK_RTX_GETVALTYPE(rtx, a0) == HAWK_VAL_MAP);
if (r == HAWK_NULL) 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 (!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)
{
hawk_oow_t nargs;
hawk_val_t* a0, * a0_val, * a1, * a2;
hawk_val_type_t a0_type, v_type;
hawk_val_t* r, * rmap = HAWK_NULL;
hawk_int_t rv = 0; /* as if no element in the map */
hawk_val_map_itr_t itr;
hawk_fun_t* fun = HAWK_NULL;
hawk_oow_t msz, i;
hawk_val_t** va;
int x;
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);
v_type = hawk_rtx_getrefvaltype(rtx, (hawk_val_ref_t*)a0);
if (v_type != HAWK_VAL_MAP)
{
if (v_type == HAWK_VAL_NIL)
{
/* treat it as an empty value */
goto done;
}
hawk_rtx_seterrfmt (rtx, HAWK_NULL, HAWK_ENOTMAP, HAWK_T("source not a map"));
return -1;
}
a0_val = hawk_rtx_getrefval(rtx, (hawk_val_ref_t*)a0);
HAWK_ASSERT (HAWK_RTX_GETVALTYPE(rtx, a0_val) == HAWK_VAL_MAP);
if (nargs >= 2)
{
a1 = hawk_rtx_getarg(rtx, 1); /* destination map */
if (nargs >= 3)
{
a2 = hawk_rtx_getarg(rtx, 2);
if (HAWK_RTX_GETVALTYPE(rtx, a2) != HAWK_VAL_FUN)
{
hawk_rtx_seterrfmt (rtx, HAWK_NULL, HAWK_EINVAL, HAWK_T("comparator not a function"));
return -1;
}
fun = ((hawk_val_fun_t*)a2)->fun;
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;
}
}
}
else
{
a1 = a0; /* let a0 be the destination. a0 is both source and destination */
}
if (!hawk_rtx_getfirstmapvalitr(rtx, a0_val, &itr)) goto done; /* map empty */
msz = hawk_htb_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 (!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 (!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 || !(rmap = 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,
10 | HAWK_FMT_UINTMAX_NOTRUNC | HAWK_FMT_UINTMAX_NONULL,
-1,
HAWK_T('\0'),
HAWK_NULL
);
if (hawk_rtx_setmapvalfld(rtx, rmap, ridx, ridx_len, va[i]) == HAWK_NULL)
{
/* 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, rmap, 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);
done:
r = hawk_rtx_makeintval(rtx, rv);
if (!r) return -1;
if (rmap)
{
/* rmap can be NULL when a jump has been made for an empty source
* at the beginning of this fucntion */
hawk_rtx_refupval (rtx, rmap);
x = hawk_rtx_setrefval (rtx, (hawk_val_ref_t*)a1, rmap);
hawk_rtx_refdownval (rtx, rmap);
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);
}