From 9ee8378d393778ac67314be7ea8d5bcbaeee9ee0 Mon Sep 17 00:00:00 2001 From: Aaron Seigo Date: Sun, 7 Dec 2014 10:08:07 +0100 Subject: try out unqlite --- common/unqlite/jx9_builtin.c | 8297 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 8297 insertions(+) create mode 100644 common/unqlite/jx9_builtin.c (limited to 'common/unqlite/jx9_builtin.c') diff --git a/common/unqlite/jx9_builtin.c b/common/unqlite/jx9_builtin.c new file mode 100644 index 0000000..83e6c6e --- /dev/null +++ b/common/unqlite/jx9_builtin.c @@ -0,0 +1,8297 @@ +/* + * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. + * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ + * Version 1.7.2 + * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES + * please contact Symisc Systems via: + * legal@symisc.net + * licensing@symisc.net + * contact@symisc.net + * or visit: + * http://jx9.symisc.net/ + */ + /* $SymiscID: builtin.c v1.7 Win7 2012-12-13 00:01 stable $ */ +#ifndef JX9_AMALGAMATION +#include "jx9Int.h" +#endif +/* This file implement built-in 'foreign' functions for the JX9 engine */ +/* + * Section: + * Variable handling Functions. + * Authors: + * Symisc Systems, devel@symisc.net. + * Copyright (C) Symisc Systems, http://jx9.symisc.net + * Status: + * Stable. + */ +/* + * bool is_bool($var) + * Finds out whether a variable is a boolean. + * Parameters + * $var: The variable being evaluated. + * Return + * TRUE if var is a boolean. False otherwise. + */ +static int jx9Builtin_is_bool(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int res = 0; /* Assume false by default */ + if( nArg > 0 ){ + res = jx9_value_is_bool(apArg[0]); + } + /* Query result */ + jx9_result_bool(pCtx, res); + return JX9_OK; +} +/* + * bool is_float($var) + * bool is_real($var) + * bool is_double($var) + * Finds out whether a variable is a float. + * Parameters + * $var: The variable being evaluated. + * Return + * TRUE if var is a float. False otherwise. + */ +static int jx9Builtin_is_float(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int res = 0; /* Assume false by default */ + if( nArg > 0 ){ + res = jx9_value_is_float(apArg[0]); + } + /* Query result */ + jx9_result_bool(pCtx, res); + return JX9_OK; +} +/* + * bool is_int($var) + * bool is_integer($var) + * bool is_long($var) + * Finds out whether a variable is an integer. + * Parameters + * $var: The variable being evaluated. + * Return + * TRUE if var is an integer. False otherwise. + */ +static int jx9Builtin_is_int(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int res = 0; /* Assume false by default */ + if( nArg > 0 ){ + res = jx9_value_is_int(apArg[0]); + } + /* Query result */ + jx9_result_bool(pCtx, res); + return JX9_OK; +} +/* + * bool is_string($var) + * Finds out whether a variable is a string. + * Parameters + * $var: The variable being evaluated. + * Return + * TRUE if var is string. False otherwise. + */ +static int jx9Builtin_is_string(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int res = 0; /* Assume false by default */ + if( nArg > 0 ){ + res = jx9_value_is_string(apArg[0]); + } + /* Query result */ + jx9_result_bool(pCtx, res); + return JX9_OK; +} +/* + * bool is_null($var) + * Finds out whether a variable is NULL. + * Parameters + * $var: The variable being evaluated. + * Return + * TRUE if var is NULL. False otherwise. + */ +static int jx9Builtin_is_null(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int res = 0; /* Assume false by default */ + if( nArg > 0 ){ + res = jx9_value_is_null(apArg[0]); + } + /* Query result */ + jx9_result_bool(pCtx, res); + return JX9_OK; +} +/* + * bool is_numeric($var) + * Find out whether a variable is NULL. + * Parameters + * $var: The variable being evaluated. + * Return + * True if var is numeric. False otherwise. + */ +static int jx9Builtin_is_numeric(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int res = 0; /* Assume false by default */ + if( nArg > 0 ){ + res = jx9_value_is_numeric(apArg[0]); + } + /* Query result */ + jx9_result_bool(pCtx, res); + return JX9_OK; +} +/* + * bool is_scalar($var) + * Find out whether a variable is a scalar. + * Parameters + * $var: The variable being evaluated. + * Return + * True if var is scalar. False otherwise. + */ +static int jx9Builtin_is_scalar(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int res = 0; /* Assume false by default */ + if( nArg > 0 ){ + res = jx9_value_is_scalar(apArg[0]); + } + /* Query result */ + jx9_result_bool(pCtx, res); + return JX9_OK; +} +/* + * bool is_array($var) + * Find out whether a variable is an array. + * Parameters + * $var: The variable being evaluated. + * Return + * True if var is an array. False otherwise. + */ +static int jx9Builtin_is_array(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int res = 0; /* Assume false by default */ + if( nArg > 0 ){ + res = jx9_value_is_json_array(apArg[0]); + } + /* Query result */ + jx9_result_bool(pCtx, res); + return JX9_OK; +} +/* + * bool is_object($var) + * Find out whether a variable is an object. + * Parameters + * $var: The variable being evaluated. + * Return + * True if var is an object. False otherwise. + */ +static int jx9Builtin_is_object(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int res = 0; /* Assume false by default */ + if( nArg > 0 ){ + res = jx9_value_is_json_object(apArg[0]); + } + /* Query result */ + jx9_result_bool(pCtx, res); + return JX9_OK; +} +/* + * bool is_resource($var) + * Find out whether a variable is a resource. + * Parameters + * $var: The variable being evaluated. + * Return + * True if a resource. False otherwise. + */ +static int jx9Builtin_is_resource(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int res = 0; /* Assume false by default */ + if( nArg > 0 ){ + res = jx9_value_is_resource(apArg[0]); + } + jx9_result_bool(pCtx, res); + return JX9_OK; +} +/* + * float floatval($var) + * Get float value of a variable. + * Parameter + * $var: The variable being processed. + * Return + * the float value of a variable. + */ +static int jx9Builtin_floatval(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + if( nArg < 1 ){ + /* return 0.0 */ + jx9_result_double(pCtx, 0); + }else{ + double dval; + /* Perform the cast */ + dval = jx9_value_to_double(apArg[0]); + jx9_result_double(pCtx, dval); + } + return JX9_OK; +} +/* + * int intval($var) + * Get integer value of a variable. + * Parameter + * $var: The variable being processed. + * Return + * the int value of a variable. + */ +static int jx9Builtin_intval(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + if( nArg < 1 ){ + /* return 0 */ + jx9_result_int(pCtx, 0); + }else{ + sxi64 iVal; + /* Perform the cast */ + iVal = jx9_value_to_int64(apArg[0]); + jx9_result_int64(pCtx, iVal); + } + return JX9_OK; +} +/* + * string strval($var) + * Get the string representation of a variable. + * Parameter + * $var: The variable being processed. + * Return + * the string value of a variable. + */ +static int jx9Builtin_strval(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + if( nArg < 1 ){ + /* return NULL */ + jx9_result_null(pCtx); + }else{ + const char *zVal; + int iLen = 0; /* cc -O6 warning */ + /* Perform the cast */ + zVal = jx9_value_to_string(apArg[0], &iLen); + jx9_result_string(pCtx, zVal, iLen); + } + return JX9_OK; +} +/* + * bool empty($var) + * Determine whether a variable is empty. + * Parameters + * $var: The variable being checked. + * Return + * 0 if var has a non-empty and non-zero value.1 otherwise. + */ +static int jx9Builtin_empty(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int res = 1; /* Assume empty by default */ + if( nArg > 0 ){ + res = jx9_value_is_empty(apArg[0]); + } + jx9_result_bool(pCtx, res); + return JX9_OK; + +} +#ifndef JX9_DISABLE_BUILTIN_FUNC +#ifdef JX9_ENABLE_MATH_FUNC +/* + * Section: + * Math Functions. + * Authors: + * Symisc Systems, devel@symisc.net. + * Copyright (C) Symisc Systems, http://jx9.symisc.net + * Status: + * Stable. + */ +#include /* abs */ +#include +/* + * float sqrt(float $arg ) + * Square root of the given number. + * Parameter + * The number to process. + * Return + * The square root of arg or the special value Nan of failure. + */ +static int jx9Builtin_sqrt(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = sqrt(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float exp(float $arg ) + * Calculates the exponent of e. + * Parameter + * The number to process. + * Return + * 'e' raised to the power of arg. + */ +static int jx9Builtin_exp(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = exp(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float floor(float $arg ) + * Round fractions down. + * Parameter + * The number to process. + * Return + * Returns the next lowest integer value by rounding down value if necessary. + */ +static int jx9Builtin_floor(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = floor(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float cos(float $arg ) + * Cosine. + * Parameter + * The number to process. + * Return + * The cosine of arg. + */ +static int jx9Builtin_cos(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = cos(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float acos(float $arg ) + * Arc cosine. + * Parameter + * The number to process. + * Return + * The arc cosine of arg. + */ +static int jx9Builtin_acos(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = acos(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float cosh(float $arg ) + * Hyperbolic cosine. + * Parameter + * The number to process. + * Return + * The hyperbolic cosine of arg. + */ +static int jx9Builtin_cosh(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = cosh(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float sin(float $arg ) + * Sine. + * Parameter + * The number to process. + * Return + * The sine of arg. + */ +static int jx9Builtin_sin(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = sin(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float asin(float $arg ) + * Arc sine. + * Parameter + * The number to process. + * Return + * The arc sine of arg. + */ +static int jx9Builtin_asin(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = asin(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float sinh(float $arg ) + * Hyperbolic sine. + * Parameter + * The number to process. + * Return + * The hyperbolic sine of arg. + */ +static int jx9Builtin_sinh(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = sinh(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float ceil(float $arg ) + * Round fractions up. + * Parameter + * The number to process. + * Return + * The next highest integer value by rounding up value if necessary. + */ +static int jx9Builtin_ceil(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = ceil(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float tan(float $arg ) + * Tangent. + * Parameter + * The number to process. + * Return + * The tangent of arg. + */ +static int jx9Builtin_tan(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = tan(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float atan(float $arg ) + * Arc tangent. + * Parameter + * The number to process. + * Return + * The arc tangent of arg. + */ +static int jx9Builtin_atan(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = atan(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float tanh(float $arg ) + * Hyperbolic tangent. + * Parameter + * The number to process. + * Return + * The Hyperbolic tangent of arg. + */ +static int jx9Builtin_tanh(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = tanh(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float atan2(float $y, float $x) + * Arc tangent of two variable. + * Parameter + * $y = Dividend parameter. + * $x = Divisor parameter. + * Return + * The arc tangent of y/x in radian. + */ +static int jx9Builtin_atan2(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x, y; + if( nArg < 2 ){ + /* Missing arguments, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + y = jx9_value_to_double(apArg[0]); + x = jx9_value_to_double(apArg[1]); + /* Perform the requested operation */ + r = atan2(y, x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float/int64 abs(float/int64 $arg ) + * Absolute value. + * Parameter + * The number to process. + * Return + * The absolute value of number. + */ +static int jx9Builtin_abs(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int is_float; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + is_float = jx9_value_is_float(apArg[0]); + if( is_float ){ + double r, x; + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = fabs(x); + jx9_result_double(pCtx, r); + }else{ + int r, x; + x = jx9_value_to_int(apArg[0]); + /* Perform the requested operation */ + r = abs(x); + jx9_result_int(pCtx, r); + } + return JX9_OK; +} +/* + * float log(float $arg, [int/float $base]) + * Natural logarithm. + * Parameter + * $arg: The number to process. + * $base: The optional logarithmic base to use. (only base-10 is supported) + * Return + * The logarithm of arg to base, if given, or the natural logarithm. + * Note: + * only Natural log and base-10 log are supported. + */ +static int jx9Builtin_log(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + if( nArg == 2 && jx9_value_is_numeric(apArg[1]) && jx9_value_to_int(apArg[1]) == 10 ){ + /* Base-10 log */ + r = log10(x); + }else{ + r = log(x); + } + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float log10(float $arg ) + * Base-10 logarithm. + * Parameter + * The number to process. + * Return + * The Base-10 logarithm of the given number. + */ +static int jx9Builtin_log10(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + /* Perform the requested operation */ + r = log10(x); + /* store the result back */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * number pow(number $base, number $exp) + * Exponential expression. + * Parameter + * base + * The base to use. + * exp + * The exponent. + * Return + * base raised to the power of exp. + * If the result can be represented as integer it will be returned + * as type integer, else it will be returned as type float. + */ +static int jx9Builtin_pow(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double r, x, y; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + x = jx9_value_to_double(apArg[0]); + y = jx9_value_to_double(apArg[1]); + /* Perform the requested operation */ + r = pow(x, y); + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float pi(void) + * Returns an approximation of pi. + * Note + * you can use the M_PI constant which yields identical results to pi(). + * Return + * The value of pi as float. + */ +static int jx9Builtin_pi(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + SXUNUSED(nArg); /* cc warning */ + SXUNUSED(apArg); + jx9_result_double(pCtx, JX9_PI); + return JX9_OK; +} +/* + * float fmod(float $x, float $y) + * Returns the floating point remainder (modulo) of the division of the arguments. + * Parameters + * $x + * The dividend + * $y + * The divisor + * Return + * The floating point remainder of x/y. + */ +static int jx9Builtin_fmod(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double x, y, r; + if( nArg < 2 ){ + /* Missing arguments */ + jx9_result_double(pCtx, 0); + return JX9_OK; + } + /* Extract given arguments */ + x = jx9_value_to_double(apArg[0]); + y = jx9_value_to_double(apArg[1]); + /* Perform the requested operation */ + r = fmod(x, y); + /* Processing result */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +/* + * float hypot(float $x, float $y) + * Calculate the length of the hypotenuse of a right-angle triangle . + * Parameters + * $x + * Length of first side + * $y + * Length of first side + * Return + * Calculated length of the hypotenuse. + */ +static int jx9Builtin_hypot(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + double x, y, r; + if( nArg < 2 ){ + /* Missing arguments */ + jx9_result_double(pCtx, 0); + return JX9_OK; + } + /* Extract given arguments */ + x = jx9_value_to_double(apArg[0]); + y = jx9_value_to_double(apArg[1]); + /* Perform the requested operation */ + r = hypot(x, y); + /* Processing result */ + jx9_result_double(pCtx, r); + return JX9_OK; +} +#endif /* JX9_ENABLE_MATH_FUNC */ +/* + * float round ( float $val [, int $precision = 0 [, int $mode = JX9_ROUND_HALF_UP ]] ) + * Exponential expression. + * Parameter + * $val + * The value to round. + * $precision + * The optional number of decimal digits to round to. + * $mode + * One of JX9_ROUND_HALF_UP, JX9_ROUND_HALF_DOWN, JX9_ROUND_HALF_EVEN, or JX9_ROUND_HALF_ODD. + * (not supported). + * Return + * The rounded value. + */ +static int jx9Builtin_round(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int n = 0; + double r; + if( nArg < 1 ){ + /* Missing argument, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Extract the precision if available */ + if( nArg > 1 ){ + n = jx9_value_to_int(apArg[1]); + if( n>30 ){ + n = 30; + } + if( n<0 ){ + n = 0; + } + } + r = jx9_value_to_double(apArg[0]); + /* If Y==0 and X will fit in a 64-bit int, + * handle the rounding directly.Otherwise + * use our own cutsom printf [i.e:SyBufferFormat()]. + */ + if( n==0 && r>=0 && r= 0xc0 ){ + /* UTF-8 stream */ + zString++; + while( zString < zEnd && (((unsigned char)zString[0] & 0xc0) == 0x80) ){ + zString++; + } + }else{ + if( SyisHex(zString[0]) ){ + break; + } + /* Ignore */ + zString++; + } + } + if( zString < zEnd ){ + /* Cast */ + SyHexStrToInt64(zString, (sxu32)(zEnd-zString), (void *)&iVal, 0); + } + }else{ + /* Extract as a 64-bit integer */ + iVal = jx9_value_to_int64(apArg[0]); + } + /* Return the number */ + jx9_result_int64(pCtx, iVal); + return JX9_OK; +} +/* + * int64 bindec(string $bin_string) + * Binary to decimal. + * Parameters + * $bin_string + * The binary string to convert + * Return + * Returns the decimal equivalent of the binary number represented by the binary_string argument. + */ +static int jx9Builtin_bindec(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString; + jx9_int64 iVal; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return -1 */ + jx9_result_int(pCtx, -1); + return JX9_OK; + } + iVal = 0; + if( jx9_value_is_string(apArg[0]) ){ + /* Extract the given string */ + zString = jx9_value_to_string(apArg[0], &nLen); + if( nLen > 0 ){ + /* Perform a binary cast */ + SyBinaryStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0); + } + }else{ + /* Extract as a 64-bit integer */ + iVal = jx9_value_to_int64(apArg[0]); + } + /* Return the number */ + jx9_result_int64(pCtx, iVal); + return JX9_OK; +} +/* + * int64 octdec(string $oct_string) + * Octal to decimal. + * Parameters + * $oct_string + * The octal string to convert + * Return + * Returns the decimal equivalent of the octal number represented by the octal_string argument. + */ +static int jx9Builtin_octdec(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString; + jx9_int64 iVal; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return -1 */ + jx9_result_int(pCtx, -1); + return JX9_OK; + } + iVal = 0; + if( jx9_value_is_string(apArg[0]) ){ + /* Extract the given string */ + zString = jx9_value_to_string(apArg[0], &nLen); + if( nLen > 0 ){ + /* Perform the cast */ + SyOctalStrToInt64(zString, (sxu32)nLen, (void *)&iVal, 0); + } + }else{ + /* Extract as a 64-bit integer */ + iVal = jx9_value_to_int64(apArg[0]); + } + /* Return the number */ + jx9_result_int64(pCtx, iVal); + return JX9_OK; +} +/* + * string base_convert(string $number, int $frombase, int $tobase) + * Convert a number between arbitrary bases. + * Parameters + * $number + * The number to convert + * $frombase + * The base number is in + * $tobase + * The base to convert number to + * Return + * Number converted to base tobase + */ +static int jx9Builtin_base_convert(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int nLen, iFbase, iTobase; + const char *zNum; + jx9_int64 iNum; + if( nArg < 3 ){ + /* Return the empty string*/ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Base numbers */ + iFbase = jx9_value_to_int(apArg[1]); + iTobase = jx9_value_to_int(apArg[2]); + if( jx9_value_is_string(apArg[0]) ){ + /* Extract the target number */ + zNum = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Return the empty string*/ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Base conversion */ + switch(iFbase){ + case 16: + /* Hex */ + SyHexStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0); + break; + case 8: + /* Octal */ + SyOctalStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0); + break; + case 2: + /* Binary */ + SyBinaryStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0); + break; + default: + /* Decimal */ + SyStrToInt64(zNum, (sxu32)nLen, (void *)&iNum, 0); + break; + } + }else{ + iNum = jx9_value_to_int64(apArg[0]); + } + switch(iTobase){ + case 16: + /* Hex */ + jx9_result_string_format(pCtx, "%qx", iNum); /* Quad hex */ + break; + case 8: + /* Octal */ + jx9_result_string_format(pCtx, "%qo", iNum); /* Quad octal */ + break; + case 2: + /* Binary */ + jx9_result_string_format(pCtx, "%qB", iNum); /* Quad binary */ + break; + default: + /* Decimal */ + jx9_result_string_format(pCtx, "%qd", iNum); /* Quad decimal */ + break; + } + return JX9_OK; +} +/* + * Section: + * String handling Functions. + * Authors: + * Symisc Systems, devel@symisc.net. + * Copyright (C) Symisc Systems, http://jx9.symisc.net + * Status: + * Stable. + */ +/* + * string substr(string $string, int $start[, int $length ]) + * Return part of a string. + * Parameters + * $string + * The input string. Must be one character or longer. + * $start + * If start is non-negative, the returned string will start at the start'th position + * in string, counting from zero. For instance, in the string 'abcdef', the character + * at position 0 is 'a', the character at position 2 is 'c', and so forth. + * If start is negative, the returned string will start at the start'th character + * from the end of string. + * If string is less than or equal to start characters long, FALSE will be returned. + * $length + * If length is given and is positive, the string returned will contain at most length + * characters beginning from start (depending on the length of string). + * If length is given and is negative, then that many characters will be omitted from + * the end of string (after the start position has been calculated when a start is negative). + * If start denotes the position of this truncation or beyond, false will be returned. + * If length is given and is 0, FALSE or NULL an empty string will be returned. + * If length is omitted, the substring starting from start until the end of the string + * will be returned. + * Return + * Returns the extracted part of string, or FALSE on failure or an empty string. + */ +static int jx9Builtin_substr(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zSource, *zOfft; + int nOfft, nLen, nSrcLen; + if( nArg < 2 ){ + /* return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zSource = jx9_value_to_string(apArg[0], &nSrcLen); + if( nSrcLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + nLen = nSrcLen; /* cc warning */ + /* Extract the offset */ + nOfft = jx9_value_to_int(apArg[1]); + if( nOfft < 0 ){ + zOfft = &zSource[nSrcLen+nOfft]; + if( zOfft < zSource ){ + /* Invalid offset */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + nLen = (int)(&zSource[nSrcLen]-zOfft); + nOfft = (int)(zOfft-zSource); + }else if( nOfft >= nSrcLen ){ + /* Invalid offset */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + }else{ + zOfft = &zSource[nOfft]; + nLen = nSrcLen - nOfft; + } + if( nArg > 2 ){ + /* Extract the length */ + nLen = jx9_value_to_int(apArg[2]); + if( nLen == 0 ){ + /* Invalid length, return an empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + }else if( nLen < 0 ){ + nLen = nSrcLen + nLen - nOfft; + if( nLen < 1 ){ + /* Invalid length */ + nLen = nSrcLen - nOfft; + } + } + if( nLen + nOfft > nSrcLen ){ + /* Invalid length */ + nLen = nSrcLen - nOfft; + } + } + /* Return the substring */ + jx9_result_string(pCtx, zOfft, nLen); + return JX9_OK; +} +/* + * int substr_compare(string $main_str, string $str , int $offset[, int $length[, bool $case_insensitivity = false ]]) + * Binary safe comparison of two strings from an offset, up to length characters. + * Parameters + * $main_str + * The main string being compared. + * $str + * The secondary string being compared. + * $offset + * The start position for the comparison. If negative, it starts counting from + * the end of the string. + * $length + * The length of the comparison. The default value is the largest of the length + * of the str compared to the length of main_str less the offset. + * $case_insensitivity + * If case_insensitivity is TRUE, comparison is case insensitive. + * Return + * Returns < 0 if main_str from position offset is less than str, > 0 if it is greater than + * str, and 0 if they are equal. If offset is equal to or greater than the length of main_str + * or length is set and is less than 1, substr_compare() prints a warning and returns FALSE. + */ +static int jx9Builtin_substr_compare(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zSource, *zOfft, *zSub; + int nOfft, nLen, nSrcLen, nSublen; + int iCase = 0; + int rc; + if( nArg < 3 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zSource = jx9_value_to_string(apArg[0], &nSrcLen); + if( nSrcLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + nLen = nSrcLen; /* cc warning */ + /* Extract the substring */ + zSub = jx9_value_to_string(apArg[1], &nSublen); + if( nSublen < 1 || nSublen > nSrcLen){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the offset */ + nOfft = jx9_value_to_int(apArg[2]); + if( nOfft < 0 ){ + zOfft = &zSource[nSrcLen+nOfft]; + if( zOfft < zSource ){ + /* Invalid offset */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + nLen = (int)(&zSource[nSrcLen]-zOfft); + nOfft = (int)(zOfft-zSource); + }else if( nOfft >= nSrcLen ){ + /* Invalid offset */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + }else{ + zOfft = &zSource[nOfft]; + nLen = nSrcLen - nOfft; + } + if( nArg > 3 ){ + /* Extract the length */ + nLen = jx9_value_to_int(apArg[3]); + if( nLen < 1 ){ + /* Invalid length */ + jx9_result_int(pCtx, 1); + return JX9_OK; + }else if( nLen + nOfft > nSrcLen ){ + /* Invalid length */ + nLen = nSrcLen - nOfft; + } + if( nArg > 4 ){ + /* Case-sensitive or not */ + iCase = jx9_value_to_bool(apArg[4]); + } + } + /* Perform the comparison */ + if( iCase ){ + rc = SyStrnicmp(zOfft, zSub, (sxu32)nLen); + }else{ + rc = SyStrncmp(zOfft, zSub, (sxu32)nLen); + } + /* Comparison result */ + jx9_result_int(pCtx, rc); + return JX9_OK; +} +/* + * int substr_count(string $haystack, string $needle[, int $offset = 0 [, int $length ]]) + * Count the number of substring occurrences. + * Parameters + * $haystack + * The string to search in + * $needle + * The substring to search for + * $offset + * The offset where to start counting + * $length (NOT USED) + * The maximum length after the specified offset to search for the substring. + * It outputs a warning if the offset plus the length is greater than the haystack length. + * Return + * Toral number of substring occurrences. + */ +static int jx9Builtin_substr_count(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zText, *zPattern, *zEnd; + int nTextlen, nPatlen; + int iCount = 0; + sxu32 nOfft; + sxi32 rc; + if( nArg < 2 ){ + /* Missing arguments */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Point to the haystack */ + zText = jx9_value_to_string(apArg[0], &nTextlen); + /* Point to the neddle */ + zPattern = jx9_value_to_string(apArg[1], &nPatlen); + if( nTextlen < 1 || nPatlen < 1 || nPatlen > nTextlen ){ + /* NOOP, return zero */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + if( nArg > 2 ){ + int nOfft; + /* Extract the offset */ + nOfft = jx9_value_to_int(apArg[2]); + if( nOfft < 0 || nOfft > nTextlen ){ + /* Invalid offset, return zero */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Point to the desired offset */ + zText = &zText[nOfft]; + /* Adjust length */ + nTextlen -= nOfft; + } + /* Point to the end of the string */ + zEnd = &zText[nTextlen]; + if( nArg > 3 ){ + int nLen; + /* Extract the length */ + nLen = jx9_value_to_int(apArg[3]); + if( nLen < 0 || nLen > nTextlen ){ + /* Invalid length, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Adjust pointer */ + nTextlen = nLen; + zEnd = &zText[nTextlen]; + } + /* Perform the search */ + for(;;){ + rc = SyBlobSearch((const void *)zText, (sxu32)(zEnd-zText), (const void *)zPattern, nPatlen, &nOfft); + if( rc != SXRET_OK ){ + /* Pattern not found, break immediately */ + break; + } + /* Increment counter and update the offset */ + iCount++; + zText += nOfft + nPatlen; + if( zText >= zEnd ){ + break; + } + } + /* Pattern count */ + jx9_result_int(pCtx, iCount); + return JX9_OK; +} +/* + * string chunk_split(string $body[, int $chunklen = 76 [, string $end = "\r\n" ]]) + * Split a string into smaller chunks. + * Parameters + * $body + * The string to be chunked. + * $chunklen + * The chunk length. + * $end + * The line ending sequence. + * Return + * The chunked string or NULL on failure. + */ +static int jx9Builtin_chunk_split(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zIn, *zEnd, *zSep = "\r\n"; + int nSepLen, nChunkLen, nLen; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Nothing to split, return null */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* initialize/Extract arguments */ + nSepLen = (int)sizeof("\r\n") - 1; + nChunkLen = 76; + zIn = jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + if( nArg > 1 ){ + /* Chunk length */ + nChunkLen = jx9_value_to_int(apArg[1]); + if( nChunkLen < 1 ){ + /* Switch back to the default length */ + nChunkLen = 76; + } + if( nArg > 2 ){ + /* Separator */ + zSep = jx9_value_to_string(apArg[2], &nSepLen); + if( nSepLen < 1 ){ + /* Switch back to the default separator */ + zSep = "\r\n"; + nSepLen = (int)sizeof("\r\n") - 1; + } + } + } + /* Perform the requested operation */ + if( nChunkLen > nLen ){ + /* Nothing to split, return the string and the separator */ + jx9_result_string_format(pCtx, "%.*s%.*s", nLen, zIn, nSepLen, zSep); + return JX9_OK; + } + while( zIn < zEnd ){ + if( nChunkLen > (int)(zEnd-zIn) ){ + nChunkLen = (int)(zEnd - zIn); + } + /* Append the chunk and the separator */ + jx9_result_string_format(pCtx, "%.*s%.*s", nChunkLen, zIn, nSepLen, zSep); + /* Point beyond the chunk */ + zIn += nChunkLen; + } + return JX9_OK; +} +/* + * string htmlspecialchars(string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $charset]]) + * HTML escaping of special characters. + * The translations performed are: + * '&' (ampersand) ==> '&' + * '"' (double quote) ==> '"' when ENT_NOQUOTES is not set. + * "'" (single quote) ==> ''' only when ENT_QUOTES is set. + * '<' (less than) ==> '<' + * '>' (greater than) ==> '>' + * Parameters + * $string + * The string being converted. + * $flags + * A bitmask of one or more of the following flags, which specify how to handle quotes. + * The default is ENT_COMPAT | ENT_HTML401. + * ENT_COMPAT Will convert double-quotes and leave single-quotes alone. + * ENT_QUOTES Will convert both double and single quotes. + * ENT_NOQUOTES Will leave both double and single quotes unconverted. + * ENT_IGNORE Silently discard invalid code unit sequences instead of returning an empty string. + * $charset + * Defines character set used in conversion. The default character set is ISO-8859-1. (Not used) + * Return + * The escaped string or NULL on failure. + */ +static int jx9Builtin_htmlspecialchars(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zCur, *zIn, *zEnd; + int iFlags = 0x01|0x40; /* ENT_COMPAT | ENT_HTML401 */ + int nLen, c; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the target string */ + zIn = jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + /* Extract the flags if available */ + if( nArg > 1 ){ + iFlags = jx9_value_to_int(apArg[1]); + if( iFlags < 0 ){ + iFlags = 0x01|0x40; + } + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + break; + } + zCur = zIn; + while( zIn < zEnd && zIn[0] != '&' && zIn[0] != '\'' && zIn[0] != '"' && zIn[0] != '<' && zIn[0] != '>' ){ + zIn++; + } + if( zCur < zIn ){ + /* Append the raw string verbatim */ + jx9_result_string(pCtx, zCur, (int)(zIn-zCur)); + } + if( zIn >= zEnd ){ + break; + } + c = zIn[0]; + if( c == '&' ){ + /* Expand '&' */ + jx9_result_string(pCtx, "&", (int)sizeof("&")-1); + }else if( c == '<' ){ + /* Expand '<' */ + jx9_result_string(pCtx, "<", (int)sizeof("<")-1); + }else if( c == '>' ){ + /* Expand '>' */ + jx9_result_string(pCtx, ">", (int)sizeof(">")-1); + }else if( c == '\'' ){ + if( iFlags & 0x02 /*ENT_QUOTES*/ ){ + /* Expand ''' */ + jx9_result_string(pCtx, "'", (int)sizeof("'")-1); + }else{ + /* Leave the single quote untouched */ + jx9_result_string(pCtx, "'", (int)sizeof(char)); + } + }else if( c == '"' ){ + if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){ + /* Expand '"' */ + jx9_result_string(pCtx, """, (int)sizeof(""")-1); + }else{ + /* Leave the double quote untouched */ + jx9_result_string(pCtx, "\"", (int)sizeof(char)); + } + } + /* Ignore the unsafe HTML character */ + zIn++; + } + return JX9_OK; +} +/* + * string htmlspecialchars_decode(string $string[, int $quote_style = ENT_COMPAT ]) + * Unescape HTML entities. + * Parameters + * $string + * The string to decode + * $quote_style + * The quote style. One of the following constants: + * ENT_COMPAT Will convert double-quotes and leave single-quotes alone (default) + * ENT_QUOTES Will convert both double and single quotes + * ENT_NOQUOTES Will leave both double and single quotes unconverted + * Return + * The unescaped string or NULL on failure. + */ +static int jx9Builtin_htmlspecialchars_decode(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zCur, *zIn, *zEnd; + int iFlags = 0x01; /* ENT_COMPAT */ + int nLen, nJump; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the target string */ + zIn = jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + /* Extract the flags if available */ + if( nArg > 1 ){ + iFlags = jx9_value_to_int(apArg[1]); + if( iFlags < 0 ){ + iFlags = 0x01; + } + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + break; + } + zCur = zIn; + while( zIn < zEnd && zIn[0] != '&' ){ + zIn++; + } + if( zCur < zIn ){ + /* Append the raw string verbatim */ + jx9_result_string(pCtx, zCur, (int)(zIn-zCur)); + } + nLen = (int)(zEnd-zIn); + nJump = (int)sizeof(char); + if( nLen >= (int)sizeof("&")-1 && SyStrnicmp(zIn, "&", sizeof("&")-1) == 0 ){ + /* & ==> '&' */ + jx9_result_string(pCtx, "&", (int)sizeof(char)); + nJump = (int)sizeof("&")-1; + }else if( nLen >= (int)sizeof("<")-1 && SyStrnicmp(zIn, "<", sizeof("<")-1) == 0 ){ + /* < ==> < */ + jx9_result_string(pCtx, "<", (int)sizeof(char)); + nJump = (int)sizeof("<")-1; + }else if( nLen >= (int)sizeof(">")-1 && SyStrnicmp(zIn, ">", sizeof(">")-1) == 0 ){ + /* > ==> '>' */ + jx9_result_string(pCtx, ">", (int)sizeof(char)); + nJump = (int)sizeof(">")-1; + }else if( nLen >= (int)sizeof(""")-1 && SyStrnicmp(zIn, """, sizeof(""")-1) == 0 ){ + /* " ==> '"' */ + if( (iFlags & 0x04) == 0 /*ENT_NOQUOTES*/ ){ + jx9_result_string(pCtx, "\"", (int)sizeof(char)); + }else{ + /* Leave untouched */ + jx9_result_string(pCtx, """, (int)sizeof(""")-1); + } + nJump = (int)sizeof(""")-1; + }else if( nLen >= (int)sizeof("'")-1 && SyStrnicmp(zIn, "'", sizeof("'")-1) == 0 ){ + /* ' ==> ''' */ + if( iFlags & 0x02 /*ENT_QUOTES*/ ){ + /* Expand ''' */ + jx9_result_string(pCtx, "'", (int)sizeof(char)); + }else{ + /* Leave untouched */ + jx9_result_string(pCtx, "'", (int)sizeof("'")-1); + } + nJump = (int)sizeof("'")-1; + }else if( nLen >= (int)sizeof(char) ){ + /* expand '&' */ + jx9_result_string(pCtx, "&", (int)sizeof(char)); + }else{ + /* No more input to process */ + break; + } + zIn += nJump; + } + return JX9_OK; +} +/* HTML encoding/Decoding table + * Source: Symisc RunTime API.[chm@symisc.net] + */ +static const char *azHtmlEscape[] = { + "<", "<", ">", ">", "&", "&", """, "\"", "'", "'", + "!", "!", "$", "$", "#", "#", "%", "%", "(", "(", + ")", ")", "{", "{", "}", "}", "=", "=", "+", "+", + "?", "?", "[", "[", "]", "]", "@", "@", ",", "," + }; +/* + * array get_html_translation_table(void) + * Returns the translation table used by htmlspecialchars() and htmlentities(). + * Parameters + * None + * Return + * The translation table as an array or NULL on failure. + */ +static int jx9Builtin_get_html_translation_table(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_value *pArray, *pValue; + sxu32 n; + /* Element value */ + pValue = jx9_context_new_scalar(pCtx); + if( pValue == 0 ){ + SXUNUSED(nArg); /* cc warning */ + SXUNUSED(apArg); + /* Return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Create a new array */ + pArray = jx9_context_new_array(pCtx); + if( pArray == 0 ){ + /* Return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Make the table */ + for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){ + /* Prepare the value */ + jx9_value_string(pValue, azHtmlEscape[n], -1 /* Compute length automatically */); + /* Insert the value */ + jx9_array_add_strkey_elem(pArray, azHtmlEscape[n+1], pValue); + /* Reset the string cursor */ + jx9_value_reset_string_cursor(pValue); + } + /* + * Return the array. + * Don't worry about freeing memory, everything will be automatically + * released upon we return from this function. + */ + jx9_result_value(pCtx, pArray); + return JX9_OK; +} +/* + * string htmlentities( string $string [, int $flags = ENT_COMPAT | ENT_HTML401]); + * Convert all applicable characters to HTML entities + * Parameters + * $string + * The input string. + * $flags + * A bitmask of one or more of the flags (see block-comment on jx9Builtin_htmlspecialchars()) + * Return + * The encoded string. + */ +static int jx9Builtin_htmlentities(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int iFlags = 0x01; /* ENT_COMPAT */ + const char *zIn, *zEnd; + int nLen, c; + sxu32 n; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the target string */ + zIn = jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + /* Extract the flags if available */ + if( nArg > 1 ){ + iFlags = jx9_value_to_int(apArg[1]); + if( iFlags < 0 ){ + iFlags = 0x01; + } + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + /* No more input to process */ + break; + } + c = zIn[0]; + /* Perform a linear lookup on the decoding table */ + for( n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){ + if( azHtmlEscape[n+1][0] == c ){ + /* Got one */ + break; + } + } + if( n < SX_ARRAYSIZE(azHtmlEscape) ){ + /* Output the safe sequence [i.e: '<' ==> '<"] */ + if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){ + /* Expand the double quote verbatim */ + jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); + }else if(c == '\'' && ((iFlags & 0x02 /*ENT_QUOTES*/) == 0 || (iFlags & 0x04) /*ENT_NOQUOTES*/) ){ + /* expand single quote verbatim */ + jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); + }else{ + jx9_result_string(pCtx, azHtmlEscape[n], -1/*Compute length automatically */); + } + }else{ + /* Output character verbatim */ + jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); + } + zIn++; + } + return JX9_OK; +} +/* + * string html_entity_decode(string $string [, int $quote_style = ENT_COMPAT [, string $charset = 'UTF-8' ]]) + * Perform the reverse operation of html_entity_decode(). + * Parameters + * $string + * The input string. + * $flags + * A bitmask of one or more of the flags (see comment on jx9Builtin_htmlspecialchars()) + * Return + * The decoded string. + */ +static int jx9Builtin_html_entity_decode(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zCur, *zIn, *zEnd; + int iFlags = 0x01; /* ENT_COMPAT */ + int nLen; + sxu32 n; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the target string */ + zIn = jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + /* Extract the flags if available */ + if( nArg > 1 ){ + iFlags = jx9_value_to_int(apArg[1]); + if( iFlags < 0 ){ + iFlags = 0x01; + } + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + /* No more input to process */ + break; + } + zCur = zIn; + while( zIn < zEnd && zIn[0] != '&' ){ + zIn++; + } + if( zCur < zIn ){ + /* Append raw string verbatim */ + jx9_result_string(pCtx, zCur, (int)(zIn-zCur)); + } + if( zIn >= zEnd ){ + break; + } + nLen = (int)(zEnd-zIn); + /* Find an encoded sequence */ + for(n = 0 ; n < SX_ARRAYSIZE(azHtmlEscape) ; n += 2 ){ + int iLen = (int)SyStrlen(azHtmlEscape[n]); + if( nLen >= iLen && SyStrnicmp(zIn, azHtmlEscape[n], (sxu32)iLen) == 0 ){ + /* Got one */ + zIn += iLen; + break; + } + } + if( n < SX_ARRAYSIZE(azHtmlEscape) ){ + int c = azHtmlEscape[n+1][0]; + /* Output the decoded character */ + if( c == '\'' && ((iFlags & 0x02) == 0 /*ENT_QUOTES*/|| (iFlags & 0x04) /*ENT_NOQUOTES*/) ){ + /* Do not process single quotes */ + jx9_result_string(pCtx, azHtmlEscape[n], -1); + }else if( c == '"' && (iFlags & 0x04) /*ENT_NOQUOTES*/ ){ + /* Do not process double quotes */ + jx9_result_string(pCtx, azHtmlEscape[n], -1); + }else{ + jx9_result_string(pCtx, azHtmlEscape[n+1], -1); /* Compute length automatically */ + } + }else{ + /* Append '&' */ + jx9_result_string(pCtx, "&", (int)sizeof(char)); + zIn++; + } + } + return JX9_OK; +} +/* + * int strlen($string) + * return the length of the given string. + * Parameter + * string: The string being measured for length. + * Return + * length of the given string. + */ +static int jx9Builtin_strlen(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int iLen = 0; + if( nArg > 0 ){ + jx9_value_to_string(apArg[0], &iLen); + } + /* String length */ + jx9_result_int(pCtx, iLen); + return JX9_OK; +} +/* + * int strcmp(string $str1, string $str2) + * Perform a binary safe string comparison. + * Parameter + * str1: The first string + * str2: The second string + * Return + * Returns < 0 if str1 is less than str2; > 0 if str1 is greater + * than str2, and 0 if they are equal. + */ +static int jx9Builtin_strcmp(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *z1, *z2; + int n1, n2; + int res; + if( nArg < 2 ){ + res = nArg == 0 ? 0 : 1; + jx9_result_int(pCtx, res); + return JX9_OK; + } + /* Perform the comparison */ + z1 = jx9_value_to_string(apArg[0], &n1); + z2 = jx9_value_to_string(apArg[1], &n2); + res = SyStrncmp(z1, z2, (sxu32)(SXMAX(n1, n2))); + /* Comparison result */ + jx9_result_int(pCtx, res); + return JX9_OK; +} +/* + * int strncmp(string $str1, string $str2, int n) + * Perform a binary safe string comparison of the first n characters. + * Parameter + * str1: The first string + * str2: The second string + * Return + * Returns < 0 if str1 is less than str2; > 0 if str1 is greater + * than str2, and 0 if they are equal. + */ +static int jx9Builtin_strncmp(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *z1, *z2; + int res; + int n; + if( nArg < 3 ){ + /* Perform a standard comparison */ + return jx9Builtin_strcmp(pCtx, nArg, apArg); + } + /* Desired comparison length */ + n = jx9_value_to_int(apArg[2]); + if( n < 0 ){ + /* Invalid length */ + jx9_result_int(pCtx, -1); + return JX9_OK; + } + /* Perform the comparison */ + z1 = jx9_value_to_string(apArg[0], 0); + z2 = jx9_value_to_string(apArg[1], 0); + res = SyStrncmp(z1, z2, (sxu32)n); + /* Comparison result */ + jx9_result_int(pCtx, res); + return JX9_OK; +} +/* + * int strcasecmp(string $str1, string $str2, int n) + * Perform a binary safe case-insensitive string comparison. + * Parameter + * str1: The first string + * str2: The second string + * Return + * Returns < 0 if str1 is less than str2; > 0 if str1 is greater + * than str2, and 0 if they are equal. + */ +static int jx9Builtin_strcasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *z1, *z2; + int n1, n2; + int res; + if( nArg < 2 ){ + res = nArg == 0 ? 0 : 1; + jx9_result_int(pCtx, res); + return JX9_OK; + } + /* Perform the comparison */ + z1 = jx9_value_to_string(apArg[0], &n1); + z2 = jx9_value_to_string(apArg[1], &n2); + res = SyStrnicmp(z1, z2, (sxu32)(SXMAX(n1, n2))); + /* Comparison result */ + jx9_result_int(pCtx, res); + return JX9_OK; +} +/* + * int strncasecmp(string $str1, string $str2, int n) + * Perform a binary safe case-insensitive string comparison of the first n characters. + * Parameter + * $str1: The first string + * $str2: The second string + * $len: The length of strings to be used in the comparison. + * Return + * Returns < 0 if str1 is less than str2; > 0 if str1 is greater + * than str2, and 0 if they are equal. + */ +static int jx9Builtin_strncasecmp(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *z1, *z2; + int res; + int n; + if( nArg < 3 ){ + /* Perform a standard comparison */ + return jx9Builtin_strcasecmp(pCtx, nArg, apArg); + } + /* Desired comparison length */ + n = jx9_value_to_int(apArg[2]); + if( n < 0 ){ + /* Invalid length */ + jx9_result_int(pCtx, -1); + return JX9_OK; + } + /* Perform the comparison */ + z1 = jx9_value_to_string(apArg[0], 0); + z2 = jx9_value_to_string(apArg[1], 0); + res = SyStrnicmp(z1, z2, (sxu32)n); + /* Comparison result */ + jx9_result_int(pCtx, res); + return JX9_OK; +} +/* + * Implode context [i.e: it's private data]. + * A pointer to the following structure is forwarded + * verbatim to the array walker callback defined below. + */ +struct implode_data { + jx9_context *pCtx; /* Call context */ + int bRecursive; /* TRUE if recursive implode [this is a symisc eXtension] */ + const char *zSep; /* Arguments separator if any */ + int nSeplen; /* Separator length */ + int bFirst; /* TRUE if first call */ + int nRecCount; /* Recursion count to avoid infinite loop */ +}; +/* + * Implode walker callback for the [jx9_array_walk()] interface. + * The following routine is invoked for each array entry passed + * to the implode() function. + */ +static int implode_callback(jx9_value *pKey, jx9_value *pValue, void *pUserData) +{ + struct implode_data *pData = (struct implode_data *)pUserData; + const char *zData; + int nLen; + if( pData->bRecursive && jx9_value_is_json_array(pValue) && pData->nRecCount < 32 ){ + if( pData->nSeplen > 0 ){ + if( !pData->bFirst ){ + /* append the separator first */ + jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen); + }else{ + pData->bFirst = 0; + } + } + /* Recurse */ + pData->bFirst = 1; + pData->nRecCount++; + jx9HashmapWalk((jx9_hashmap *)pValue->x.pOther, implode_callback, pData); + pData->nRecCount--; + return JX9_OK; + } + /* Extract the string representation of the entry value */ + zData = jx9_value_to_string(pValue, &nLen); + if( nLen > 0 ){ + if( pData->nSeplen > 0 ){ + if( !pData->bFirst ){ + /* append the separator first */ + jx9_result_string(pData->pCtx, pData->zSep, pData->nSeplen); + }else{ + pData->bFirst = 0; + } + } + jx9_result_string(pData->pCtx, zData, nLen); + }else{ + SXUNUSED(pKey); /* cc warning */ + } + return JX9_OK; +} +/* + * string implode(string $glue, array $pieces, ...) + * string implode(array $pieces, ...) + * Join array elements with a string. + * $glue + * Defaults to an empty string. This is not the preferred usage of implode() as glue + * would be the second parameter and thus, the bad prototype would be used. + * $pieces + * The array of strings to implode. + * Return + * Returns a string containing a string representation of all the array elements in the same + * order, with the glue string between each element. + */ +static int jx9Builtin_implode(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + struct implode_data imp_data; + int i = 1; + if( nArg < 1 ){ + /* Missing argument, return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Prepare the implode context */ + imp_data.pCtx = pCtx; + imp_data.bRecursive = 0; + imp_data.bFirst = 1; + imp_data.nRecCount = 0; + if( !jx9_value_is_json_array(apArg[0]) ){ + imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen); + }else{ + imp_data.zSep = 0; + imp_data.nSeplen = 0; + i = 0; + } + jx9_result_string(pCtx, "", 0); /* Set an empty stirng */ + /* Start the 'join' process */ + while( i < nArg ){ + if( jx9_value_is_json_array(apArg[i]) ){ + /* Iterate throw array entries */ + jx9_array_walk(apArg[i], implode_callback, &imp_data); + }else{ + const char *zData; + int nLen; + /* Extract the string representation of the jx9 value */ + zData = jx9_value_to_string(apArg[i], &nLen); + if( nLen > 0 ){ + if( imp_data.nSeplen > 0 ){ + if( !imp_data.bFirst ){ + /* append the separator first */ + jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen); + }else{ + imp_data.bFirst = 0; + } + } + jx9_result_string(pCtx, zData, nLen); + } + } + i++; + } + return JX9_OK; +} +/* + * string implode_recursive(string $glue, array $pieces, ...) + * Purpose + * Same as implode() but recurse on arrays. + * Example: + * $a = array('usr', array('home', 'dean')); + * print implode_recursive("/", $a); + * Will output + * usr/home/dean. + * While the standard implode would produce. + * usr/Array. + * Parameter + * Refer to implode(). + * Return + * Refer to implode(). + */ +static int jx9Builtin_implode_recursive(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + struct implode_data imp_data; + int i = 1; + if( nArg < 1 ){ + /* Missing argument, return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Prepare the implode context */ + imp_data.pCtx = pCtx; + imp_data.bRecursive = 1; + imp_data.bFirst = 1; + imp_data.nRecCount = 0; + if( !jx9_value_is_json_array(apArg[0]) ){ + imp_data.zSep = jx9_value_to_string(apArg[0], &imp_data.nSeplen); + }else{ + imp_data.zSep = 0; + imp_data.nSeplen = 0; + i = 0; + } + jx9_result_string(pCtx, "", 0); /* Set an empty stirng */ + /* Start the 'join' process */ + while( i < nArg ){ + if( jx9_value_is_json_array(apArg[i]) ){ + /* Iterate throw array entries */ + jx9_array_walk(apArg[i], implode_callback, &imp_data); + }else{ + const char *zData; + int nLen; + /* Extract the string representation of the jx9 value */ + zData = jx9_value_to_string(apArg[i], &nLen); + if( nLen > 0 ){ + if( imp_data.nSeplen > 0 ){ + if( !imp_data.bFirst ){ + /* append the separator first */ + jx9_result_string(pCtx, imp_data.zSep, imp_data.nSeplen); + }else{ + imp_data.bFirst = 0; + } + } + jx9_result_string(pCtx, zData, nLen); + } + } + i++; + } + return JX9_OK; +} +/* + * array explode(string $delimiter, string $string[, int $limit ]) + * Returns an array of strings, each of which is a substring of string + * formed by splitting it on boundaries formed by the string delimiter. + * Parameters + * $delimiter + * The boundary string. + * $string + * The input string. + * $limit + * If limit is set and positive, the returned array will contain a maximum + * of limit elements with the last element containing the rest of string. + * If the limit parameter is negative, all fields except the last -limit are returned. + * If the limit parameter is zero, then this is treated as 1. + * Returns + * Returns an array of strings created by splitting the string parameter + * on boundaries formed by the delimiter. + * If delimiter is an empty string (""), explode() will return FALSE. + * If delimiter contains a value that is not contained in string and a negative + * limit is used, then an empty array will be returned, otherwise an array containing string + * will be returned. + * NOTE: + * Negative limit is not supported. + */ +static int jx9Builtin_explode(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zDelim, *zString, *zCur, *zEnd; + int nDelim, nStrlen, iLimit; + jx9_value *pArray; + jx9_value *pValue; + sxu32 nOfft; + sxi32 rc; + if( nArg < 2 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the delimiter */ + zDelim = jx9_value_to_string(apArg[0], &nDelim); + if( nDelim < 1 ){ + /* Empty delimiter, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the string */ + zString = jx9_value_to_string(apArg[1], &nStrlen); + if( nStrlen < 1 ){ + /* Empty delimiter, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the end of the string */ + zEnd = &zString[nStrlen]; + /* Create the array */ + pArray = jx9_context_new_array(pCtx); + pValue = jx9_context_new_scalar(pCtx); + if( pArray == 0 || pValue == 0 ){ + /* Out of memory, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Set a defualt limit */ + iLimit = SXI32_HIGH; + if( nArg > 2 ){ + iLimit = jx9_value_to_int(apArg[2]); + if( iLimit < 0 ){ + iLimit = -iLimit; + } + if( iLimit == 0 ){ + iLimit = 1; + } + iLimit--; + } + /* Start exploding */ + for(;;){ + if( zString >= zEnd ){ + /* No more entry to process */ + break; + } + rc = SyBlobSearch(zString, (sxu32)(zEnd-zString), zDelim, nDelim, &nOfft); + if( rc != SXRET_OK || iLimit <= (int)jx9_array_count(pArray) ){ + /* Limit reached, insert the rest of the string and break */ + if( zEnd > zString ){ + jx9_value_string(pValue, zString, (int)(zEnd-zString)); + jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue); + } + break; + } + /* Point to the desired offset */ + zCur = &zString[nOfft]; + if( zCur > zString ){ + /* Perform the store operation */ + jx9_value_string(pValue, zString, (int)(zCur-zString)); + jx9_array_add_elem(pArray, 0/* Automatic index assign*/, pValue); + } + /* Point beyond the delimiter */ + zString = &zCur[nDelim]; + /* Reset the cursor */ + jx9_value_reset_string_cursor(pValue); + } + /* Return the freshly created array */ + jx9_result_value(pCtx, pArray); + /* NOTE that every allocated jx9_value will be automatically + * released as soon we return from this foregin function. + */ + return JX9_OK; +} +/* + * string trim(string $str[, string $charlist ]) + * Strip whitespace (or other characters) from the beginning and end of a string. + * Parameters + * $str + * The string that will be trimmed. + * $charlist + * Optionally, the stripped characters can also be specified using the charlist parameter. + * Simply list all characters that you want to be stripped. + * With .. you can specify a range of characters. + * Returns. + * Thr processed string. + */ +static int jx9Builtin_trim(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return null */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the target string */ + zString = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string, return */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Start the trim process */ + if( nArg < 2 ){ + SyString sStr; + /* Remove white spaces and NUL bytes */ + SyStringInitFromBuf(&sStr, zString, nLen); + SyStringFullTrimSafe(&sStr); + jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte); + }else{ + /* Char list */ + const char *zList; + int nListlen; + zList = jx9_value_to_string(apArg[1], &nListlen); + if( nListlen < 1 ){ + /* Return the string unchanged */ + jx9_result_string(pCtx, zString, nLen); + }else{ + const char *zEnd = &zString[nLen]; + const char *zCur = zString; + const char *zPtr; + int i; + /* Left trim */ + for(;;){ + if( zCur >= zEnd ){ + break; + } + zPtr = zCur; + for( i = 0 ; i < nListlen ; i++ ){ + if( zCur < zEnd && zCur[0] == zList[i] ){ + zCur++; + } + } + if( zCur == zPtr ){ + /* No match, break immediately */ + break; + } + } + /* Right trim */ + zEnd--; + for(;;){ + if( zEnd <= zCur ){ + break; + } + zPtr = zEnd; + for( i = 0 ; i < nListlen ; i++ ){ + if( zEnd > zCur && zEnd[0] == zList[i] ){ + zEnd--; + } + } + if( zEnd == zPtr ){ + break; + } + } + if( zCur >= zEnd ){ + /* Return the empty string */ + jx9_result_string(pCtx, "", 0); + }else{ + zEnd++; + jx9_result_string(pCtx, zCur, (int)(zEnd-zCur)); + } + } + } + return JX9_OK; +} +/* + * string rtrim(string $str[, string $charlist ]) + * Strip whitespace (or other characters) from the end of a string. + * Parameters + * $str + * The string that will be trimmed. + * $charlist + * Optionally, the stripped characters can also be specified using the charlist parameter. + * Simply list all characters that you want to be stripped. + * With .. you can specify a range of characters. + * Returns. + * Thr processed string. + */ +static int jx9Builtin_rtrim(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return null */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the target string */ + zString = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string, return */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Start the trim process */ + if( nArg < 2 ){ + SyString sStr; + /* Remove white spaces and NUL bytes*/ + SyStringInitFromBuf(&sStr, zString, nLen); + SyStringRightTrimSafe(&sStr); + jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte); + }else{ + /* Char list */ + const char *zList; + int nListlen; + zList = jx9_value_to_string(apArg[1], &nListlen); + if( nListlen < 1 ){ + /* Return the string unchanged */ + jx9_result_string(pCtx, zString, nLen); + }else{ + const char *zEnd = &zString[nLen - 1]; + const char *zCur = zString; + const char *zPtr; + int i; + /* Right trim */ + for(;;){ + if( zEnd <= zCur ){ + break; + } + zPtr = zEnd; + for( i = 0 ; i < nListlen ; i++ ){ + if( zEnd > zCur && zEnd[0] == zList[i] ){ + zEnd--; + } + } + if( zEnd == zPtr ){ + break; + } + } + if( zEnd <= zCur ){ + /* Return the empty string */ + jx9_result_string(pCtx, "", 0); + }else{ + zEnd++; + jx9_result_string(pCtx, zCur, (int)(zEnd-zCur)); + } + } + } + return JX9_OK; +} +/* + * string ltrim(string $str[, string $charlist ]) + * Strip whitespace (or other characters) from the beginning and end of a string. + * Parameters + * $str + * The string that will be trimmed. + * $charlist + * Optionally, the stripped characters can also be specified using the charlist parameter. + * Simply list all characters that you want to be stripped. + * With .. you can specify a range of characters. + * Returns. + * The processed string. + */ +static int jx9Builtin_ltrim(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return null */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the target string */ + zString = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string, return */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Start the trim process */ + if( nArg < 2 ){ + SyString sStr; + /* Remove white spaces and NUL byte */ + SyStringInitFromBuf(&sStr, zString, nLen); + SyStringLeftTrimSafe(&sStr); + jx9_result_string(pCtx, sStr.zString, (int)sStr.nByte); + }else{ + /* Char list */ + const char *zList; + int nListlen; + zList = jx9_value_to_string(apArg[1], &nListlen); + if( nListlen < 1 ){ + /* Return the string unchanged */ + jx9_result_string(pCtx, zString, nLen); + }else{ + const char *zEnd = &zString[nLen]; + const char *zCur = zString; + const char *zPtr; + int i; + /* Left trim */ + for(;;){ + if( zCur >= zEnd ){ + break; + } + zPtr = zCur; + for( i = 0 ; i < nListlen ; i++ ){ + if( zCur < zEnd && zCur[0] == zList[i] ){ + zCur++; + } + } + if( zCur == zPtr ){ + /* No match, break immediately */ + break; + } + } + if( zCur >= zEnd ){ + /* Return the empty string */ + jx9_result_string(pCtx, "", 0); + }else{ + jx9_result_string(pCtx, zCur, (int)(zEnd-zCur)); + } + } + } + return JX9_OK; +} +/* + * string strtolower(string $str) + * Make a string lowercase. + * Parameters + * $str + * The input string. + * Returns. + * The lowercased string. + */ +static int jx9Builtin_strtolower(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString, *zCur, *zEnd; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return null */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the target string */ + zString = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string, return */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Perform the requested operation */ + zEnd = &zString[nLen]; + for(;;){ + if( zString >= zEnd ){ + /* No more input, break immediately */ + break; + } + if( (unsigned char)zString[0] >= 0xc0 ){ + /* UTF-8 stream, output verbatim */ + zCur = zString; + zString++; + while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){ + zString++; + } + /* Append UTF-8 stream */ + jx9_result_string(pCtx, zCur, (int)(zString-zCur)); + }else{ + int c = zString[0]; + if( SyisUpper(c) ){ + c = SyToLower(zString[0]); + } + /* Append character */ + jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); + /* Advance the cursor */ + zString++; + } + } + return JX9_OK; +} +/* + * string strtolower(string $str) + * Make a string uppercase. + * Parameters + * $str + * The input string. + * Returns. + * The uppercased string. + */ +static int jx9Builtin_strtoupper(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString, *zCur, *zEnd; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return null */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the target string */ + zString = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string, return */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Perform the requested operation */ + zEnd = &zString[nLen]; + for(;;){ + if( zString >= zEnd ){ + /* No more input, break immediately */ + break; + } + if( (unsigned char)zString[0] >= 0xc0 ){ + /* UTF-8 stream, output verbatim */ + zCur = zString; + zString++; + while( zString < zEnd && ((unsigned char)zString[0] & 0xc0) == 0x80){ + zString++; + } + /* Append UTF-8 stream */ + jx9_result_string(pCtx, zCur, (int)(zString-zCur)); + }else{ + int c = zString[0]; + if( SyisLower(c) ){ + c = SyToUpper(zString[0]); + } + /* Append character */ + jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); + /* Advance the cursor */ + zString++; + } + } + return JX9_OK; +} +/* + * int ord(string $string) + * Returns the ASCII value of the first character of string. + * Parameters + * $str + * The input string. + * Returns. + * The ASCII value as an integer. + */ +static int jx9Builtin_ord(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString; + int nLen, c; + if( nArg < 1 ){ + /* Missing arguments, return -1 */ + jx9_result_int(pCtx, -1); + return JX9_OK; + } + /* Extract the target string */ + zString = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string, return -1 */ + jx9_result_int(pCtx, -1); + return JX9_OK; + } + /* Extract the ASCII value of the first character */ + c = zString[0]; + /* Return that value */ + jx9_result_int(pCtx, c); + return JX9_OK; +} +/* + * string chr(int $ascii) + * Returns a one-character string containing the character specified by ascii. + * Parameters + * $ascii + * The ascii code. + * Returns. + * The specified character. + */ +static int jx9Builtin_chr(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int c; + if( nArg < 1 ){ + /* Missing arguments, return null */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the ASCII value */ + c = jx9_value_to_int(apArg[0]); + /* Return the specified character */ + jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); + return JX9_OK; +} +/* + * Binary to hex consumer callback. + * This callback is the default consumer used by the hash functions + * [i.e: bin2hex(), md5(), sha1(), md5_file() ... ] defined below. + */ +static int HashConsumer(const void *pData, unsigned int nLen, void *pUserData) +{ + /* Append hex chunk verbatim */ + jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen); + return SXRET_OK; +} +/* + * string bin2hex(string $str) + * Convert binary data into hexadecimal representation. + * Parameters + * $str + * The input string. + * Returns. + * Returns the hexadecimal representation of the given string. + */ +static int jx9Builtin_bin2hex(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return null */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the target string */ + zString = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string, return */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Perform the requested operation */ + SyBinToHexConsumer((const void *)zString, (sxu32)nLen, HashConsumer, pCtx); + return JX9_OK; +} +/* Search callback signature */ +typedef sxi32 (*ProcStringMatch)(const void *, sxu32, const void *, sxu32, sxu32 *); +/* + * Case-insensitive pattern match. + * Brute force is the default search method used here. + * This is due to the fact that brute-forcing works quite + * well for short/medium texts on modern hardware. + */ +static sxi32 iPatternMatch(const void *pText, sxu32 nLen, const void *pPattern, sxu32 iPatLen, sxu32 *pOfft) +{ + const char *zpIn = (const char *)pPattern; + const char *zIn = (const char *)pText; + const char *zpEnd = &zpIn[iPatLen]; + const char *zEnd = &zIn[nLen]; + const char *zPtr, *zPtr2; + int c, d; + if( iPatLen > nLen ){ + /* Don't bother processing */ + return SXERR_NOTFOUND; + } + for(;;){ + if( zIn >= zEnd ){ + break; + } + c = SyToLower(zIn[0]); + d = SyToLower(zpIn[0]); + if( c == d ){ + zPtr = &zIn[1]; + zPtr2 = &zpIn[1]; + for(;;){ + if( zPtr2 >= zpEnd ){ + /* Pattern found */ + if( pOfft ){ *pOfft = (sxu32)(zIn-(const char *)pText); } + return SXRET_OK; + } + if( zPtr >= zEnd ){ + break; + } + c = SyToLower(zPtr[0]); + d = SyToLower(zPtr2[0]); + if( c != d ){ + break; + } + zPtr++; zPtr2++; + } + } + zIn++; + } + /* Pattern not found */ + return SXERR_NOTFOUND; +} +/* + * string strstr(string $haystack, string $needle[, bool $before_needle = false ]) + * Find the first occurrence of a string. + * Parameters + * $haystack + * The input string. + * $needle + * Search pattern (must be a string). + * $before_needle + * If TRUE, strstr() returns the part of the haystack before the first occurrence + * of the needle (excluding the needle). + * Return + * Returns the portion of string, or FALSE if needle is not found. + */ +static int jx9Builtin_strstr(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */ + const char *zBlob, *zPattern; + int nLen, nPatLen; + sxu32 nOfft; + sxi32 rc; + if( nArg < 2 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the needle and the haystack */ + zBlob = jx9_value_to_string(apArg[0], &nLen); + zPattern = jx9_value_to_string(apArg[1], &nPatLen); + nOfft = 0; /* cc warning */ + if( nLen > 0 && nPatLen > 0 ){ + int before = 0; + /* Perform the lookup */ + rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft); + if( rc != SXRET_OK ){ + /* Pattern not found, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Return the portion of the string */ + if( nArg > 2 ){ + before = jx9_value_to_int(apArg[2]); + } + if( before ){ + jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob)); + }else{ + jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft])); + } + }else{ + jx9_result_bool(pCtx, 0); + } + return JX9_OK; +} +/* + * string stristr(string $haystack, string $needle[, bool $before_needle = false ]) + * Case-insensitive strstr(). + * Parameters + * $haystack + * The input string. + * $needle + * Search pattern (must be a string). + * $before_needle + * If TRUE, strstr() returns the part of the haystack before the first occurrence + * of the needle (excluding the needle). + * Return + * Returns the portion of string, or FALSE if needle is not found. + */ +static int jx9Builtin_stristr(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */ + const char *zBlob, *zPattern; + int nLen, nPatLen; + sxu32 nOfft; + sxi32 rc; + if( nArg < 2 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the needle and the haystack */ + zBlob = jx9_value_to_string(apArg[0], &nLen); + zPattern = jx9_value_to_string(apArg[1], &nPatLen); + nOfft = 0; /* cc warning */ + if( nLen > 0 && nPatLen > 0 ){ + int before = 0; + /* Perform the lookup */ + rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft); + if( rc != SXRET_OK ){ + /* Pattern not found, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Return the portion of the string */ + if( nArg > 2 ){ + before = jx9_value_to_int(apArg[2]); + } + if( before ){ + jx9_result_string(pCtx, zBlob, (int)(&zBlob[nOfft]-zBlob)); + }else{ + jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft])); + } + }else{ + jx9_result_bool(pCtx, 0); + } + return JX9_OK; +} +/* + * int strpos(string $haystack, string $needle [, int $offset = 0 ] ) + * Returns the numeric position of the first occurrence of needle in the haystack string. + * Parameters + * $haystack + * The input string. + * $needle + * Search pattern (must be a string). + * $offset + * This optional offset parameter allows you to specify which character in haystack + * to start searching. The position returned is still relative to the beginning + * of haystack. + * Return + * Returns the position as an integer.If needle is not found, strpos() will return FALSE. + */ +static int jx9Builtin_strpos(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */ + const char *zBlob, *zPattern; + int nLen, nPatLen, nStart; + sxu32 nOfft; + sxi32 rc; + if( nArg < 2 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the needle and the haystack */ + zBlob = jx9_value_to_string(apArg[0], &nLen); + zPattern = jx9_value_to_string(apArg[1], &nPatLen); + nOfft = 0; /* cc warning */ + nStart = 0; + /* Peek the starting offset if available */ + if( nArg > 2 ){ + nStart = jx9_value_to_int(apArg[2]); + if( nStart < 0 ){ + nStart = -nStart; + } + if( nStart >= nLen ){ + /* Invalid offset */ + nStart = 0; + }else{ + zBlob += nStart; + nLen -= nStart; + } + } + if( nLen > 0 && nPatLen > 0 ){ + /* Perform the lookup */ + rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft); + if( rc != SXRET_OK ){ + /* Pattern not found, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Return the pattern position */ + jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart)); + }else{ + jx9_result_bool(pCtx, 0); + } + return JX9_OK; +} +/* + * int stripos(string $haystack, string $needle [, int $offset = 0 ] ) + * Case-insensitive strpos. + * Parameters + * $haystack + * The input string. + * $needle + * Search pattern (must be a string). + * $offset + * This optional offset parameter allows you to specify which character in haystack + * to start searching. The position returned is still relative to the beginning + * of haystack. + * Return + * Returns the position as an integer.If needle is not found, strpos() will return FALSE. + */ +static int jx9Builtin_stripos(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */ + const char *zBlob, *zPattern; + int nLen, nPatLen, nStart; + sxu32 nOfft; + sxi32 rc; + if( nArg < 2 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the needle and the haystack */ + zBlob = jx9_value_to_string(apArg[0], &nLen); + zPattern = jx9_value_to_string(apArg[1], &nPatLen); + nOfft = 0; /* cc warning */ + nStart = 0; + /* Peek the starting offset if available */ + if( nArg > 2 ){ + nStart = jx9_value_to_int(apArg[2]); + if( nStart < 0 ){ + nStart = -nStart; + } + if( nStart >= nLen ){ + /* Invalid offset */ + nStart = 0; + }else{ + zBlob += nStart; + nLen -= nStart; + } + } + if( nLen > 0 && nPatLen > 0 ){ + /* Perform the lookup */ + rc = xPatternMatch(zBlob, (sxu32)nLen, zPattern, (sxu32)nPatLen, &nOfft); + if( rc != SXRET_OK ){ + /* Pattern not found, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Return the pattern position */ + jx9_result_int64(pCtx, (jx9_int64)(nOfft+nStart)); + }else{ + jx9_result_bool(pCtx, 0); + } + return JX9_OK; +} +/* + * int strrpos(string $haystack, string $needle [, int $offset = 0 ] ) + * Find the numeric position of the last occurrence of needle in the haystack string. + * Parameters + * $haystack + * The input string. + * $needle + * Search pattern (must be a string). + * $offset + * If specified, search will start this number of characters counted from the beginning + * of the string. If the value is negative, search will instead start from that many + * characters from the end of the string, searching backwards. + * Return + * Returns the position as an integer.If needle is not found, strrpos() will return FALSE. + */ +static int jx9Builtin_strrpos(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd; + ProcStringMatch xPatternMatch = SyBlobSearch; /* Case-sensitive pattern match */ + int nLen, nPatLen; + sxu32 nOfft; + sxi32 rc; + if( nArg < 2 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the needle and the haystack */ + zBlob = jx9_value_to_string(apArg[0], &nLen); + zPattern = jx9_value_to_string(apArg[1], &nPatLen); + /* Point to the end of the pattern */ + zPtr = &zBlob[nLen - 1]; + zEnd = &zBlob[nLen]; + /* Save the starting posistion */ + zStart = zBlob; + nOfft = 0; /* cc warning */ + /* Peek the starting offset if available */ + if( nArg > 2 ){ + int nStart; + nStart = jx9_value_to_int(apArg[2]); + if( nStart < 0 ){ + nStart = -nStart; + if( nStart >= nLen ){ + /* Invalid offset */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + }else{ + nLen -= nStart; + zPtr = &zBlob[nLen - 1]; + zEnd = &zBlob[nLen]; + } + }else{ + if( nStart >= nLen ){ + /* Invalid offset */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + }else{ + zBlob += nStart; + nLen -= nStart; + } + } + } + if( nLen > 0 && nPatLen > 0 ){ + /* Perform the lookup */ + for(;;){ + if( zBlob >= zPtr ){ + break; + } + rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft); + if( rc == SXRET_OK ){ + /* Pattern found, return it's position */ + jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart)); + return JX9_OK; + } + zPtr--; + } + /* Pattern not found, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + jx9_result_bool(pCtx, 0); + } + return JX9_OK; +} +/* + * int strripos(string $haystack, string $needle [, int $offset = 0 ] ) + * Case-insensitive strrpos. + * Parameters + * $haystack + * The input string. + * $needle + * Search pattern (must be a string). + * $offset + * If specified, search will start this number of characters counted from the beginning + * of the string. If the value is negative, search will instead start from that many + * characters from the end of the string, searching backwards. + * Return + * Returns the position as an integer.If needle is not found, strrpos() will return FALSE. + */ +static int jx9Builtin_strripos(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zStart, *zBlob, *zPattern, *zPtr, *zEnd; + ProcStringMatch xPatternMatch = iPatternMatch; /* Case-insensitive pattern match */ + int nLen, nPatLen; + sxu32 nOfft; + sxi32 rc; + if( nArg < 2 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the needle and the haystack */ + zBlob = jx9_value_to_string(apArg[0], &nLen); + zPattern = jx9_value_to_string(apArg[1], &nPatLen); + /* Point to the end of the pattern */ + zPtr = &zBlob[nLen - 1]; + zEnd = &zBlob[nLen]; + /* Save the starting posistion */ + zStart = zBlob; + nOfft = 0; /* cc warning */ + /* Peek the starting offset if available */ + if( nArg > 2 ){ + int nStart; + nStart = jx9_value_to_int(apArg[2]); + if( nStart < 0 ){ + nStart = -nStart; + if( nStart >= nLen ){ + /* Invalid offset */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + }else{ + nLen -= nStart; + zPtr = &zBlob[nLen - 1]; + zEnd = &zBlob[nLen]; + } + }else{ + if( nStart >= nLen ){ + /* Invalid offset */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + }else{ + zBlob += nStart; + nLen -= nStart; + } + } + } + if( nLen > 0 && nPatLen > 0 ){ + /* Perform the lookup */ + for(;;){ + if( zBlob >= zPtr ){ + break; + } + rc = xPatternMatch((const void *)zPtr, (sxu32)(zEnd-zPtr), (const void *)zPattern, (sxu32)nPatLen, &nOfft); + if( rc == SXRET_OK ){ + /* Pattern found, return it's position */ + jx9_result_int64(pCtx, (jx9_int64)(&zPtr[nOfft] - zStart)); + return JX9_OK; + } + zPtr--; + } + /* Pattern not found, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + jx9_result_bool(pCtx, 0); + } + return JX9_OK; +} +/* + * int strrchr(string $haystack, mixed $needle) + * Find the last occurrence of a character in a string. + * Parameters + * $haystack + * The input string. + * $needle + * If needle contains more than one character, only the first is used. + * This behavior is different from that of strstr(). + * If needle is not a string, it is converted to an integer and applied + * as the ordinal value of a character. + * Return + * This function returns the portion of string, or FALSE if needle is not found. + */ +static int jx9Builtin_strrchr(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zBlob; + int nLen, c; + if( nArg < 2 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the haystack */ + zBlob = jx9_value_to_string(apArg[0], &nLen); + c = 0; /* cc warning */ + if( nLen > 0 ){ + sxu32 nOfft; + sxi32 rc; + if( jx9_value_is_string(apArg[1]) ){ + const char *zPattern; + zPattern = jx9_value_to_string(apArg[1], 0); /* Never fail, so there is no need to check + * for NULL pointer. + */ + c = zPattern[0]; + }else{ + /* Int cast */ + c = jx9_value_to_int(apArg[1]); + } + /* Perform the lookup */ + rc = SyByteFind2(zBlob, (sxu32)nLen, c, &nOfft); + if( rc != SXRET_OK ){ + /* No such entry, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Return the string portion */ + jx9_result_string(pCtx, &zBlob[nOfft], (int)(&zBlob[nLen]-&zBlob[nOfft])); + }else{ + jx9_result_bool(pCtx, 0); + } + return JX9_OK; +} +/* + * string strrev(string $string) + * Reverse a string. + * Parameters + * $string + * String to be reversed. + * Return + * The reversed string. + */ +static int jx9Builtin_strrev(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zIn, *zEnd; + int nLen, c; + if( nArg < 1 ){ + /* Missing arguments, return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the target string */ + zIn = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string Return null */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Perform the requested operation */ + zEnd = &zIn[nLen - 1]; + for(;;){ + if( zEnd < zIn ){ + /* No more input to process */ + break; + } + /* Append current character */ + c = zEnd[0]; + jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); + zEnd--; + } + return JX9_OK; +} +/* + * string str_repeat(string $input, int $multiplier) + * Returns input repeated multiplier times. + * Parameters + * $string + * String to be repeated. + * $multiplier + * Number of time the input string should be repeated. + * multiplier has to be greater than or equal to 0. If the multiplier is set + * to 0, the function will return an empty string. + * Return + * The repeated string. + */ +static int jx9Builtin_str_repeat(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zIn; + int nLen, nMul; + int rc; + if( nArg < 2 ){ + /* Missing arguments, return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the target string */ + zIn = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string.Return null */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the multiplier */ + nMul = jx9_value_to_int(apArg[1]); + if( nMul < 1 ){ + /* Return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + if( nMul < 1 ){ + break; + } + /* Append the copy */ + rc = jx9_result_string(pCtx, zIn, nLen); + if( rc != JX9_OK ){ + /* Out of memory, break immediately */ + break; + } + nMul--; + } + return JX9_OK; +} +/* + * string nl2br(string $string[, bool $is_xhtml = true ]) + * Inserts HTML line breaks before all newlines in a string. + * Parameters + * $string + * The input string. + * $is_xhtml + * Whenever to use XHTML compatible line breaks or not. + * Return + * The processed string. + */ +static int jx9Builtin_nl2br(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zIn, *zCur, *zEnd; + int is_xhtml = 0; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Extract the target string */ + zIn = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string, return null */ + jx9_result_null(pCtx); + return JX9_OK; + } + if( nArg > 1 ){ + is_xhtml = jx9_value_to_bool(apArg[1]); + } + zEnd = &zIn[nLen]; + /* Perform the requested operation */ + for(;;){ + zCur = zIn; + /* Delimit the string */ + while( zIn < zEnd && (zIn[0] != '\n'&& zIn[0] != '\r') ){ + zIn++; + } + if( zCur < zIn ){ + /* Output chunk verbatim */ + jx9_result_string(pCtx, zCur, (int)(zIn-zCur)); + } + if( zIn >= zEnd ){ + /* No more input to process */ + break; + } + /* Output the HTML line break */ + if( is_xhtml ){ + jx9_result_string(pCtx, "
", (int)sizeof("
")-1); + }else{ + jx9_result_string(pCtx, "
", (int)sizeof("
")-1); + } + zCur = zIn; + /* Append trailing line */ + while( zIn < zEnd && (zIn[0] == '\n' || zIn[0] == '\r') ){ + zIn++; + } + if( zCur < zIn ){ + /* Output chunk verbatim */ + jx9_result_string(pCtx, zCur, (int)(zIn-zCur)); + } + } + return JX9_OK; +} +/* + * Format a given string and invoke the given callback on each processed chunk. + * According to the JX9 reference manual. + * The format string is composed of zero or more directives: ordinary characters + * (excluding %) that are copied directly to the result, and conversion + * specifications, each of which results in fetching its own parameter. + * This applies to both sprintf() and printf(). + * Each conversion specification consists of a percent sign (%), followed by one + * or more of these elements, in order: + * An optional sign specifier that forces a sign (- or +) to be used on a number. + * By default, only the - sign is used on a number if it's negative. This specifier forces + * positive numbers to have the + sign attached as well. + * An optional padding specifier that says what character will be used for padding + * the results to the right string size. This may be a space character or a 0 (zero character). + * The default is to pad with spaces. An alternate padding character can be specified by prefixing + * it with a single quote ('). See the examples below. + * An optional alignment specifier that says if the result should be left-justified or right-justified. + * The default is right-justified; a - character here will make it left-justified. + * An optional number, a width specifier that says how many characters (minimum) this conversion + * should result in. + * An optional precision specifier in the form of a period (`.') followed by an optional decimal + * digit string that says how many decimal digits should be displayed for floating-point numbers. + * When using this specifier on a string, it acts as a cutoff point, setting a maximum character + * limit to the string. + * A type specifier that says what type the argument data should be treated as. Possible types: + * % - a literal percent character. No argument is required. + * b - the argument is treated as an integer, and presented as a binary number. + * c - the argument is treated as an integer, and presented as the character with that ASCII value. + * d - the argument is treated as an integer, and presented as a (signed) decimal number. + * e - the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands + * for the number of digits after the decimal point. + * E - like %e but uses uppercase letter (e.g. 1.2E+2). + * u - the argument is treated as an integer, and presented as an unsigned decimal number. + * f - the argument is treated as a float, and presented as a floating-point number (locale aware). + * F - the argument is treated as a float, and presented as a floating-point number (non-locale aware). + * g - shorter of %e and %f. + * G - shorter of %E and %f. + * o - the argument is treated as an integer, and presented as an octal number. + * s - the argument is treated as and presented as a string. + * x - the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters). + * X - the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters). + */ +/* + * This implementation is based on the one found in the SQLite3 source tree. + */ +#define JX9_FMT_BUFSIZ 1024 /* Conversion buffer size */ +/* +** Conversion types fall into various categories as defined by the +** following enumeration. +*/ +#define JX9_FMT_RADIX 1 /* Integer types.%d, %x, %o, and so forth */ +#define JX9_FMT_FLOAT 2 /* Floating point.%f */ +#define JX9_FMT_EXP 3 /* Exponentional notation.%e and %E */ +#define JX9_FMT_GENERIC 4 /* Floating or exponential, depending on exponent.%g */ +#define JX9_FMT_SIZE 5 /* Total number of characters processed so far.%n */ +#define JX9_FMT_STRING 6 /* Strings.%s */ +#define JX9_FMT_PERCENT 7 /* Percent symbol.%% */ +#define JX9_FMT_CHARX 8 /* Characters.%c */ +#define JX9_FMT_ERROR 9 /* Used to indicate no such conversion type */ +/* +** Allowed values for jx9_fmt_info.flags +*/ +#define JX9_FMT_FLAG_SIGNED 0x01 +#define JX9_FMT_FLAG_UNSIGNED 0x02 +/* +** Each builtin conversion character (ex: the 'd' in "%d") is described +** by an instance of the following structure +*/ +typedef struct jx9_fmt_info jx9_fmt_info; +struct jx9_fmt_info +{ + char fmttype; /* The format field code letter [i.e: 'd', 's', 'x'] */ + sxu8 base; /* The base for radix conversion */ + int flags; /* One or more of JX9_FMT_FLAG_ constants below */ + sxu8 type; /* Conversion paradigm */ + char *charset; /* The character set for conversion */ + char *prefix; /* Prefix on non-zero values in alt format */ +}; +#ifndef JX9_OMIT_FLOATING_POINT +/* +** "*val" is a double such that 0.1 <= *val < 10.0 +** Return the ascii code for the leading digit of *val, then +** multiply "*val" by 10.0 to renormalize. +** +** Example: +** input: *val = 3.14159 +** output: *val = 1.4159 function return = '3' +** +** The counter *cnt is incremented each time. After counter exceeds +** 16 (the number of significant digits in a 64-bit float) '0' is +** always returned. +*/ +static int vxGetdigit(sxlongreal *val, int *cnt) +{ + sxlongreal d; + int digit; + + if( (*cnt)++ >= 16 ){ + return '0'; + } + digit = (int)*val; + d = digit; + *val = (*val - d)*10.0; + return digit + '0' ; +} +#endif /* JX9_OMIT_FLOATING_POINT */ +/* + * The following table is searched linearly, so it is good to put the most frequently + * used conversion types first. + */ +static const jx9_fmt_info aFmt[] = { + { 'd', 10, JX9_FMT_FLAG_SIGNED, JX9_FMT_RADIX, "0123456789", 0 }, + { 's', 0, 0, JX9_FMT_STRING, 0, 0 }, + { 'c', 0, 0, JX9_FMT_CHARX, 0, 0 }, + { 'x', 16, 0, JX9_FMT_RADIX, "0123456789abcdef", "x0" }, + { 'X', 16, 0, JX9_FMT_RADIX, "0123456789ABCDEF", "X0" }, + { 'b', 2, 0, JX9_FMT_RADIX, "01", "b0"}, + { 'o', 8, 0, JX9_FMT_RADIX, "01234567", "0" }, + { 'u', 10, 0, JX9_FMT_RADIX, "0123456789", 0 }, + { 'f', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT, 0, 0 }, + { 'F', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_FLOAT, 0, 0 }, + { 'e', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP, "e", 0 }, + { 'E', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_EXP, "E", 0 }, + { 'g', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC, "e", 0 }, + { 'G', 0, JX9_FMT_FLAG_SIGNED, JX9_FMT_GENERIC, "E", 0 }, + { '%', 0, 0, JX9_FMT_PERCENT, 0, 0 } +}; +/* + * Format a given string. + * The root program. All variations call this core. + * INPUTS: + * xConsumer This is a pointer to a function taking four arguments + * 1. A pointer to the call context. + * 2. A pointer to the list of characters to be output + * (Note, this list is NOT null terminated.) + * 3. An integer number of characters to be output. + * (Note: This number might be zero.) + * 4. Upper layer private data. + * zIn This is the format string, as in the usual print. + * apArg This is a pointer to a list of arguments. + */ +JX9_PRIVATE sxi32 jx9InputFormat( + int (*xConsumer)(jx9_context *, const char *, int, void *), /* Format consumer */ + jx9_context *pCtx, /* call context */ + const char *zIn, /* Format string */ + int nByte, /* Format string length */ + int nArg, /* Total argument of the given arguments */ + jx9_value **apArg, /* User arguments */ + void *pUserData, /* Last argument to xConsumer() */ + int vf /* TRUE if called from vfprintf, vsprintf context */ + ) +{ + char spaces[] = " "; +#define etSPACESIZE ((int)sizeof(spaces)-1) + const char *zCur, *zEnd = &zIn[nByte]; + char *zBuf, zWorker[JX9_FMT_BUFSIZ]; /* Working buffer */ + const jx9_fmt_info *pInfo; /* Pointer to the appropriate info structure */ + int flag_alternateform; /* True if "#" flag is present */ + int flag_leftjustify; /* True if "-" flag is present */ + int flag_blanksign; /* True if " " flag is present */ + int flag_plussign; /* True if "+" flag is present */ + int flag_zeropad; /* True if field width constant starts with zero */ + jx9_value *pArg; /* Current processed argument */ + jx9_int64 iVal; + int precision; /* Precision of the current field */ + char *zExtra; + int c, rc, n; + int length; /* Length of the field */ + int prefix; + sxu8 xtype; /* Conversion paradigm */ + int width; /* Width of the current field */ + int idx; + n = (vf == TRUE) ? 0 : 1; +#define NEXT_ARG ( n < nArg ? apArg[n++] : 0 ) + /* Start the format process */ + for(;;){ + zCur = zIn; + while( zIn < zEnd && zIn[0] != '%' ){ + zIn++; + } + if( zCur < zIn ){ + /* Consume chunk verbatim */ + rc = xConsumer(pCtx, zCur, (int)(zIn-zCur), pUserData); + if( rc == SXERR_ABORT ){ + /* Callback request an operation abort */ + break; + } + } + if( zIn >= zEnd ){ + /* No more input to process, break immediately */ + break; + } + /* Find out what flags are present */ + flag_leftjustify = flag_plussign = flag_blanksign = + flag_alternateform = flag_zeropad = 0; + zIn++; /* Jump the precent sign */ + do{ + c = zIn[0]; + switch( c ){ + case '-': flag_leftjustify = 1; c = 0; break; + case '+': flag_plussign = 1; c = 0; break; + case ' ': flag_blanksign = 1; c = 0; break; + case '#': flag_alternateform = 1; c = 0; break; + case '0': flag_zeropad = 1; c = 0; break; + case '\'': + zIn++; + if( zIn < zEnd ){ + /* An alternate padding character can be specified by prefixing it with a single quote (') */ + c = zIn[0]; + for(idx = 0 ; idx < etSPACESIZE ; ++idx ){ + spaces[idx] = (char)c; + } + c = 0; + } + break; + default: break; + } + }while( c==0 && (zIn++ < zEnd) ); + /* Get the field width */ + width = 0; + while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){ + width = width*10 + (zIn[0] - '0'); + zIn++; + } + if( zIn < zEnd && zIn[0] == '$' ){ + /* Position specifer */ + if( width > 0 ){ + n = width; + if( vf && n > 0 ){ + n--; + } + } + zIn++; + width = 0; + if( zIn < zEnd && zIn[0] == '0' ){ + flag_zeropad = 1; + zIn++; + } + while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){ + width = width*10 + (zIn[0] - '0'); + zIn++; + } + } + if( width > JX9_FMT_BUFSIZ-10 ){ + width = JX9_FMT_BUFSIZ-10; + } + /* Get the precision */ + precision = -1; + if( zIn < zEnd && zIn[0] == '.' ){ + precision = 0; + zIn++; + while( zIn < zEnd && ( zIn[0] >='0' && zIn[0] <='9') ){ + precision = precision*10 + (zIn[0] - '0'); + zIn++; + } + } + if( zIn >= zEnd ){ + /* No more input */ + break; + } + /* Fetch the info entry for the field */ + pInfo = 0; + xtype = JX9_FMT_ERROR; + c = zIn[0]; + zIn++; /* Jump the format specifer */ + for(idx=0; idx< (int)SX_ARRAYSIZE(aFmt); idx++){ + if( c==aFmt[idx].fmttype ){ + pInfo = &aFmt[idx]; + xtype = pInfo->type; + break; + } + } + zBuf = zWorker; /* Point to the working buffer */ + length = 0; + zExtra = 0; + /* + ** At this point, variables are initialized as follows: + ** + ** flag_alternateform TRUE if a '#' is present. + ** flag_plussign TRUE if a '+' is present. + ** flag_leftjustify TRUE if a '-' is present or if the + ** field width was negative. + ** flag_zeropad TRUE if the width began with 0. + ** the conversion character. + ** flag_blanksign TRUE if a ' ' is present. + ** width The specified field width. This is + ** always non-negative. Zero is the default. + ** precision The specified precision. The default + ** is -1. + */ + switch(xtype){ + case JX9_FMT_PERCENT: + /* A literal percent character */ + zWorker[0] = '%'; + length = (int)sizeof(char); + break; + case JX9_FMT_CHARX: + /* The argument is treated as an integer, and presented as the character + * with that ASCII value + */ + pArg = NEXT_ARG; + if( pArg == 0 ){ + c = 0; + }else{ + c = jx9_value_to_int(pArg); + } + /* NUL byte is an acceptable value */ + zWorker[0] = (char)c; + length = (int)sizeof(char); + break; + case JX9_FMT_STRING: + /* the argument is treated as and presented as a string */ + pArg = NEXT_ARG; + if( pArg == 0 ){ + length = 0; + }else{ + zBuf = (char *)jx9_value_to_string(pArg, &length); + } + if( length < 1 ){ + zBuf = " "; + length = (int)sizeof(char); + } + if( precision>=0 && precisionJX9_FMT_BUFSIZ-40 ){ + precision = JX9_FMT_BUFSIZ-40; + } +#if 1 + /* For the format %#x, the value zero is printed "0" not "0x0". + ** I think this is stupid.*/ + if( iVal==0 ) flag_alternateform = 0; +#else + /* More sensible: turn off the prefix for octal (to prevent "00"), + ** but leave the prefix for hex.*/ + if( iVal==0 && pInfo->base==8 ) flag_alternateform = 0; +#endif + if( pInfo->flags & JX9_FMT_FLAG_SIGNED ){ + if( iVal<0 ){ + iVal = -iVal; + /* Ticket 1433-003 */ + if( iVal < 0 ){ + /* Overflow */ + iVal= 0x7FFFFFFFFFFFFFFF; + } + prefix = '-'; + }else if( flag_plussign ) prefix = '+'; + else if( flag_blanksign ) prefix = ' '; + else prefix = 0; + }else{ + if( iVal<0 ){ + iVal = -iVal; + /* Ticket 1433-003 */ + if( iVal < 0 ){ + /* Overflow */ + iVal= 0x7FFFFFFFFFFFFFFF; + } + } + prefix = 0; + } + if( flag_zeropad && precisioncharset; + base = pInfo->base; + do{ /* Convert to ascii */ + *(--zBuf) = cset[iVal%base]; + iVal = iVal/base; + }while( iVal>0 ); + } + length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf; + for(idx=precision-length; idx>0; idx--){ + *(--zBuf) = '0'; /* Zero pad */ + } + if( prefix ) *(--zBuf) = (char)prefix; /* Add sign */ + if( flag_alternateform && pInfo->prefix ){ /* Add "0" or "0x" */ + char *pre, x; + pre = pInfo->prefix; + if( *zBuf!=pre[0] ){ + for(pre=pInfo->prefix; (x=(*pre))!=0; pre++) *(--zBuf) = x; + } + } + length = &zWorker[JX9_FMT_BUFSIZ-1]-zBuf; + break; + case JX9_FMT_FLOAT: + case JX9_FMT_EXP: + case JX9_FMT_GENERIC:{ +#ifndef JX9_OMIT_FLOATING_POINT + long double realvalue; + int exp; /* exponent of real numbers */ + double rounder; /* Used for rounding floating point values */ + int flag_dp; /* True if decimal point should be shown */ + int flag_rtz; /* True if trailing zeros should be removed */ + int flag_exp; /* True to force display of the exponent */ + int nsd; /* Number of significant digits returned */ + pArg = NEXT_ARG; + if( pArg == 0 ){ + realvalue = 0; + }else{ + realvalue = jx9_value_to_double(pArg); + } + if( precision<0 ) precision = 6; /* Set default precision */ + if( precision>JX9_FMT_BUFSIZ-40) precision = JX9_FMT_BUFSIZ-40; + if( realvalue<0.0 ){ + realvalue = -realvalue; + prefix = '-'; + }else{ + if( flag_plussign ) prefix = '+'; + else if( flag_blanksign ) prefix = ' '; + else prefix = 0; + } + if( pInfo->type==JX9_FMT_GENERIC && precision>0 ) precision--; + rounder = 0.0; +#if 0 + /* Rounding works like BSD when the constant 0.4999 is used.Wierd! */ + for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1); +#else + /* It makes more sense to use 0.5 */ + for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1); +#endif + if( pInfo->type==JX9_FMT_FLOAT ) realvalue += rounder; + /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ + exp = 0; + if( realvalue>0.0 ){ + while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; } + while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; } + while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; } + while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; } + if( exp>350 || exp<-350 ){ + zBuf = "NaN"; + length = 3; + break; + } + } + zBuf = zWorker; + /* + ** If the field type is etGENERIC, then convert to either etEXP + ** or etFLOAT, as appropriate. + */ + flag_exp = xtype==JX9_FMT_EXP; + if( xtype!=JX9_FMT_FLOAT ){ + realvalue += rounder; + if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } + } + if( xtype==JX9_FMT_GENERIC ){ + flag_rtz = !flag_alternateform; + if( exp<-4 || exp>precision ){ + xtype = JX9_FMT_EXP; + }else{ + precision = precision - exp; + xtype = JX9_FMT_FLOAT; + } + }else{ + flag_rtz = 0; + } + /* + ** The "exp+precision" test causes output to be of type etEXP if + ** the precision is too large to fit in buf[]. + */ + nsd = 0; + if( xtype==JX9_FMT_FLOAT && exp+precision0 || flag_alternateform); + if( prefix ) *(zBuf++) = (char)prefix; /* Sign */ + if( exp<0 ) *(zBuf++) = '0'; /* Digits before "." */ + else for(; exp>=0; exp--) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd); + if( flag_dp ) *(zBuf++) = '.'; /* The decimal point */ + for(exp++; exp<0 && precision>0; precision--, exp++){ + *(zBuf++) = '0'; + } + while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd); + *(zBuf--) = 0; /* Null terminate */ + if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */ + while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0; + if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0; + } + zBuf++; /* point to next free slot */ + }else{ /* etEXP or etGENERIC */ + flag_dp = (precision>0 || flag_alternateform); + if( prefix ) *(zBuf++) = (char)prefix; /* Sign */ + *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd); /* First digit */ + if( flag_dp ) *(zBuf++) = '.'; /* Decimal point */ + while( (precision--)>0 ) *(zBuf++) = (char)vxGetdigit(&realvalue, &nsd); + zBuf--; /* point to last digit */ + if( flag_rtz && flag_dp ){ /* Remove tail zeros */ + while( zBuf>=zWorker && *zBuf=='0' ) *(zBuf--) = 0; + if( zBuf>=zWorker && *zBuf=='.' ) *(zBuf--) = 0; + } + zBuf++; /* point to next free slot */ + if( exp || flag_exp ){ + *(zBuf++) = pInfo->charset[0]; + if( exp<0 ){ *(zBuf++) = '-'; exp = -exp; } /* sign of exp */ + else { *(zBuf++) = '+'; } + if( exp>=100 ){ + *(zBuf++) = (char)((exp/100)+'0'); /* 100's digit */ + exp %= 100; + } + *(zBuf++) = (char)(exp/10+'0'); /* 10's digit */ + *(zBuf++) = (char)(exp%10+'0'); /* 1's digit */ + } + } + /* The converted number is in buf[] and zero terminated.Output it. + ** Note that the number is in the usual order, not reversed as with + ** integer conversions.*/ + length = (int)(zBuf-zWorker); + zBuf = zWorker; + /* Special case: Add leading zeros if the flag_zeropad flag is + ** set and we are not left justified */ + if( flag_zeropad && !flag_leftjustify && length < width){ + int i; + int nPad = width - length; + for(i=width; i>=nPad; i--){ + zBuf[i] = zBuf[i-nPad]; + } + i = prefix!=0; + while( nPad-- ) zBuf[i++] = '0'; + length = width; + } +#else + zBuf = " "; + length = (int)sizeof(char); +#endif /* JX9_OMIT_FLOATING_POINT */ + break; + } + default: + /* Invalid format specifer */ + zWorker[0] = '?'; + length = (int)sizeof(char); + break; + } + /* + ** The text of the conversion is pointed to by "zBuf" and is + ** "length" characters long.The field width is "width".Do + ** the output. + */ + if( !flag_leftjustify ){ + register int nspace; + nspace = width-length; + if( nspace>0 ){ + while( nspace>=etSPACESIZE ){ + rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData); + if( rc != SXRET_OK ){ + return SXERR_ABORT; /* Consumer routine request an operation abort */ + } + nspace -= etSPACESIZE; + } + if( nspace>0 ){ + rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData); + if( rc != SXRET_OK ){ + return SXERR_ABORT; /* Consumer routine request an operation abort */ + } + } + } + } + if( length>0 ){ + rc = xConsumer(pCtx, zBuf, (unsigned int)length, pUserData); + if( rc != SXRET_OK ){ + return SXERR_ABORT; /* Consumer routine request an operation abort */ + } + } + if( flag_leftjustify ){ + register int nspace; + nspace = width-length; + if( nspace>0 ){ + while( nspace>=etSPACESIZE ){ + rc = xConsumer(pCtx, spaces, etSPACESIZE, pUserData); + if( rc != SXRET_OK ){ + return SXERR_ABORT; /* Consumer routine request an operation abort */ + } + nspace -= etSPACESIZE; + } + if( nspace>0 ){ + rc = xConsumer(pCtx, spaces, (unsigned int)nspace, pUserData); + if( rc != SXRET_OK ){ + return SXERR_ABORT; /* Consumer routine request an operation abort */ + } + } + } + } + }/* for(;;) */ + return SXRET_OK; +} +/* + * Callback [i.e: Formatted input consumer] of the sprintf function. + */ +static int sprintfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData) +{ + /* Consume directly */ + jx9_result_string(pCtx, zInput, nLen); + SXUNUSED(pUserData); /* cc warning */ + return JX9_OK; +} +/* + * string sprintf(string $format[, mixed $args [, mixed $... ]]) + * Return a formatted string. + * Parameters + * $format + * The format string (see block comment above) + * Return + * A string produced according to the formatting string format. + */ +static int jx9Builtin_sprintf(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zFormat; + int nLen; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Extract the string format */ + zFormat = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Format the string */ + jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, nArg, apArg, 0, FALSE); + return JX9_OK; +} +/* + * Callback [i.e: Formatted input consumer] of the printf function. + */ +static int printfConsumer(jx9_context *pCtx, const char *zInput, int nLen, void *pUserData) +{ + jx9_int64 *pCounter = (jx9_int64 *)pUserData; + /* Call the VM output consumer directly */ + jx9_context_output(pCtx, zInput, nLen); + /* Increment counter */ + *pCounter += nLen; + return JX9_OK; +} +/* + * int64 printf(string $format[, mixed $args[, mixed $... ]]) + * Output a formatted string. + * Parameters + * $format + * See sprintf() for a description of format. + * Return + * The length of the outputted string. + */ +static int jx9Builtin_printf(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_int64 nCounter = 0; + const char *zFormat; + int nLen; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Extract the string format */ + zFormat = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Format the string */ + jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, nArg, apArg, (void *)&nCounter, FALSE); + /* Return the length of the outputted string */ + jx9_result_int64(pCtx, nCounter); + return JX9_OK; +} +/* + * int vprintf(string $format, array $args) + * Output a formatted string. + * Parameters + * $format + * See sprintf() for a description of format. + * Return + * The length of the outputted string. + */ +static int jx9Builtin_vprintf(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_int64 nCounter = 0; + const char *zFormat; + jx9_hashmap *pMap; + SySet sArg; + int nLen, n; + if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){ + /* Missing/Invalid arguments, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Extract the string format */ + zFormat = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Point to the hashmap */ + pMap = (jx9_hashmap *)apArg[1]->x.pOther; + /* Extract arguments from the hashmap */ + n = jx9HashmapValuesToSet(pMap, &sArg); + /* Format the string */ + jx9InputFormat(printfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), (void *)&nCounter, TRUE); + /* Return the length of the outputted string */ + jx9_result_int64(pCtx, nCounter); + /* Release the container */ + SySetRelease(&sArg); + return JX9_OK; +} +/* + * int vsprintf(string $format, array $args) + * Output a formatted string. + * Parameters + * $format + * See sprintf() for a description of format. + * Return + * A string produced according to the formatting string format. + */ +static int jx9Builtin_vsprintf(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zFormat; + jx9_hashmap *pMap; + SySet sArg; + int nLen, n; + if( nArg < 2 || !jx9_value_is_string(apArg[0]) || !jx9_value_is_json_array(apArg[1]) ){ + /* Missing/Invalid arguments, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Extract the string format */ + zFormat = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Point to hashmap */ + pMap = (jx9_hashmap *)apArg[1]->x.pOther; + /* Extract arguments from the hashmap */ + n = jx9HashmapValuesToSet(pMap, &sArg); + /* Format the string */ + jx9InputFormat(sprintfConsumer, pCtx, zFormat, nLen, n, (jx9_value **)SySetBasePtr(&sArg), 0, TRUE); + /* Release the container */ + SySetRelease(&sArg); + return JX9_OK; +} +/* + * string size_format(int64 $size) + * Return a smart string represenation of the given size [i.e: 64-bit integer] + * Example: + * print size_format(1*1024*1024*1024);// 1GB + * print size_format(512*1024*1024); // 512 MB + * print size_format(file_size(/path/to/my/file_8192)); //8KB + * Parameter + * $size + * Entity size in bytes. + * Return + * Formatted string representation of the given size. + */ +static int jx9Builtin_size_format(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + /*Kilo*/ /*Mega*/ /*Giga*/ /*Tera*/ /*Peta*/ /*Exa*/ /*Zeta*/ + static const char zUnit[] = {"KMGTPEZ"}; + sxi32 nRest, i_32; + jx9_int64 iSize; + int c = -1; /* index in zUnit[] */ + + if( nArg < 1 ){ + /* Missing argument, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Extract the given size */ + iSize = jx9_value_to_int64(apArg[0]); + if( iSize < 100 /* Bytes */ ){ + /* Don't bother formatting, return immediately */ + jx9_result_string(pCtx, "0.1 KB", (int)sizeof("0.1 KB")-1); + return JX9_OK; + } + for(;;){ + nRest = (sxi32)(iSize & 0x3FF); + iSize >>= 10; + c++; + if( (iSize & (~0 ^ 1023)) == 0 ){ + break; + } + } + nRest /= 100; + if( nRest > 9 ){ + nRest = 9; + } + if( iSize > 999 ){ + c++; + nRest = 9; + iSize = 0; + } + i_32 = (sxi32)iSize; + /* Format */ + jx9_result_string_format(pCtx, "%d.%d %cB", i_32, nRest, zUnit[c]); + return JX9_OK; +} +#if !defined(JX9_DISABLE_HASH_FUNC) +/* + * string md5(string $str[, bool $raw_output = false]) + * Calculate the md5 hash of a string. + * Parameter + * $str + * Input string + * $raw_output + * If the optional raw_output is set to TRUE, then the md5 digest + * is instead returned in raw binary format with a length of 16. + * Return + * MD5 Hash as a 32-character hexadecimal string. + */ +static int jx9Builtin_md5(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + unsigned char zDigest[16]; + int raw_output = FALSE; + const void *pIn; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Extract the input string */ + pIn = (const void *)jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + if( nArg > 1 && jx9_value_is_bool(apArg[1])){ + raw_output = jx9_value_to_bool(apArg[1]); + } + /* Compute the MD5 digest */ + SyMD5Compute(pIn, (sxu32)nLen, zDigest); + if( raw_output ){ + /* Output raw digest */ + jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest)); + }else{ + /* Perform a binary to hex conversion */ + SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx); + } + return JX9_OK; +} +/* + * string sha1(string $str[, bool $raw_output = false]) + * Calculate the sha1 hash of a string. + * Parameter + * $str + * Input string + * $raw_output + * If the optional raw_output is set to TRUE, then the md5 digest + * is instead returned in raw binary format with a length of 16. + * Return + * SHA1 Hash as a 40-character hexadecimal string. + */ +static int jx9Builtin_sha1(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + unsigned char zDigest[20]; + int raw_output = FALSE; + const void *pIn; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Extract the input string */ + pIn = (const void *)jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + if( nArg > 1 && jx9_value_is_bool(apArg[1])){ + raw_output = jx9_value_to_bool(apArg[1]); + } + /* Compute the SHA1 digest */ + SySha1Compute(pIn, (sxu32)nLen, zDigest); + if( raw_output ){ + /* Output raw digest */ + jx9_result_string(pCtx, (const char *)zDigest, (int)sizeof(zDigest)); + }else{ + /* Perform a binary to hex conversion */ + SyBinToHexConsumer((const void *)zDigest, sizeof(zDigest), HashConsumer, pCtx); + } + return JX9_OK; +} +/* + * int64 crc32(string $str) + * Calculates the crc32 polynomial of a strin. + * Parameter + * $str + * Input string + * Return + * CRC32 checksum of the given input (64-bit integer). + */ +static int jx9Builtin_crc32(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const void *pIn; + sxu32 nCRC; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return 0 */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Extract the input string */ + pIn = (const void *)jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty string */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Calculate the sum */ + nCRC = SyCrc32(pIn, (sxu32)nLen); + /* Return the CRC32 as 64-bit integer */ + jx9_result_int64(pCtx, (jx9_int64)nCRC^ 0xFFFFFFFF); + return JX9_OK; +} +#endif /* JX9_DISABLE_HASH_FUNC */ +/* + * Parse a CSV string and invoke the supplied callback for each processed xhunk. + */ +JX9_PRIVATE sxi32 jx9ProcessCsv( + const char *zInput, /* Raw input */ + int nByte, /* Input length */ + int delim, /* Delimiter */ + int encl, /* Enclosure */ + int escape, /* Escape character */ + sxi32 (*xConsumer)(const char *, int, void *), /* User callback */ + void *pUserData /* Last argument to xConsumer() */ + ) +{ + const char *zEnd = &zInput[nByte]; + const char *zIn = zInput; + const char *zPtr; + int isEnc; + /* Start processing */ + for(;;){ + if( zIn >= zEnd ){ + /* No more input to process */ + break; + } + isEnc = 0; + zPtr = zIn; + /* Find the first delimiter */ + while( zIn < zEnd ){ + if( zIn[0] == delim && !isEnc){ + /* Delimiter found, break imediately */ + break; + }else if( zIn[0] == encl ){ + /* Inside enclosure? */ + isEnc = !isEnc; + }else if( zIn[0] == escape ){ + /* Escape sequence */ + zIn++; + } + /* Advance the cursor */ + zIn++; + } + if( zIn > zPtr ){ + int nByte = (int)(zIn-zPtr); + sxi32 rc; + /* Invoke the supllied callback */ + if( zPtr[0] == encl ){ + zPtr++; + nByte-=2; + } + if( nByte > 0 ){ + rc = xConsumer(zPtr, nByte, pUserData); + if( rc == SXERR_ABORT ){ + /* User callback request an operation abort */ + break; + } + } + } + /* Ignore trailing delimiter */ + while( zIn < zEnd && zIn[0] == delim ){ + zIn++; + } + } + return SXRET_OK; +} +/* + * Default consumer callback for the CSV parsing routine defined above. + * All the processed input is insereted into an array passed as the last + * argument to this callback. + */ +JX9_PRIVATE sxi32 jx9CsvConsumer(const char *zToken, int nTokenLen, void *pUserData) +{ + jx9_value *pArray = (jx9_value *)pUserData; + jx9_value sEntry; + SyString sToken; + /* Insert the token in the given array */ + SyStringInitFromBuf(&sToken, zToken, nTokenLen); + /* Remove trailing and leading white spcaces and null bytes */ + SyStringFullTrimSafe(&sToken); + if( sToken.nByte < 1){ + return SXRET_OK; + } + jx9MemObjInitFromString(pArray->pVm, &sEntry, &sToken); + jx9_array_add_elem(pArray, 0, &sEntry); + jx9MemObjRelease(&sEntry); + return SXRET_OK; +} +/* + * array str_getcsv(string $input[, string $delimiter = ', '[, string $enclosure = '"' [, string $escape='\\']]]) + * Parse a CSV string into an array. + * Parameters + * $input + * The string to parse. + * $delimiter + * Set the field delimiter (one character only). + * $enclosure + * Set the field enclosure character (one character only). + * $escape + * Set the escape character (one character only). Defaults as a backslash (\) + * Return + * An indexed array containing the CSV fields or NULL on failure. + */ +static int jx9Builtin_str_getcsv(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zInput, *zPtr; + jx9_value *pArray; + int delim = ','; /* Delimiter */ + int encl = '"' ; /* Enclosure */ + int escape = '\\'; /* Escape character */ + int nLen; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Extract the raw input */ + zInput = jx9_value_to_string(apArg[0], &nLen); + if( nArg > 1 ){ + int i; + if( jx9_value_is_string(apArg[1]) ){ + /* Extract the delimiter */ + zPtr = jx9_value_to_string(apArg[1], &i); + if( i > 0 ){ + delim = zPtr[0]; + } + } + if( nArg > 2 ){ + if( jx9_value_is_string(apArg[2]) ){ + /* Extract the enclosure */ + zPtr = jx9_value_to_string(apArg[2], &i); + if( i > 0 ){ + encl = zPtr[0]; + } + } + if( nArg > 3 ){ + if( jx9_value_is_string(apArg[3]) ){ + /* Extract the escape character */ + zPtr = jx9_value_to_string(apArg[3], &i); + if( i > 0 ){ + escape = zPtr[0]; + } + } + } + } + } + /* Create our array */ + pArray = jx9_context_new_array(pCtx); + if( pArray == 0 ){ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); + jx9_result_null(pCtx); + return JX9_OK; + } + /* Parse the raw input */ + jx9ProcessCsv(zInput, nLen, delim, encl, escape, jx9CsvConsumer, pArray); + /* Return the freshly created array */ + jx9_result_value(pCtx, pArray); + return JX9_OK; +} +/* + * Extract a tag name from a raw HTML input and insert it in the given + * container. + * Refer to [strip_tags()]. + */ +static sxi32 AddTag(SySet *pSet, const char *zTag, int nByte) +{ + const char *zEnd = &zTag[nByte]; + const char *zPtr; + SyString sEntry; + /* Strip tags */ + for(;;){ + while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?' + || zTag[0] == '!' || zTag[0] == '-' || ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){ + zTag++; + } + if( zTag >= zEnd ){ + break; + } + zPtr = zTag; + /* Delimit the tag */ + while(zTag < zEnd ){ + if( (unsigned char)zTag[0] >= 0xc0 ){ + /* UTF-8 stream */ + zTag++; + SX_JMP_UTF8(zTag, zEnd); + }else if( !SyisAlphaNum(zTag[0]) ){ + break; + }else{ + zTag++; + } + } + if( zTag > zPtr ){ + /* Perform the insertion */ + SyStringInitFromBuf(&sEntry, zPtr, (int)(zTag-zPtr)); + SyStringFullTrim(&sEntry); + SySetPut(pSet, (const void *)&sEntry); + } + /* Jump the trailing '>' */ + zTag++; + } + return SXRET_OK; +} +/* + * Check if the given HTML tag name is present in the given container. + * Return SXRET_OK if present.SXERR_NOTFOUND otherwise. + * Refer to [strip_tags()]. + */ +static sxi32 FindTag(SySet *pSet, const char *zTag, int nByte) +{ + if( SySetUsed(pSet) > 0 ){ + const char *zCur, *zEnd = &zTag[nByte]; + SyString sTag; + while( zTag < zEnd && (zTag[0] == '<' || zTag[0] == '/' || zTag[0] == '?' || + ((unsigned char)zTag[0] < 0xc0 && SyisSpace(zTag[0]))) ){ + zTag++; + } + /* Delimit the tag */ + zCur = zTag; + while(zTag < zEnd ){ + if( (unsigned char)zTag[0] >= 0xc0 ){ + /* UTF-8 stream */ + zTag++; + SX_JMP_UTF8(zTag, zEnd); + }else if( !SyisAlphaNum(zTag[0]) ){ + break; + }else{ + zTag++; + } + } + SyStringInitFromBuf(&sTag, zCur, zTag-zCur); + /* Trim leading white spaces and null bytes */ + SyStringLeftTrimSafe(&sTag); + if( sTag.nByte > 0 ){ + SyString *aEntry, *pEntry; + sxi32 rc; + sxu32 n; + /* Perform the lookup */ + aEntry = (SyString *)SySetBasePtr(pSet); + for( n = 0 ; n < SySetUsed(pSet) ; ++n ){ + pEntry = &aEntry[n]; + /* Do the comparison */ + rc = SyStringCmp(pEntry, &sTag, SyStrnicmp); + if( !rc ){ + return SXRET_OK; + } + } + } + } + /* No such tag */ + return SXERR_NOTFOUND; +} +/* + * This function tries to return a string [i.e: in the call context result buffer] + * with all NUL bytes, HTML and JX9 tags stripped from a given string. + * Refer to [strip_tags()]. + */ +JX9_PRIVATE sxi32 jx9StripTagsFromString(jx9_context *pCtx, const char *zIn, int nByte, const char *zTaglist, int nTaglen) +{ + const char *zEnd = &zIn[nByte]; + const char *zPtr, *zTag; + SySet sSet; + /* initialize the set of allowed tags */ + SySetInit(&sSet, &pCtx->pVm->sAllocator, sizeof(SyString)); + if( nTaglen > 0 ){ + /* Set of allowed tags */ + AddTag(&sSet, zTaglist, nTaglen); + } + /* Set the empty string */ + jx9_result_string(pCtx, "", 0); + /* Start processing */ + for(;;){ + if(zIn >= zEnd){ + /* No more input to process */ + break; + } + zPtr = zIn; + /* Find a tag */ + while( zIn < zEnd && zIn[0] != '<' && zIn[0] != 0 /* NUL byte */ ){ + zIn++; + } + if( zIn > zPtr ){ + /* Consume raw input */ + jx9_result_string(pCtx, zPtr, (int)(zIn-zPtr)); + } + /* Ignore trailing null bytes */ + while( zIn < zEnd && zIn[0] == 0 ){ + zIn++; + } + if(zIn >= zEnd){ + /* No more input to process */ + break; + } + if( zIn[0] == '<' ){ + sxi32 rc; + zTag = zIn++; + /* Delimit the tag */ + while( zIn < zEnd && zIn[0] != '>' ){ + zIn++; + } + if( zIn < zEnd ){ + zIn++; /* Ignore the trailing closing tag */ + } + /* Query the set */ + rc = FindTag(&sSet, zTag, (int)(zIn-zTag)); + if( rc == SXRET_OK ){ + /* Keep the tag */ + jx9_result_string(pCtx, zTag, (int)(zIn-zTag)); + } + } + } + /* Cleanup */ + SySetRelease(&sSet); + return SXRET_OK; +} +/* + * string strip_tags(string $str[, string $allowable_tags]) + * Strip HTML and JX9 tags from a string. + * Parameters + * $str + * The input string. + * $allowable_tags + * You can use the optional second parameter to specify tags which should not be stripped. + * Return + * Returns the stripped string. + */ +static int jx9Builtin_strip_tags(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zTaglist = 0; + const char *zString; + int nTaglen = 0; + int nLen; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Point to the raw string */ + zString = jx9_value_to_string(apArg[0], &nLen); + if( nArg > 1 && jx9_value_is_string(apArg[1]) ){ + /* Allowed tag */ + zTaglist = jx9_value_to_string(apArg[1], &nTaglen); + } + /* Process input */ + jx9StripTagsFromString(pCtx, zString, nLen, zTaglist, nTaglen); + return JX9_OK; +} +/* + * array str_split(string $string[, int $split_length = 1 ]) + * Convert a string to an array. + * Parameters + * $str + * The input string. + * $split_length + * Maximum length of the chunk. + * Return + * If the optional split_length parameter is specified, the returned array + * will be broken down into chunks with each being split_length in length, otherwise + * each chunk will be one character in length. FALSE is returned if split_length is less than 1. + * If the split_length length exceeds the length of string, the entire string is returned + * as the first (and only) array element. + */ +static int jx9Builtin_str_split(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString, *zEnd; + jx9_value *pArray, *pValue; + int split_len; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the target string */ + zString = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Nothing to process, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + split_len = (int)sizeof(char); + if( nArg > 1 ){ + /* Split length */ + split_len = jx9_value_to_int(apArg[1]); + if( split_len < 1 ){ + /* Invalid length, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + if( split_len > nLen ){ + split_len = nLen; + } + } + /* Create the array and the scalar value */ + pArray = jx9_context_new_array(pCtx); + /*Chunk value */ + pValue = jx9_context_new_scalar(pCtx); + if( pValue == 0 || pArray == 0 ){ + /* Return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the end of the string */ + zEnd = &zString[nLen]; + /* Perform the requested operation */ + for(;;){ + int nMax; + if( zString >= zEnd ){ + /* No more input to process */ + break; + } + nMax = (int)(zEnd-zString); + if( nMax < split_len ){ + split_len = nMax; + } + /* Copy the current chunk */ + jx9_value_string(pValue, zString, split_len); + /* Insert it */ + jx9_array_add_elem(pArray, 0, pValue); /* Will make it's own copy */ + /* reset the string cursor */ + jx9_value_reset_string_cursor(pValue); + /* Update position */ + zString += split_len; + } + /* + * Return the array. + * Don't worry about freeing memory, everything will be automatically released + * upon we return from this function. + */ + jx9_result_value(pCtx, pArray); + return JX9_OK; +} +/* + * Tokenize a raw string and extract the first non-space token. + * Refer to [strspn()]. + */ +static sxi32 ExtractNonSpaceToken(const char **pzIn, const char *zEnd, SyString *pOut) +{ + const char *zIn = *pzIn; + const char *zPtr; + /* Ignore leading white spaces */ + while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ + zIn++; + } + if( zIn >= zEnd ){ + /* End of input */ + return SXERR_EOF; + } + zPtr = zIn; + /* Extract the token */ + while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && !SyisSpace(zIn[0]) ){ + zIn++; + } + SyStringInitFromBuf(pOut, zPtr, zIn-zPtr); + /* Synchronize pointers */ + *pzIn = zIn; + /* Return to the caller */ + return SXRET_OK; +} +/* + * Check if the given string contains only characters from the given mask. + * return the longest match. + * Refer to [strspn()]. + */ +static int LongestStringMask(const char *zString, int nLen, const char *zMask, int nMaskLen) +{ + const char *zEnd = &zString[nLen]; + const char *zIn = zString; + int i, c; + for(;;){ + if( zString >= zEnd ){ + break; + } + /* Extract current character */ + c = zString[0]; + /* Perform the lookup */ + for( i = 0 ; i < nMaskLen ; i++ ){ + if( c == zMask[i] ){ + /* Character found */ + break; + } + } + if( i >= nMaskLen ){ + /* Character not in the current mask, break immediately */ + break; + } + /* Advance cursor */ + zString++; + } + /* Longest match */ + return (int)(zString-zIn); +} +/* + * Do the reverse operation of the previous function [i.e: LongestStringMask()]. + * Refer to [strcspn()]. + */ +static int LongestStringMask2(const char *zString, int nLen, const char *zMask, int nMaskLen) +{ + const char *zEnd = &zString[nLen]; + const char *zIn = zString; + int i, c; + for(;;){ + if( zString >= zEnd ){ + break; + } + /* Extract current character */ + c = zString[0]; + /* Perform the lookup */ + for( i = 0 ; i < nMaskLen ; i++ ){ + if( c == zMask[i] ){ + break; + } + } + if( i < nMaskLen ){ + /* Character in the current mask, break immediately */ + break; + } + /* Advance cursor */ + zString++; + } + /* Longest match */ + return (int)(zString-zIn); +} +/* + * int strspn(string $str, string $mask[, int $start[, int $length]]) + * Finds the length of the initial segment of a string consisting entirely + * of characters contained within a given mask. + * Parameters + * $str + * The input string. + * $mask + * The list of allowable characters. + * $start + * The position in subject to start searching. + * If start is given and is non-negative, then strspn() will begin examining + * subject at the start'th position. For instance, in the string 'abcdef', the character + * at position 0 is 'a', the character at position 2 is 'c', and so forth. + * If start is given and is negative, then strspn() will begin examining subject at the + * start'th position from the end of subject. + * $length + * The length of the segment from subject to examine. + * If length is given and is non-negative, then subject will be examined for length + * characters after the starting position. + * If lengthis given and is negative, then subject will be examined from the starting + * position up to length characters from the end of subject. + * Return + * Returns the length of the initial segment of subject which consists entirely of characters + * in mask. + */ +static int jx9Builtin_strspn(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString, *zMask, *zEnd; + int iMasklen, iLen; + SyString sToken; + int iCount = 0; + int rc; + if( nArg < 2 ){ + /* Missing agruments, return zero */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zString = jx9_value_to_string(apArg[0], &iLen); + /* Extract the mask */ + zMask = jx9_value_to_string(apArg[1], &iMasklen); + if( iLen < 1 || iMasklen < 1 ){ + /* Nothing to process, return zero */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + if( nArg > 2 ){ + int nOfft; + /* Extract the offset */ + nOfft = jx9_value_to_int(apArg[2]); + if( nOfft < 0 ){ + const char *zBase = &zString[iLen + nOfft]; + if( zBase > zString ){ + iLen = (int)(&zString[iLen]-zBase); + zString = zBase; + }else{ + /* Invalid offset */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + }else{ + if( nOfft >= iLen ){ + /* Invalid offset */ + jx9_result_int(pCtx, 0); + return JX9_OK; + }else{ + /* Update offset */ + zString += nOfft; + iLen -= nOfft; + } + } + if( nArg > 3 ){ + int iUserlen; + /* Extract the desired length */ + iUserlen = jx9_value_to_int(apArg[3]); + if( iUserlen > 0 && iUserlen < iLen ){ + iLen = iUserlen; + } + } + } + /* Point to the end of the string */ + zEnd = &zString[iLen]; + /* Extract the first non-space token */ + rc = ExtractNonSpaceToken(&zString, zEnd, &sToken); + if( rc == SXRET_OK && sToken.nByte > 0 ){ + /* Compare against the current mask */ + iCount = LongestStringMask(sToken.zString, (int)sToken.nByte, zMask, iMasklen); + } + /* Longest match */ + jx9_result_int(pCtx, iCount); + return JX9_OK; +} +/* + * int strcspn(string $str, string $mask[, int $start[, int $length]]) + * Find length of initial segment not matching mask. + * Parameters + * $str + * The input string. + * $mask + * The list of not allowed characters. + * $start + * The position in subject to start searching. + * If start is given and is non-negative, then strspn() will begin examining + * subject at the start'th position. For instance, in the string 'abcdef', the character + * at position 0 is 'a', the character at position 2 is 'c', and so forth. + * If start is given and is negative, then strspn() will begin examining subject at the + * start'th position from the end of subject. + * $length + * The length of the segment from subject to examine. + * If length is given and is non-negative, then subject will be examined for length + * characters after the starting position. + * If lengthis given and is negative, then subject will be examined from the starting + * position up to length characters from the end of subject. + * Return + * Returns the length of the segment as an integer. + */ +static int jx9Builtin_strcspn(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString, *zMask, *zEnd; + int iMasklen, iLen; + SyString sToken; + int iCount = 0; + int rc; + if( nArg < 2 ){ + /* Missing agruments, return zero */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zString = jx9_value_to_string(apArg[0], &iLen); + /* Extract the mask */ + zMask = jx9_value_to_string(apArg[1], &iMasklen); + if( iLen < 1 ){ + /* Nothing to process, return zero */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + if( iMasklen < 1 ){ + /* No given mask, return the string length */ + jx9_result_int(pCtx, iLen); + return JX9_OK; + } + if( nArg > 2 ){ + int nOfft; + /* Extract the offset */ + nOfft = jx9_value_to_int(apArg[2]); + if( nOfft < 0 ){ + const char *zBase = &zString[iLen + nOfft]; + if( zBase > zString ){ + iLen = (int)(&zString[iLen]-zBase); + zString = zBase; + }else{ + /* Invalid offset */ + jx9_result_int(pCtx, 0); + return JX9_OK; + } + }else{ + if( nOfft >= iLen ){ + /* Invalid offset */ + jx9_result_int(pCtx, 0); + return JX9_OK; + }else{ + /* Update offset */ + zString += nOfft; + iLen -= nOfft; + } + } + if( nArg > 3 ){ + int iUserlen; + /* Extract the desired length */ + iUserlen = jx9_value_to_int(apArg[3]); + if( iUserlen > 0 && iUserlen < iLen ){ + iLen = iUserlen; + } + } + } + /* Point to the end of the string */ + zEnd = &zString[iLen]; + /* Extract the first non-space token */ + rc = ExtractNonSpaceToken(&zString, zEnd, &sToken); + if( rc == SXRET_OK && sToken.nByte > 0 ){ + /* Compare against the current mask */ + iCount = LongestStringMask2(sToken.zString, (int)sToken.nByte, zMask, iMasklen); + } + /* Longest match */ + jx9_result_int(pCtx, iCount); + return JX9_OK; +} +/* + * string strpbrk(string $haystack, string $char_list) + * Search a string for any of a set of characters. + * Parameters + * $haystack + * The string where char_list is looked for. + * $char_list + * This parameter is case sensitive. + * Return + * Returns a string starting from the character found, or FALSE if it is not found. + */ +static int jx9Builtin_strpbrk(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zString, *zList, *zEnd; + int iLen, iListLen, i, c; + sxu32 nOfft, nMax; + sxi32 rc; + if( nArg < 2 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the haystack and the char list */ + zString = jx9_value_to_string(apArg[0], &iLen); + zList = jx9_value_to_string(apArg[1], &iListLen); + if( iLen < 1 ){ + /* Nothing to process, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Point to the end of the string */ + zEnd = &zString[iLen]; + nOfft = nMax = SXU32_HIGH; + /* perform the requested operation */ + for( i = 0 ; i < iListLen ; i++ ){ + c = zList[i]; + rc = SyByteFind(zString, (sxu32)iLen, c, &nMax); + if( rc == SXRET_OK ){ + if( nMax < nOfft ){ + nOfft = nMax; + } + } + } + if( nOfft == SXU32_HIGH ){ + /* No such substring, return FALSE */ + jx9_result_bool(pCtx, 0); + }else{ + /* Return the substring */ + jx9_result_string(pCtx, &zString[nOfft], (int)(zEnd-&zString[nOfft])); + } + return JX9_OK; +} +/* + * string soundex(string $str) + * Calculate the soundex key of a string. + * Parameters + * $str + * The input string. + * Return + * Returns the soundex key as a string. + * Note: + * This implementation is based on the one found in the SQLite3 + * source tree. + */ +static int jx9Builtin_soundex(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const unsigned char *zIn; + char zResult[8]; + int i, j; + static const unsigned char iCode[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, + 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0, + 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0, + }; + if( nArg < 1 ){ + /* Missing arguments, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + zIn = (unsigned char *)jx9_value_to_string(apArg[0], 0); + for(i=0; zIn[i] && zIn[i] < 0xc0 && !SyisAlpha(zIn[i]); i++){} + if( zIn[i] ){ + unsigned char prevcode = iCode[zIn[i]&0x7f]; + zResult[0] = (char)SyToUpper(zIn[i]); + for(j=1; j<4 && zIn[i]; i++){ + int code = iCode[zIn[i]&0x7f]; + if( code>0 ){ + if( code!=prevcode ){ + prevcode = (unsigned char)code; + zResult[j++] = (char)code + '0'; + } + }else{ + prevcode = 0; + } + } + while( j<4 ){ + zResult[j++] = '0'; + } + jx9_result_string(pCtx, zResult, 4); + }else{ + jx9_result_string(pCtx, "?000", 4); + } + return JX9_OK; +} +/* + * string wordwrap(string $str[, int $width = 75[, string $break = "\n"]]) + * Wraps a string to a given number of characters. + * Parameters + * $str + * The input string. + * $width + * The column width. + * $break + * The line is broken using the optional break parameter. + * Return + * Returns the given string wrapped at the specified column. + */ +static int jx9Builtin_wordwrap(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zIn, *zEnd, *zBreak; + int iLen, iBreaklen, iChunk; + if( nArg < 1 ){ + /* Missing arguments, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Extract the input string */ + zIn = jx9_value_to_string(apArg[0], &iLen); + if( iLen < 1 ){ + /* Nothing to process, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Chunk length */ + iChunk = 75; + iBreaklen = 0; + zBreak = ""; /* cc warning */ + if( nArg > 1 ){ + iChunk = jx9_value_to_int(apArg[1]); + if( iChunk < 1 ){ + iChunk = 75; + } + if( nArg > 2 ){ + zBreak = jx9_value_to_string(apArg[2], &iBreaklen); + } + } + if( iBreaklen < 1 ){ + /* Set a default column break */ +#ifdef __WINNT__ + zBreak = "\r\n"; + iBreaklen = (int)sizeof("\r\n")-1; +#else + zBreak = "\n"; + iBreaklen = (int)sizeof(char); +#endif + } + /* Perform the requested operation */ + zEnd = &zIn[iLen]; + for(;;){ + int nMax; + if( zIn >= zEnd ){ + /* No more input to process */ + break; + } + nMax = (int)(zEnd-zIn); + if( iChunk > nMax ){ + iChunk = nMax; + } + /* Append the column first */ + jx9_result_string(pCtx, zIn, iChunk); /* Will make it's own copy */ + /* Advance the cursor */ + zIn += iChunk; + if( zIn < zEnd ){ + /* Append the line break */ + jx9_result_string(pCtx, zBreak, iBreaklen); + } + } + return JX9_OK; +} +/* + * Check if the given character is a member of the given mask. + * Return TRUE on success. FALSE otherwise. + * Refer to [strtok()]. + */ +static int CheckMask(int c, const char *zMask, int nMasklen, int *pOfft) +{ + int i; + for( i = 0 ; i < nMasklen ; ++i ){ + if( c == zMask[i] ){ + if( pOfft ){ + *pOfft = i; + } + return TRUE; + } + } + return FALSE; +} +/* + * Extract a single token from the input stream. + * Refer to [strtok()]. + */ +static sxi32 ExtractToken(const char **pzIn, const char *zEnd, const char *zMask, int nMasklen, SyString *pOut) +{ + const char *zIn = *pzIn; + const char *zPtr; + /* Ignore leading delimiter */ + while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && CheckMask(zIn[0], zMask, nMasklen, 0) ){ + zIn++; + } + if( zIn >= zEnd ){ + /* End of input */ + return SXERR_EOF; + } + zPtr = zIn; + /* Extract the token */ + while( zIn < zEnd ){ + if( (unsigned char)zIn[0] >= 0xc0 ){ + /* UTF-8 stream */ + zIn++; + SX_JMP_UTF8(zIn, zEnd); + }else{ + if( CheckMask(zIn[0], zMask, nMasklen, 0) ){ + break; + } + zIn++; + } + } + SyStringInitFromBuf(pOut, zPtr, zIn-zPtr); + /* Update the cursor */ + *pzIn = zIn; + /* Return to the caller */ + return SXRET_OK; +} +/* strtok auxiliary private data */ +typedef struct strtok_aux_data strtok_aux_data; +struct strtok_aux_data +{ + const char *zDup; /* Complete duplicate of the input */ + const char *zIn; /* Current input stream */ + const char *zEnd; /* End of input */ +}; +/* + * string strtok(string $str, string $token) + * string strtok(string $token) + * strtok() splits a string (str) into smaller strings (tokens), with each token + * being delimited by any character from token. That is, if you have a string like + * "This is an example string" you could tokenize this string into its individual + * words by using the space character as the token. + * Note that only the first call to strtok uses the string argument. Every subsequent + * call to strtok only needs the token to use, as it keeps track of where it is in + * the current string. To start over, or to tokenize a new string you simply call strtok + * with the string argument again to initialize it. Note that you may put multiple tokens + * in the token parameter. The string will be tokenized when any one of the characters in + * the argument are found. + * Parameters + * $str + * The string being split up into smaller strings (tokens). + * $token + * The delimiter used when splitting up str. + * Return + * Current token or FALSE on EOF. + */ +static int jx9Builtin_strtok(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + strtok_aux_data *pAux; + const char *zMask; + SyString sToken; + int nMasklen; + sxi32 rc; + if( nArg < 2 ){ + /* Extract top aux data */ + pAux = (strtok_aux_data *)jx9_context_peek_aux_data(pCtx); + if( pAux == 0 ){ + /* No aux data, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + nMasklen = 0; + zMask = ""; /* cc warning */ + if( nArg > 0 ){ + /* Extract the mask */ + zMask = jx9_value_to_string(apArg[0], &nMasklen); + } + if( nMasklen < 1 ){ + /* Invalid mask, return FALSE */ + jx9_context_free_chunk(pCtx, (void *)pAux->zDup); + jx9_context_free_chunk(pCtx, pAux); + (void)jx9_context_pop_aux_data(pCtx); + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the token */ + rc = ExtractToken(&pAux->zIn, pAux->zEnd, zMask, nMasklen, &sToken); + if( rc != SXRET_OK ){ + /* EOF , discard the aux data */ + jx9_context_free_chunk(pCtx, (void *)pAux->zDup); + jx9_context_free_chunk(pCtx, pAux); + (void)jx9_context_pop_aux_data(pCtx); + jx9_result_bool(pCtx, 0); + }else{ + /* Return the extracted token */ + jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte); + } + }else{ + const char *zInput, *zCur; + char *zDup; + int nLen; + /* Extract the raw input */ + zCur = zInput = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Empty input, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the mask */ + zMask = jx9_value_to_string(apArg[1], &nMasklen); + if( nMasklen < 1 ){ + /* Set a default mask */ +#define TOK_MASK " \n\t\r\f" + zMask = TOK_MASK; + nMasklen = (int)sizeof(TOK_MASK) - 1; +#undef TOK_MASK + } + /* Extract a single token */ + rc = ExtractToken(&zInput, &zInput[nLen], zMask, nMasklen, &sToken); + if( rc != SXRET_OK ){ + /* Empty input */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + }else{ + /* Return the extracted token */ + jx9_result_string(pCtx, sToken.zString, (int)sToken.nByte); + } + /* Create our auxilliary data and copy the input */ + pAux = (strtok_aux_data *)jx9_context_alloc_chunk(pCtx, sizeof(strtok_aux_data), TRUE, FALSE); + if( pAux ){ + nLen -= (int)(zInput-zCur); + if( nLen < 1 ){ + jx9_context_free_chunk(pCtx, pAux); + return JX9_OK; + } + /* Duplicate input */ + zDup = (char *)jx9_context_alloc_chunk(pCtx, (unsigned int)(nLen+1), TRUE, FALSE); + if( zDup ){ + SyMemcpy(zInput, zDup, (sxu32)nLen); + /* Register the aux data */ + pAux->zDup = pAux->zIn = zDup; + pAux->zEnd = &zDup[nLen]; + jx9_context_push_aux_data(pCtx, pAux); + } + } + } + return JX9_OK; +} +/* + * string str_pad(string $input, int $pad_length[, string $pad_string = " " [, int $pad_type = STR_PAD_RIGHT]]) + * Pad a string to a certain length with another string + * Parameters + * $input + * The input string. + * $pad_length + * If the value of pad_length is negative, less than, or equal to the length of the input + * string, no padding takes place. + * $pad_string + * Note: + * The pad_string WIIL NOT BE truncated if the required number of padding characters can't be evenly + * divided by the pad_string's length. + * $pad_type + * Optional argument pad_type can be STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH. If pad_type + * is not specified it is assumed to be STR_PAD_RIGHT. + * Return + * The padded string. + */ +static int jx9Builtin_str_pad(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int iLen, iPadlen, iType, i, iDiv, iStrpad, iRealPad, jPad; + const char *zIn, *zPad; + if( nArg < 2 ){ + /* Missing arguments, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Extract the target string */ + zIn = jx9_value_to_string(apArg[0], &iLen); + /* Padding length */ + iRealPad = iPadlen = jx9_value_to_int(apArg[1]); + if( iPadlen > 0 ){ + iPadlen -= iLen; + } + if( iPadlen < 1 ){ + /* Return the string verbatim */ + jx9_result_string(pCtx, zIn, iLen); + return JX9_OK; + } + zPad = " "; /* Whitespace padding */ + iStrpad = (int)sizeof(char); + iType = 1 ; /* STR_PAD_RIGHT */ + if( nArg > 2 ){ + /* Padding string */ + zPad = jx9_value_to_string(apArg[2], &iStrpad); + if( iStrpad < 1 ){ + /* Empty string */ + zPad = " "; /* Whitespace padding */ + iStrpad = (int)sizeof(char); + } + if( nArg > 3 ){ + /* Padd type */ + iType = jx9_value_to_int(apArg[3]); + if( iType != 0 /* STR_PAD_LEFT */ && iType != 2 /* STR_PAD_BOTH */ ){ + iType = 1 ; /* STR_PAD_RIGHT */ + } + } + } + iDiv = 1; + if( iType == 2 ){ + iDiv = 2; /* STR_PAD_BOTH */ + } + /* Perform the requested operation */ + if( iType == 0 /* STR_PAD_LEFT */ || iType == 2 /* STR_PAD_BOTH */ ){ + jPad = iStrpad; + for( i = 0 ; i < iPadlen/iDiv ; i += jPad ){ + /* Padding */ + if( (int)jx9_context_result_buf_length(pCtx) + iLen + jPad >= iRealPad ){ + break; + } + jx9_result_string(pCtx, zPad, jPad); + } + if( iType == 0 /* STR_PAD_LEFT */ ){ + while( (int)jx9_context_result_buf_length(pCtx) + iLen < iRealPad ){ + jPad = iRealPad - (iLen + (int)jx9_context_result_buf_length(pCtx) ); + if( jPad > iStrpad ){ + jPad = iStrpad; + } + if( jPad < 1){ + break; + } + jx9_result_string(pCtx, zPad, jPad); + } + } + } + if( iLen > 0 ){ + /* Append the input string */ + jx9_result_string(pCtx, zIn, iLen); + } + if( iType == 1 /* STR_PAD_RIGHT */ || iType == 2 /* STR_PAD_BOTH */ ){ + for( i = 0 ; i < iPadlen/iDiv ; i += iStrpad ){ + /* Padding */ + if( (int)jx9_context_result_buf_length(pCtx) + iStrpad >= iRealPad ){ + break; + } + jx9_result_string(pCtx, zPad, iStrpad); + } + while( (int)jx9_context_result_buf_length(pCtx) < iRealPad ){ + jPad = iRealPad - (int)jx9_context_result_buf_length(pCtx); + if( jPad > iStrpad ){ + jPad = iStrpad; + } + if( jPad < 1){ + break; + } + jx9_result_string(pCtx, zPad, jPad); + } + } + return JX9_OK; +} +/* + * String replacement private data. + */ +typedef struct str_replace_data str_replace_data; +struct str_replace_data +{ + /* The following two fields are only used by the strtr function */ + SyBlob *pWorker; /* Working buffer */ + ProcStringMatch xMatch; /* Pattern match routine */ + /* The following two fields are only used by the str_replace function */ + SySet *pCollector; /* Argument collector*/ + jx9_context *pCtx; /* Call context */ +}; +/* + * Remove a substring. + */ +#define STRDEL(SRC, SLEN, OFFT, ILEN){\ + for(;;){\ + if( OFFT + ILEN >= SLEN ) break; SRC[OFFT] = SRC[OFFT+ILEN]; ++OFFT;\ + }\ +} +/* + * Shift right and insert algorithm. + */ +#define SHIFTRANDINSERT(SRC, LEN, OFFT, ENTRY, ELEN){\ + sxu32 INLEN = LEN - OFFT;\ + for(;;){\ + if( LEN > 0 ){ LEN--; } if(INLEN < 1 ) break; SRC[LEN + ELEN] = SRC[LEN] ; --INLEN; \ + }\ + for(;;){\ + if(ELEN < 1)break; SRC[OFFT] = ENTRY[0]; OFFT++; ENTRY++; --ELEN;\ + }\ +} +/* + * Replace all occurrences of the search string at offset (nOfft) with the given + * replacement string [i.e: zReplace]. + */ +static int StringReplace(SyBlob *pWorker, sxu32 nOfft, int nLen, const char *zReplace, int nReplen) +{ + char *zInput = (char *)SyBlobData(pWorker); + sxu32 n, m; + n = SyBlobLength(pWorker); + m = nOfft; + /* Delete the old entry */ + STRDEL(zInput, n, m, nLen); + SyBlobLength(pWorker) -= nLen; + if( nReplen > 0 ){ + sxi32 iRep = nReplen; + sxi32 rc; + /* + * Make sure the working buffer is big enough to hold the replacement + * string. + */ + rc = SyBlobAppend(pWorker, 0/* Grow without an append operation*/, (sxu32)nReplen); + if( rc != SXRET_OK ){ + /* Simply ignore any memory failure problem */ + return SXRET_OK; + } + /* Perform the insertion now */ + zInput = (char *)SyBlobData(pWorker); + n = SyBlobLength(pWorker); + SHIFTRANDINSERT(zInput, n, nOfft, zReplace, iRep); + SyBlobLength(pWorker) += nReplen; + } + return SXRET_OK; +} +/* + * String replacement walker callback. + * The following callback is invoked for each array entry that hold + * the replace string. + * Refer to the strtr() implementation for more information. + */ +static int StringReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData) +{ + str_replace_data *pRepData = (str_replace_data *)pUserData; + const char *zTarget, *zReplace; + SyBlob *pWorker; + int tLen, nLen; + sxu32 nOfft; + sxi32 rc; + /* Point to the working buffer */ + pWorker = pRepData->pWorker; + if( !jx9_value_is_string(pKey) ){ + /* Target and replace must be a string */ + return JX9_OK; + } + /* Extract the target and the replace */ + zTarget = jx9_value_to_string(pKey, &tLen); + if( tLen < 1 ){ + /* Empty target, return immediately */ + return JX9_OK; + } + /* Perform a pattern search */ + rc = pRepData->xMatch(SyBlobData(pWorker), SyBlobLength(pWorker), (const void *)zTarget, (sxu32)tLen, &nOfft); + if( rc != SXRET_OK ){ + /* Pattern not found */ + return JX9_OK; + } + /* Extract the replace string */ + zReplace = jx9_value_to_string(pData, &nLen); + /* Perform the replace process */ + StringReplace(pWorker, nOfft, tLen, zReplace, nLen); + /* All done */ + return JX9_OK; +} +/* + * The following walker callback is invoked by the str_rplace() function inorder + * to collect search/replace string. + * This callback is invoked only if the given argument is of type array. + */ +static int StrReplaceWalker(jx9_value *pKey, jx9_value *pData, void *pUserData) +{ + str_replace_data *pRep = (str_replace_data *)pUserData; + SyString sWorker; + const char *zIn; + int nByte; + /* Extract a string representation of the given argument */ + zIn = jx9_value_to_string(pData, &nByte); + SyStringInitFromBuf(&sWorker, 0, 0); + if( nByte > 0 ){ + char *zDup; + /* Duplicate the chunk */ + zDup = (char *)jx9_context_alloc_chunk(pRep->pCtx, (unsigned int)nByte, FALSE, + TRUE /* Release the chunk automatically, upon this context is destroyd */ + ); + if( zDup == 0 ){ + /* Ignore any memory failure problem */ + jx9_context_throw_error(pRep->pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); + return JX9_OK; + } + SyMemcpy(zIn, zDup, (sxu32)nByte); + /* Save the chunk */ + SyStringInitFromBuf(&sWorker, zDup, nByte); + } + /* Save for later processing */ + SySetPut(pRep->pCollector, (const void *)&sWorker); + /* All done */ + SXUNUSED(pKey); /* cc warning */ + return JX9_OK; +} +/* + * mixed str_replace(mixed $search, mixed $replace, mixed $subject[, int &$count ]) + * mixed str_ireplace(mixed $search, mixed $replace, mixed $subject[, int &$count ]) + * Replace all occurrences of the search string with the replacement string. + * Parameters + * If search and replace are arrays, then str_replace() takes a value from each + * array and uses them to search and replace on subject. If replace has fewer values + * than search, then an empty string is used for the rest of replacement values. + * If search is an array and replace is a string, then this replacement string is used + * for every value of search. The converse would not make sense, though. + * If search or replace are arrays, their elements are processed first to last. + * $search + * The value being searched for, otherwise known as the needle. An array may be used + * to designate multiple needles. + * $replace + * The replacement value that replaces found search values. An array may be used + * to designate multiple replacements. + * $subject + * The string or array being searched and replaced on, otherwise known as the haystack. + * If subject is an array, then the search and replace is performed with every entry + * of subject, and the return value is an array as well. + * $count (Not used) + * If passed, this will be set to the number of replacements performed. + * Return + * This function returns a string or an array with the replaced values. + */ +static int jx9Builtin_str_replace(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + SyString sTemp, *pSearch, *pReplace; + ProcStringMatch xMatch; + const char *zIn, *zFunc; + str_replace_data sRep; + SyBlob sWorker; + SySet sReplace; + SySet sSearch; + int rep_str; + int nByte; + sxi32 rc; + if( nArg < 3 ){ + /* Missing/Invalid arguments, return null */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Initialize fields */ + SySetInit(&sSearch, &pCtx->pVm->sAllocator, sizeof(SyString)); + SySetInit(&sReplace, &pCtx->pVm->sAllocator, sizeof(SyString)); + SyBlobInit(&sWorker, &pCtx->pVm->sAllocator); + SyZero(&sRep, sizeof(str_replace_data)); + sRep.pCtx = pCtx; + sRep.pCollector = &sSearch; + rep_str = 0; + /* Extract the subject */ + zIn = jx9_value_to_string(apArg[2], &nByte); + if( nByte < 1 ){ + /* Nothing to replace, return the empty string */ + jx9_result_string(pCtx, "", 0); + return JX9_OK; + } + /* Copy the subject */ + SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nByte); + /* Search string */ + if( jx9_value_is_json_array(apArg[0]) ){ + /* Collect search string */ + jx9_array_walk(apArg[0], StrReplaceWalker, &sRep); + }else{ + /* Single pattern */ + zIn = jx9_value_to_string(apArg[0], &nByte); + if( nByte < 1 ){ + /* Return the subject untouched since no search string is available */ + jx9_result_value(pCtx, apArg[2]/* Subject as thrird argument*/); + return JX9_OK; + } + SyStringInitFromBuf(&sTemp, zIn, nByte); + /* Save for later processing */ + SySetPut(&sSearch, (const void *)&sTemp); + } + /* Replace string */ + if( jx9_value_is_json_array(apArg[1]) ){ + /* Collect replace string */ + sRep.pCollector = &sReplace; + jx9_array_walk(apArg[1], StrReplaceWalker, &sRep); + }else{ + /* Single needle */ + zIn = jx9_value_to_string(apArg[1], &nByte); + rep_str = 1; + SyStringInitFromBuf(&sTemp, zIn, nByte); + /* Save for later processing */ + SySetPut(&sReplace, (const void *)&sTemp); + } + /* Reset loop cursors */ + SySetResetCursor(&sSearch); + SySetResetCursor(&sReplace); + pReplace = pSearch = 0; /* cc warning */ + SyStringInitFromBuf(&sTemp, "", 0); + /* Extract function name */ + zFunc = jx9_function_name(pCtx); + /* Set the default pattern match routine */ + xMatch = SyBlobSearch; + if( SyStrncmp(zFunc, "str_ireplace", sizeof("str_ireplace") - 1) == 0 ){ + /* Case insensitive pattern match */ + xMatch = iPatternMatch; + } + /* Start the replace process */ + while( SXRET_OK == SySetGetNextEntry(&sSearch, (void **)&pSearch) ){ + sxu32 nCount, nOfft; + if( pSearch->nByte < 1 ){ + /* Empty string, ignore */ + continue; + } + /* Extract the replace string */ + if( rep_str ){ + pReplace = (SyString *)SySetPeek(&sReplace); + }else{ + if( SXRET_OK != SySetGetNextEntry(&sReplace, (void **)&pReplace) ){ + /* Sepecial case when 'replace set' has fewer values than the search set. + * An empty string is used for the rest of replacement values + */ + pReplace = 0; + } + } + if( pReplace == 0 ){ + /* Use an empty string instead */ + pReplace = &sTemp; + } + nOfft = nCount = 0; + for(;;){ + if( nCount >= SyBlobLength(&sWorker) ){ + break; + } + /* Perform a pattern lookup */ + rc = xMatch(SyBlobDataAt(&sWorker, nCount), SyBlobLength(&sWorker) - nCount, (const void *)pSearch->zString, + pSearch->nByte, &nOfft); + if( rc != SXRET_OK ){ + /* Pattern not found */ + break; + } + /* Perform the replace operation */ + StringReplace(&sWorker, nCount+nOfft, (int)pSearch->nByte, pReplace->zString, (int)pReplace->nByte); + /* Increment offset counter */ + nCount += nOfft + pReplace->nByte; + } + } + /* All done, clean-up the mess left behind */ + jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker), (int)SyBlobLength(&sWorker)); + SySetRelease(&sSearch); + SySetRelease(&sReplace); + SyBlobRelease(&sWorker); + return JX9_OK; +} +/* + * string strtr(string $str, string $from, string $to) + * string strtr(string $str, array $replace_pairs) + * Translate characters or replace substrings. + * Parameters + * $str + * The string being translated. + * $from + * The string being translated to to. + * $to + * The string replacing from. + * $replace_pairs + * The replace_pairs parameter may be used instead of to and + * from, in which case it's an array in the form array('from' => 'to', ...). + * Return + * The translated string. + * If replace_pairs contains a key which is an empty string (""), FALSE will be returned. + */ +static int jx9Builtin_strtr(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zIn; + int nLen; + if( nArg < 1 ){ + /* Nothing to replace, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + zIn = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 || nArg < 2 ){ + /* Invalid arguments */ + jx9_result_string(pCtx, zIn, nLen); + return JX9_OK; + } + if( nArg == 2 && jx9_value_is_json_array(apArg[1]) ){ + str_replace_data sRepData; + SyBlob sWorker; + /* Initilaize the working buffer */ + SyBlobInit(&sWorker, &pCtx->pVm->sAllocator); + /* Copy raw string */ + SyBlobAppend(&sWorker, (const void *)zIn, (sxu32)nLen); + /* Init our replace data instance */ + sRepData.pWorker = &sWorker; + sRepData.xMatch = SyBlobSearch; + /* Iterate throw array entries and perform the replace operation.*/ + jx9_array_walk(apArg[1], StringReplaceWalker, &sRepData); + /* All done, return the result string */ + jx9_result_string(pCtx, (const char *)SyBlobData(&sWorker), + (int)SyBlobLength(&sWorker)); /* Will make it's own copy */ + /* Clean-up */ + SyBlobRelease(&sWorker); + }else{ + int i, flen, tlen, c, iOfft; + const char *zFrom, *zTo; + if( nArg < 3 ){ + /* Nothing to replace */ + jx9_result_string(pCtx, zIn, nLen); + return JX9_OK; + } + /* Extract given arguments */ + zFrom = jx9_value_to_string(apArg[1], &flen); + zTo = jx9_value_to_string(apArg[2], &tlen); + if( flen < 1 || tlen < 1 ){ + /* Nothing to replace */ + jx9_result_string(pCtx, zIn, nLen); + return JX9_OK; + } + /* Start the replace process */ + for( i = 0 ; i < nLen ; ++i ){ + c = zIn[i]; + if( CheckMask(c, zFrom, flen, &iOfft) ){ + if ( iOfft < tlen ){ + c = zTo[iOfft]; + } + } + jx9_result_string(pCtx, (const char *)&c, (int)sizeof(char)); + + } + } + return JX9_OK; +} +/* + * Parse an INI string. + * According to wikipedia + * The INI file format is an informal standard for configuration files for some platforms or software. + * INI files are simple text files with a basic structure composed of "sections" and "properties". + * Format +* Properties +* The basic element contained in an INI file is the property. Every property has a name and a value +* delimited by an equals sign (=). The name appears to the left of the equals sign. +* Example: +* name=value +* Sections +* Properties may be grouped into arbitrarily named sections. The section name appears on a line by itself +* in square brackets ([ and ]). All properties after the section declaration are associated with that section. +* There is no explicit "end of section" delimiter; sections end at the next section declaration +* or the end of the file. Sections may not be nested. +* Example: +* [section] +* Comments +* Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored. +* This function return an array holding parsed values on success.FALSE otherwise. +*/ +JX9_PRIVATE sxi32 jx9ParseIniString(jx9_context *pCtx, const char *zIn, sxu32 nByte, int bProcessSection) +{ + jx9_value *pCur, *pArray, *pSection, *pWorker, *pValue; + const char *zCur, *zEnd = &zIn[nByte]; + SyHashEntry *pEntry; + SyString sEntry; + SyHash sHash; + int c; + /* Create an empty array and worker variables */ + pArray = jx9_context_new_array(pCtx); + pWorker = jx9_context_new_scalar(pCtx); + pValue = jx9_context_new_scalar(pCtx); + if( pArray == 0 || pWorker == 0 || pValue == 0){ + /* Out of memory */ + jx9_context_throw_error(pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); + /* Return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + SyHashInit(&sHash, &pCtx->pVm->sAllocator, 0, 0); + pCur = pArray; + /* Start the parse process */ + for(;;){ + /* Ignore leading white spaces */ + while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0])){ + zIn++; + } + if( zIn >= zEnd ){ + /* No more input to process */ + break; + } + if( zIn[0] == ';' || zIn[0] == '#' ){ + /* Comment til the end of line */ + zIn++; + while(zIn < zEnd && zIn[0] != '\n' ){ + zIn++; + } + continue; + } + /* Reset the string cursor of the working variable */ + jx9_value_reset_string_cursor(pWorker); + if( zIn[0] == '[' ){ + /* Section: Extract the section name */ + zIn++; + zCur = zIn; + while( zIn < zEnd && zIn[0] != ']' ){ + zIn++; + } + if( zIn > zCur && bProcessSection ){ + /* Save the section name */ + SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur)); + SyStringFullTrim(&sEntry); + jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte); + if( sEntry.nByte > 0 ){ + /* Associate an array with the section */ + pSection = jx9_context_new_array(pCtx); + if( pSection ){ + jx9_array_add_elem(pArray, pWorker/*Section name*/, pSection); + pCur = pSection; + } + } + } + zIn++; /* Trailing square brackets ']' */ + }else{ + jx9_value *pOldCur; + int is_array; + int iLen; + /* Properties */ + is_array = 0; + zCur = zIn; + iLen = 0; /* cc warning */ + pOldCur = pCur; + while( zIn < zEnd && zIn[0] != '=' ){ + if( zIn[0] == '[' && !is_array ){ + /* Array */ + iLen = (int)(zIn-zCur); + is_array = 1; + if( iLen > 0 ){ + jx9_value *pvArr = 0; /* cc warning */ + /* Query the hashtable */ + SyStringInitFromBuf(&sEntry, zCur, iLen); + SyStringFullTrim(&sEntry); + pEntry = SyHashGet(&sHash, (const void *)sEntry.zString, sEntry.nByte); + if( pEntry ){ + pvArr = (jx9_value *)SyHashEntryGetUserData(pEntry); + }else{ + /* Create an empty array */ + pvArr = jx9_context_new_array(pCtx); + if( pvArr ){ + /* Save the entry */ + SyHashInsert(&sHash, (const void *)sEntry.zString, sEntry.nByte, pvArr); + /* Insert the entry */ + jx9_value_reset_string_cursor(pWorker); + jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte); + jx9_array_add_elem(pCur, pWorker, pvArr); + jx9_value_reset_string_cursor(pWorker); + } + } + if( pvArr ){ + pCur = pvArr; + } + } + while ( zIn < zEnd && zIn[0] != ']' ){ + zIn++; + } + } + zIn++; + } + if( !is_array ){ + iLen = (int)(zIn-zCur); + } + /* Trim the key */ + SyStringInitFromBuf(&sEntry, zCur, iLen); + SyStringFullTrim(&sEntry); + if( sEntry.nByte > 0 ){ + if( !is_array ){ + /* Save the key name */ + jx9_value_string(pWorker, sEntry.zString, (int)sEntry.nByte); + } + /* extract key value */ + jx9_value_reset_string_cursor(pValue); + zIn++; /* '=' */ + while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && SyisSpace(zIn[0]) ){ + zIn++; + } + if( zIn < zEnd ){ + zCur = zIn; + c = zIn[0]; + if( c == '"' || c == '\'' ){ + zIn++; + /* Delimit the value */ + while( zIn < zEnd ){ + if ( zIn[0] == c && zIn[-1] != '\\' ){ + break; + } + zIn++; + } + if( zIn < zEnd ){ + zIn++; + } + }else{ + while( zIn < zEnd ){ + if( zIn[0] == '\n' ){ + if( zIn[-1] != '\\' ){ + break; + } + }else if( zIn[0] == ';' || zIn[0] == '#' ){ + /* Inline comments */ + break; + } + zIn++; + } + } + /* Trim the value */ + SyStringInitFromBuf(&sEntry, zCur, (int)(zIn-zCur)); + SyStringFullTrim(&sEntry); + if( c == '"' || c == '\'' ){ + SyStringTrimLeadingChar(&sEntry, c); + SyStringTrimTrailingChar(&sEntry, c); + } + if( sEntry.nByte > 0 ){ + jx9_value_string(pValue, sEntry.zString, (int)sEntry.nByte); + } + /* Insert the key and it's value */ + jx9_array_add_elem(pCur, is_array ? 0 /*Automatic index assign */: pWorker, pValue); + } + }else{ + while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && ( SyisSpace(zIn[0]) || zIn[0] == '=' ) ){ + zIn++; + } + } + pCur = pOldCur; + } + } + SyHashRelease(&sHash); + /* Return the parse of the INI string */ + jx9_result_value(pCtx, pArray); + return SXRET_OK; +} +/* + * array parse_ini_string(string $ini[, bool $process_sections = false[, int $scanner_mode = INI_SCANNER_NORMAL ]]) + * Parse a configuration string. + * Parameters + * $ini + * The contents of the ini file being parsed. + * $process_sections + * By setting the process_sections parameter to TRUE, you get a multidimensional array, with the section names + * and settings included. The default for process_sections is FALSE. + * $scanner_mode (Not used) + * Can either be INI_SCANNER_NORMAL (default) or INI_SCANNER_RAW. If INI_SCANNER_RAW is supplied + * then option values will not be parsed. + * Return + * The settings are returned as an associative array on success, and FALSE on failure. + */ +static int jx9Builtin_parse_ini_string(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zIni; + int nByte; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid arguments, return FALSE*/ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the raw INI buffer */ + zIni = jx9_value_to_string(apArg[0], &nByte); + /* Process the INI buffer*/ + jx9ParseIniString(pCtx, zIni, (sxu32)nByte, (nArg > 1) ? jx9_value_to_bool(apArg[1]) : 0); + return JX9_OK; +} +/* + * Ctype Functions. + * Authors: + * Symisc Systems, devel@symisc.net. + * Copyright (C) Symisc Systems, http://jx9.symisc.net + * Status: + * Stable. + */ +/* + * bool ctype_alnum(string $text) + * Checks if all of the characters in the provided string, text, are alphanumeric. + * Parameters + * $text + * The tested string. + * Return + * TRUE if every character in text is either a letter or a digit, FALSE otherwise. + */ +static int jx9Builtin_ctype_alnum(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const unsigned char *zIn, *zEnd; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + if( nLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + /* If we reach the end of the string, then the test succeeded. */ + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + if( !SyisAlphaNum(zIn[0]) ){ + break; + } + /* Point to the next character */ + zIn++; + } + /* The test failed, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; +} +/* + * bool ctype_alpha(string $text) + * Checks if all of the characters in the provided string, text, are alphabetic. + * Parameters + * $text + * The tested string. + * Return + * TRUE if every character in text is a letter from the current locale, FALSE otherwise. + */ +static int jx9Builtin_ctype_alpha(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const unsigned char *zIn, *zEnd; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + if( nLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + /* If we reach the end of the string, then the test succeeded. */ + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + if( !SyisAlpha(zIn[0]) ){ + break; + } + /* Point to the next character */ + zIn++; + } + /* The test failed, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; +} +/* + * bool ctype_cntrl(string $text) + * Checks if all of the characters in the provided string, text, are control characters. + * Parameters + * $text + * The tested string. + * Return + * TRUE if every character in text is a control characters, FALSE otherwise. + */ +static int jx9Builtin_ctype_cntrl(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const unsigned char *zIn, *zEnd; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + if( nLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + /* If we reach the end of the string, then the test succeeded. */ + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + if( zIn[0] >= 0xc0 ){ + /* UTF-8 stream */ + break; + } + if( !SyisCtrl(zIn[0]) ){ + break; + } + /* Point to the next character */ + zIn++; + } + /* The test failed, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; +} +/* + * bool ctype_digit(string $text) + * Checks if all of the characters in the provided string, text, are numerical. + * Parameters + * $text + * The tested string. + * Return + * TRUE if every character in the string text is a decimal digit, FALSE otherwise. + */ +static int jx9Builtin_ctype_digit(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const unsigned char *zIn, *zEnd; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + if( nLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + /* If we reach the end of the string, then the test succeeded. */ + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + if( zIn[0] >= 0xc0 ){ + /* UTF-8 stream */ + break; + } + if( !SyisDigit(zIn[0]) ){ + break; + } + /* Point to the next character */ + zIn++; + } + /* The test failed, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; +} +/* + * bool ctype_xdigit(string $text) + * Check for character(s) representing a hexadecimal digit. + * Parameters + * $text + * The tested string. + * Return + * Returns TRUE if every character in text is a hexadecimal 'digit', that is + * a decimal digit or a character from [A-Fa-f] , FALSE otherwise. + */ +static int jx9Builtin_ctype_xdigit(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const unsigned char *zIn, *zEnd; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + if( nLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + /* If we reach the end of the string, then the test succeeded. */ + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + if( zIn[0] >= 0xc0 ){ + /* UTF-8 stream */ + break; + } + if( !SyisHex(zIn[0]) ){ + break; + } + /* Point to the next character */ + zIn++; + } + /* The test failed, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; +} +/* + * bool ctype_graph(string $text) + * Checks if all of the characters in the provided string, text, creates visible output. + * Parameters + * $text + * The tested string. + * Return + * Returns TRUE if every character in text is printable and actually creates visible output + * (no white space), FALSE otherwise. + */ +static int jx9Builtin_ctype_graph(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const unsigned char *zIn, *zEnd; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + if( nLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + /* If we reach the end of the string, then the test succeeded. */ + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + if( zIn[0] >= 0xc0 ){ + /* UTF-8 stream */ + break; + } + if( !SyisGraph(zIn[0]) ){ + break; + } + /* Point to the next character */ + zIn++; + } + /* The test failed, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; +} +/* + * bool ctype_print(string $text) + * Checks if all of the characters in the provided string, text, are printable. + * Parameters + * $text + * The tested string. + * Return + * Returns TRUE if every character in text will actually create output (including blanks). + * Returns FALSE if text contains control characters or characters that do not have any output + * or control function at all. + */ +static int jx9Builtin_ctype_print(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const unsigned char *zIn, *zEnd; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + if( nLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + /* If we reach the end of the string, then the test succeeded. */ + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + if( zIn[0] >= 0xc0 ){ + /* UTF-8 stream */ + break; + } + if( !SyisPrint(zIn[0]) ){ + break; + } + /* Point to the next character */ + zIn++; + } + /* The test failed, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; +} +/* + * bool ctype_punct(string $text) + * Checks if all of the characters in the provided string, text, are punctuation character. + * Parameters + * $text + * The tested string. + * Return + * Returns TRUE if every character in text is printable, but neither letter + * digit or blank, FALSE otherwise. + */ +static int jx9Builtin_ctype_punct(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const unsigned char *zIn, *zEnd; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + if( nLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + /* If we reach the end of the string, then the test succeeded. */ + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + if( zIn[0] >= 0xc0 ){ + /* UTF-8 stream */ + break; + } + if( !SyisPunct(zIn[0]) ){ + break; + } + /* Point to the next character */ + zIn++; + } + /* The test failed, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; +} +/* + * bool ctype_space(string $text) + * Checks if all of the characters in the provided string, text, creates whitespace. + * Parameters + * $text + * The tested string. + * Return + * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. + * Besides the blank character this also includes tab, vertical tab, line feed, carriage return + * and form feed characters. + */ +static int jx9Builtin_ctype_space(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const unsigned char *zIn, *zEnd; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + if( nLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + /* If we reach the end of the string, then the test succeeded. */ + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + if( zIn[0] >= 0xc0 ){ + /* UTF-8 stream */ + break; + } + if( !SyisSpace(zIn[0]) ){ + break; + } + /* Point to the next character */ + zIn++; + } + /* The test failed, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; +} +/* + * bool ctype_lower(string $text) + * Checks if all of the characters in the provided string, text, are lowercase letters. + * Parameters + * $text + * The tested string. + * Return + * Returns TRUE if every character in text is a lowercase letter in the current locale. + */ +static int jx9Builtin_ctype_lower(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const unsigned char *zIn, *zEnd; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + if( nLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + /* If we reach the end of the string, then the test succeeded. */ + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + if( !SyisLower(zIn[0]) ){ + break; + } + /* Point to the next character */ + zIn++; + } + /* The test failed, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; +} +/* + * bool ctype_upper(string $text) + * Checks if all of the characters in the provided string, text, are uppercase letters. + * Parameters + * $text + * The tested string. + * Return + * Returns TRUE if every character in text is a uppercase letter in the current locale. + */ +static int jx9Builtin_ctype_upper(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const unsigned char *zIn, *zEnd; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the target string */ + zIn = (const unsigned char *)jx9_value_to_string(apArg[0], &nLen); + zEnd = &zIn[nLen]; + if( nLen < 1 ){ + /* Empty string, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the requested operation */ + for(;;){ + if( zIn >= zEnd ){ + /* If we reach the end of the string, then the test succeeded. */ + jx9_result_bool(pCtx, 1); + return JX9_OK; + } + if( !SyisUpper(zIn[0]) ){ + break; + } + /* Point to the next character */ + zIn++; + } + /* The test failed, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; +} +/* + * Date/Time functions + * Authors: + * Symisc Systems, devel@symisc.net. + * Copyright (C) Symisc Systems, http://jx9.symisc.net + * Status: + * Devel. + */ +#include +#ifdef __WINNT__ +/* GetSystemTime() */ +#include +#ifdef _WIN32_WCE +/* +** WindowsCE does not have a localtime() function. So create a +** substitute. +** Taken from the SQLite3 source tree. +** Status: Public domain +*/ +struct tm *__cdecl localtime(const time_t *t) +{ + static struct tm y; + FILETIME uTm, lTm; + SYSTEMTIME pTm; + jx9_int64 t64; + t64 = *t; + t64 = (t64 + 11644473600)*10000000; + uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF); + uTm.dwHighDateTime= (DWORD)(t64 >> 32); + FileTimeToLocalFileTime(&uTm, &lTm); + FileTimeToSystemTime(&lTm, &pTm); + y.tm_year = pTm.wYear - 1900; + y.tm_mon = pTm.wMonth - 1; + y.tm_wday = pTm.wDayOfWeek; + y.tm_mday = pTm.wDay; + y.tm_hour = pTm.wHour; + y.tm_min = pTm.wMinute; + y.tm_sec = pTm.wSecond; + return &y; +} +#endif /*_WIN32_WCE */ +#elif defined(__UNIXES__) +#include +#endif /* __WINNT__*/ + /* + * int64 time(void) + * Current Unix timestamp + * Parameters + * None. + * Return + * Returns the current time measured in the number of seconds + * since the Unix Epoch (January 1 1970 00:00:00 GMT). + */ +static int jx9Builtin_time(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + time_t tt; + SXUNUSED(nArg); /* cc warning */ + SXUNUSED(apArg); + /* Extract the current time */ + time(&tt); + /* Return as 64-bit integer */ + jx9_result_int64(pCtx, (jx9_int64)tt); + return JX9_OK; +} +/* + * string/float microtime([ bool $get_as_float = false ]) + * microtime() returns the current Unix timestamp with microseconds. + * Parameters + * $get_as_float + * If used and set to TRUE, microtime() will return a float instead of a string + * as described in the return values section below. + * Return + * By default, microtime() returns a string in the form "msec sec", where sec + * is the current time measured in the number of seconds since the Unix + * epoch (0:00:00 January 1, 1970 GMT), and msec is the number of microseconds + * that have elapsed since sec expressed in seconds. + * If get_as_float is set to TRUE, then microtime() returns a float, which represents + * the current time in seconds since the Unix epoch accurate to the nearest microsecond. + */ +static int jx9Builtin_microtime(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int bFloat = 0; + sytime sTime; +#if defined(__UNIXES__) + struct timeval tv; + gettimeofday(&tv, 0); + sTime.tm_sec = (long)tv.tv_sec; + sTime.tm_usec = (long)tv.tv_usec; +#else + time_t tt; + time(&tt); + sTime.tm_sec = (long)tt; + sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC); +#endif /* __UNIXES__ */ + if( nArg > 0 ){ + bFloat = jx9_value_to_bool(apArg[0]); + } + if( bFloat ){ + /* Return as float */ + jx9_result_double(pCtx, (double)sTime.tm_sec); + }else{ + /* Return as string */ + jx9_result_string_format(pCtx, "%ld %ld", sTime.tm_usec, sTime.tm_sec); + } + return JX9_OK; +} +/* + * array getdate ([ int $timestamp = time() ]) + * Get date/time information. + * Parameter + * $timestamp: The optional timestamp parameter is an integer Unix timestamp + * that defaults to the current local time if a timestamp is not given. + * In other words, it defaults to the value of time(). + * Returns + * Returns an associative array of information related to the timestamp. + * Elements from the returned associative array are as follows: + * KEY VALUE + * --------- ------- + * "seconds" Numeric representation of seconds 0 to 59 + * "minutes" Numeric representation of minutes 0 to 59 + * "hours" Numeric representation of hours 0 to 23 + * "mday" Numeric representation of the day of the month 1 to 31 + * "wday" Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday) + * "mon" Numeric representation of a month 1 through 12 + * "year" A full numeric representation of a year, 4 digits Examples: 1999 or 2003 + * "yday" Numeric representation of the day of the year 0 through 365 + * "weekday" A full textual representation of the day of the week Sunday through Saturday + * "month" A full textual representation of a month, such as January or March January through December + * 0 Seconds since the Unix Epoch, similar to the values returned by time() and used by date(). + * NOTE: + * NULL is returned on failure. + */ +static int jx9Builtin_getdate(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_value *pValue, *pArray; + Sytm sTm; + if( nArg < 1 ){ +#ifdef __WINNT__ + SYSTEMTIME sOS; + GetSystemTime(&sOS); + SYSTEMTIME_TO_SYTM(&sOS, &sTm); +#else + struct tm *pTm; + time_t t; + time(&t); + pTm = localtime(&t); + STRUCT_TM_TO_SYTM(pTm, &sTm); +#endif + }else{ + /* Use the given timestamp */ + time_t t; + struct tm *pTm; +#ifdef __WINNT__ +#ifdef _MSC_VER +#if _MSC_VER >= 1400 /* Visual Studio 2005 and up */ +#pragma warning(disable:4996) /* _CRT_SECURE...*/ +#endif +#endif +#endif + if( jx9_value_is_int(apArg[0]) ){ + t = (time_t)jx9_value_to_int64(apArg[0]); + pTm = localtime(&t); + if( pTm == 0 ){ + time(&t); + } + }else{ + time(&t); + } + pTm = localtime(&t); + STRUCT_TM_TO_SYTM(pTm, &sTm); + } + /* Element value */ + pValue = jx9_context_new_scalar(pCtx); + if( pValue == 0 ){ + /* Return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Create a new array */ + pArray = jx9_context_new_array(pCtx); + if( pArray == 0 ){ + /* Return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Fill the array */ + /* Seconds */ + jx9_value_int(pValue, sTm.tm_sec); + jx9_array_add_strkey_elem(pArray, "seconds", pValue); + /* Minutes */ + jx9_value_int(pValue, sTm.tm_min); + jx9_array_add_strkey_elem(pArray, "minutes", pValue); + /* Hours */ + jx9_value_int(pValue, sTm.tm_hour); + jx9_array_add_strkey_elem(pArray, "hours", pValue); + /* mday */ + jx9_value_int(pValue, sTm.tm_mday); + jx9_array_add_strkey_elem(pArray, "mday", pValue); + /* wday */ + jx9_value_int(pValue, sTm.tm_wday); + jx9_array_add_strkey_elem(pArray, "wday", pValue); + /* mon */ + jx9_value_int(pValue, sTm.tm_mon+1); + jx9_array_add_strkey_elem(pArray, "mon", pValue); + /* year */ + jx9_value_int(pValue, sTm.tm_year); + jx9_array_add_strkey_elem(pArray, "year", pValue); + /* yday */ + jx9_value_int(pValue, sTm.tm_yday); + jx9_array_add_strkey_elem(pArray, "yday", pValue); + /* Weekday */ + jx9_value_string(pValue, SyTimeGetDay(sTm.tm_wday), -1); + jx9_array_add_strkey_elem(pArray, "weekday", pValue); + /* Month */ + jx9_value_reset_string_cursor(pValue); + jx9_value_string(pValue, SyTimeGetMonth(sTm.tm_mon), -1); + jx9_array_add_strkey_elem(pArray, "month", pValue); + /* Seconds since the epoch */ + jx9_value_int64(pValue, (jx9_int64)time(0)); + jx9_array_add_elem(pArray, 0 /* Index zero */, pValue); + /* Return the freshly created array */ + jx9_result_value(pCtx, pArray); + return JX9_OK; +} +/* + * mixed gettimeofday([ bool $return_float = false ] ) + * Returns an associative array containing the data returned from the system call. + * Parameters + * $return_float + * When set to TRUE, a float instead of an array is returned. + * Return + * By default an array is returned. If return_float is set, then + * a float is returned. + */ +static int jx9Builtin_gettimeofday(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + int bFloat = 0; + sytime sTime; +#if defined(__UNIXES__) + struct timeval tv; + gettimeofday(&tv, 0); + sTime.tm_sec = (long)tv.tv_sec; + sTime.tm_usec = (long)tv.tv_usec; +#else + time_t tt; + time(&tt); + sTime.tm_sec = (long)tt; + sTime.tm_usec = (long)(tt%SX_USEC_PER_SEC); +#endif /* __UNIXES__ */ + if( nArg > 0 ){ + bFloat = jx9_value_to_bool(apArg[0]); + } + if( bFloat ){ + /* Return as float */ + jx9_result_double(pCtx, (double)sTime.tm_sec); + }else{ + /* Return an associative array */ + jx9_value *pValue, *pArray; + /* Create a new array */ + pArray = jx9_context_new_array(pCtx); + /* Element value */ + pValue = jx9_context_new_scalar(pCtx); + if( pValue == 0 || pArray == 0 ){ + /* Return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Fill the array */ + /* sec */ + jx9_value_int64(pValue, sTime.tm_sec); + jx9_array_add_strkey_elem(pArray, "sec", pValue); + /* usec */ + jx9_value_int64(pValue, sTime.tm_usec); + jx9_array_add_strkey_elem(pArray, "usec", pValue); + /* Return the array */ + jx9_result_value(pCtx, pArray); + } + return JX9_OK; +} +/* Check if the given year is leap or not */ +#define IS_LEAP_YEAR(YEAR) (YEAR % 400 ? ( YEAR % 100 ? ( YEAR % 4 ? 0 : 1 ) : 0 ) : 1) +/* ISO-8601 numeric representation of the day of the week */ +static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 }; +/* + * Format a given date string. + * Supported format: (Taken from JX9 online docs) + * character Description + * d Day of the month + * D A textual representation of a days + * j Day of the month without leading zeros + * l A full textual representation of the day of the week + * N ISO-8601 numeric representation of the day of the week + * w Numeric representation of the day of the week + * z The day of the year (starting from 0) + * F A full textual representation of a month, such as January or March + * m Numeric representation of a month, with leading zeros 01 through 12 + * M A short textual representation of a month, three letters Jan through Dec + * n Numeric representation of a month, without leading zeros 1 through 12 + * t Number of days in the given month 28 through 31 + * L Whether it's a leap year 1 if it is a leap year, 0 otherwise. + * o ISO-8601 year number. This has the same value as Y, except that if the ISO week number + * (W) belongs to the previous or next year, that year is used instead. (added in JX9 5.1.0) Examples: 1999 or 2003 + * Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003 + * y A two digit representation of a year Examples: 99 or 03 + * a Lowercase Ante meridiem and Post meridiem am or pm + * A Uppercase Ante meridiem and Post meridiem AM or PM + * g 12-hour format of an hour without leading zeros 1 through 12 + * G 24-hour format of an hour without leading zeros 0 through 23 + * h 12-hour format of an hour with leading zeros 01 through 12 + * H 24-hour format of an hour with leading zeros 00 through 23 + * i Minutes with leading zeros 00 to 59 + * s Seconds, with leading zeros 00 through 59 + * u Microseconds Example: 654321 + * e Timezone identifier Examples: UTC, GMT, Atlantic/Azores + * I (capital i) Whether or not the date is in daylight saving time 1 if Daylight Saving Time, 0 otherwise. + * r RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 +0200 + * U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) + * S English ordinal suffix for the day of the month, 2 characters + * O Difference to Greenwich time (GMT) in hours + * Z Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those + * east of UTC is always positive. + * c ISO 8601 date + */ +static sxi32 DateFormat(jx9_context *pCtx, const char *zIn, int nLen, Sytm *pTm) +{ + const char *zEnd = &zIn[nLen]; + const char *zCur; + /* Start the format process */ + for(;;){ + if( zIn >= zEnd ){ + /* No more input to process */ + break; + } + switch(zIn[0]){ + case 'd': + /* Day of the month, 2 digits with leading zeros */ + jx9_result_string_format(pCtx, "%02d", pTm->tm_mday); + break; + case 'D': + /*A textual representation of a day, three letters*/ + zCur = SyTimeGetDay(pTm->tm_wday); + jx9_result_string(pCtx, zCur, 3); + break; + case 'j': + /* Day of the month without leading zeros */ + jx9_result_string_format(pCtx, "%d", pTm->tm_mday); + break; + case 'l': + /* A full textual representation of the day of the week */ + zCur = SyTimeGetDay(pTm->tm_wday); + jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/); + break; + case 'N':{ + /* ISO-8601 numeric representation of the day of the week */ + jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]); + break; + } + case 'w': + /*Numeric representation of the day of the week*/ + jx9_result_string_format(pCtx, "%d", pTm->tm_wday); + break; + case 'z': + /*The day of the year*/ + jx9_result_string_format(pCtx, "%d", pTm->tm_yday); + break; + case 'F': + /*A full textual representation of a month, such as January or March*/ + zCur = SyTimeGetMonth(pTm->tm_mon); + jx9_result_string(pCtx, zCur, -1/*Compute length automatically*/); + break; + case 'm': + /*Numeric representation of a month, with leading zeros*/ + jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1); + break; + case 'M': + /*A short textual representation of a month, three letters*/ + zCur = SyTimeGetMonth(pTm->tm_mon); + jx9_result_string(pCtx, zCur, 3); + break; + case 'n': + /*Numeric representation of a month, without leading zeros*/ + jx9_result_string_format(pCtx, "%d", pTm->tm_mon + 1); + break; + case 't':{ + static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int nDays = aMonDays[pTm->tm_mon % 12 ]; + if( pTm->tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(pTm->tm_year) ){ + nDays = 28; + } + /*Number of days in the given month*/ + jx9_result_string_format(pCtx, "%d", nDays); + break; + } + case 'L':{ + int isLeap = IS_LEAP_YEAR(pTm->tm_year); + /* Whether it's a leap year */ + jx9_result_string_format(pCtx, "%d", isLeap); + break; + } + case 'o': + /* ISO-8601 year number.*/ + jx9_result_string_format(pCtx, "%4d", pTm->tm_year); + break; + case 'Y': + /* A full numeric representation of a year, 4 digits */ + jx9_result_string_format(pCtx, "%4d", pTm->tm_year); + break; + case 'y': + /*A two digit representation of a year*/ + jx9_result_string_format(pCtx, "%02d", pTm->tm_year%100); + break; + case 'a': + /* Lowercase Ante meridiem and Post meridiem */ + jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", 2); + break; + case 'A': + /* Uppercase Ante meridiem and Post meridiem */ + jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", 2); + break; + case 'g': + /* 12-hour format of an hour without leading zeros*/ + jx9_result_string_format(pCtx, "%d", 1+(pTm->tm_hour%12)); + break; + case 'G': + /* 24-hour format of an hour without leading zeros */ + jx9_result_string_format(pCtx, "%d", pTm->tm_hour); + break; + case 'h': + /* 12-hour format of an hour with leading zeros */ + jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12)); + break; + case 'H': + /* 24-hour format of an hour with leading zeros */ + jx9_result_string_format(pCtx, "%02d", pTm->tm_hour); + break; + case 'i': + /* Minutes with leading zeros */ + jx9_result_string_format(pCtx, "%02d", pTm->tm_min); + break; + case 's': + /* second with leading zeros */ + jx9_result_string_format(pCtx, "%02d", pTm->tm_sec); + break; + case 'u': + /* Microseconds */ + jx9_result_string_format(pCtx, "%u", pTm->tm_sec * SX_USEC_PER_SEC); + break; + case 'S':{ + /* English ordinal suffix for the day of the month, 2 characters */ + static const char zSuffix[] = "thstndrdthththththth"; + int v = pTm->tm_mday; + jx9_result_string(pCtx, &zSuffix[2 * (int)(v / 10 % 10 != 1 ? v % 10 : 0)], (int)sizeof(char) * 2); + break; + } + case 'e': + /* Timezone identifier */ + zCur = pTm->tm_zone; + if( zCur == 0 ){ + /* Assume GMT */ + zCur = "GMT"; + } + jx9_result_string(pCtx, zCur, -1); + break; + case 'I': + /* Whether or not the date is in daylight saving time */ +#ifdef __WINNT__ +#ifdef _MSC_VER +#ifndef _WIN32_WCE + _get_daylight(&pTm->tm_isdst); +#endif +#endif +#endif + jx9_result_string_format(pCtx, "%d", pTm->tm_isdst == 1); + break; + case 'r': + /* RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 */ + jx9_result_string_format(pCtx, "%.3s, %02d %.3s %4d %02d:%02d:%02d", + SyTimeGetDay(pTm->tm_wday), + pTm->tm_mday, + SyTimeGetMonth(pTm->tm_mon), + pTm->tm_year, + pTm->tm_hour, + pTm->tm_min, + pTm->tm_sec + ); + break; + case 'U':{ + time_t tt; + /* Seconds since the Unix Epoch */ + time(&tt); + jx9_result_string_format(pCtx, "%u", (unsigned int)tt); + break; + } + case 'O': + case 'P': + /* Difference to Greenwich time (GMT) in hours */ + jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff); + break; + case 'Z': + /* Timezone offset in seconds. The offset for timezones west of UTC + * is always negative, and for those east of UTC is always positive. + */ + jx9_result_string_format(pCtx, "%+05d", pTm->tm_gmtoff); + break; + case 'c': + /* ISO 8601 date */ + jx9_result_string_format(pCtx, "%4d-%02d-%02dT%02d:%02d:%02d%+05d", + pTm->tm_year, + pTm->tm_mon+1, + pTm->tm_mday, + pTm->tm_hour, + pTm->tm_min, + pTm->tm_sec, + pTm->tm_gmtoff + ); + break; + case '\\': + zIn++; + /* Expand verbatim */ + if( zIn < zEnd ){ + jx9_result_string(pCtx, zIn, (int)sizeof(char)); + } + break; + default: + /* Unknown format specifer, expand verbatim */ + jx9_result_string(pCtx, zIn, (int)sizeof(char)); + break; + } + /* Point to the next character */ + zIn++; + } + return SXRET_OK; +} +/* + * JX9 implementation of the strftime() function. + * The following formats are supported: + * %a An abbreviated textual representation of the day + * %A A full textual representation of the day + * %d Two-digit day of the month (with leading zeros) + * %e Day of the month, with a space preceding single digits. + * %j Day of the year, 3 digits with leading zeros + * %u ISO-8601 numeric representation of the day of the week 1 (for Monday) though 7 (for Sunday) + * %w Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday) + * %U Week number of the given year, starting with the first Sunday as the first week + * %V ISO-8601:1988 week number of the given year, starting with the first week of the year with at least + * 4 weekdays, with Monday being the start of the week. + * %W A numeric representation of the week of the year + * %b Abbreviated month name, based on the locale + * %B Full month name, based on the locale + * %h Abbreviated month name, based on the locale (an alias of %b) + * %m Two digit representation of the month + * %C Two digit representation of the century (year divided by 100, truncated to an integer) + * %g Two digit representation of the year going by ISO-8601:1988 standards (see %V) + * %G The full four-digit version of %g + * %y Two digit representation of the year + * %Y Four digit representation for the year + * %H Two digit representation of the hour in 24-hour format + * %I Two digit representation of the hour in 12-hour format + * %l (lower-case 'L') Hour in 12-hour format, with a space preceeding single digits + * %M Two digit representation of the minute + * %p UPPER-CASE 'AM' or 'PM' based on the given time + * %P lower-case 'am' or 'pm' based on the given time + * %r Same as "%I:%M:%S %p" + * %R Same as "%H:%M" + * %S Two digit representation of the second + * %T Same as "%H:%M:%S" + * %X Preferred time representation based on locale, without the date + * %z Either the time zone offset from UTC or the abbreviation + * %Z The time zone offset/abbreviation option NOT given by %z + * %c Preferred date and time stamp based on local + * %D Same as "%m/%d/%y" + * %F Same as "%Y-%m-%d" + * %s Unix Epoch Time timestamp (same as the time() function) + * %x Preferred date representation based on locale, without the time + * %n A newline character ("\n") + * %t A Tab character ("\t") + * %% A literal percentage character ("%") + */ +static int jx9Strftime( + jx9_context *pCtx, /* Call context */ + const char *zIn, /* Input string */ + int nLen, /* Input length */ + Sytm *pTm /* Parse of the given time */ + ) +{ + const char *zCur, *zEnd = &zIn[nLen]; + int c; + /* Start the format process */ + for(;;){ + zCur = zIn; + while(zIn < zEnd && zIn[0] != '%' ){ + zIn++; + } + if( zIn > zCur ){ + /* Consume input verbatim */ + jx9_result_string(pCtx, zCur, (int)(zIn-zCur)); + } + zIn++; /* Jump the percent sign */ + if( zIn >= zEnd ){ + /* No more input to process */ + break; + } + c = zIn[0]; + /* Act according to the current specifer */ + switch(c){ + case '%': + /* A literal percentage character ("%") */ + jx9_result_string(pCtx, "%", (int)sizeof(char)); + break; + case 't': + /* A Tab character */ + jx9_result_string(pCtx, "\t", (int)sizeof(char)); + break; + case 'n': + /* A newline character */ + jx9_result_string(pCtx, "\n", (int)sizeof(char)); + break; + case 'a': + /* An abbreviated textual representation of the day */ + jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), (int)sizeof(char)*3); + break; + case 'A': + /* A full textual representation of the day */ + jx9_result_string(pCtx, SyTimeGetDay(pTm->tm_wday), -1/*Compute length automatically*/); + break; + case 'e': + /* Day of the month, 2 digits with leading space for single digit*/ + jx9_result_string_format(pCtx, "%2d", pTm->tm_mday); + break; + case 'd': + /* Two-digit day of the month (with leading zeros) */ + jx9_result_string_format(pCtx, "%02d", pTm->tm_mon+1); + break; + case 'j': + /*The day of the year, 3 digits with leading zeros*/ + jx9_result_string_format(pCtx, "%03d", pTm->tm_yday); + break; + case 'u': + /* ISO-8601 numeric representation of the day of the week */ + jx9_result_string_format(pCtx, "%d", aISO8601[pTm->tm_wday % 7 ]); + break; + case 'w': + /* Numeric representation of the day of the week */ + jx9_result_string_format(pCtx, "%d", pTm->tm_wday); + break; + case 'b': + case 'h': + /*A short textual representation of a month, three letters (Not based on locale)*/ + jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), (int)sizeof(char)*3); + break; + case 'B': + /* Full month name (Not based on locale) */ + jx9_result_string(pCtx, SyTimeGetMonth(pTm->tm_mon), -1/*Compute length automatically*/); + break; + case 'm': + /*Numeric representation of a month, with leading zeros*/ + jx9_result_string_format(pCtx, "%02d", pTm->tm_mon + 1); + break; + case 'C': + /* Two digit representation of the century */ + jx9_result_string_format(pCtx, "%2d", pTm->tm_year/100); + break; + case 'y': + case 'g': + /* Two digit representation of the year */ + jx9_result_string_format(pCtx, "%2d", pTm->tm_year%100); + break; + case 'Y': + case 'G': + /* Four digit representation of the year */ + jx9_result_string_format(pCtx, "%4d", pTm->tm_year); + break; + case 'I': + /* 12-hour format of an hour with leading zeros */ + jx9_result_string_format(pCtx, "%02d", 1+(pTm->tm_hour%12)); + break; + case 'l': + /* 12-hour format of an hour with leading space */ + jx9_result_string_format(pCtx, "%2d", 1+(pTm->tm_hour%12)); + break; + case 'H': + /* 24-hour format of an hour with leading zeros */ + jx9_result_string_format(pCtx, "%02d", pTm->tm_hour); + break; + case 'M': + /* Minutes with leading zeros */ + jx9_result_string_format(pCtx, "%02d", pTm->tm_min); + break; + case 'S': + /* Seconds with leading zeros */ + jx9_result_string_format(pCtx, "%02d", pTm->tm_sec); + break; + case 'z': + case 'Z': + /* Timezone identifier */ + zCur = pTm->tm_zone; + if( zCur == 0 ){ + /* Assume GMT */ + zCur = "GMT"; + } + jx9_result_string(pCtx, zCur, -1); + break; + case 'T': + case 'X': + /* Same as "%H:%M:%S" */ + jx9_result_string_format(pCtx, "%02d:%02d:%02d", pTm->tm_hour, pTm->tm_min, pTm->tm_sec); + break; + case 'R': + /* Same as "%H:%M" */ + jx9_result_string_format(pCtx, "%02d:%02d", pTm->tm_hour, pTm->tm_min); + break; + case 'P': + /* Lowercase Ante meridiem and Post meridiem */ + jx9_result_string(pCtx, pTm->tm_hour > 12 ? "pm" : "am", (int)sizeof(char)*2); + break; + case 'p': + /* Uppercase Ante meridiem and Post meridiem */ + jx9_result_string(pCtx, pTm->tm_hour > 12 ? "PM" : "AM", (int)sizeof(char)*2); + break; + case 'r': + /* Same as "%I:%M:%S %p" */ + jx9_result_string_format(pCtx, "%02d:%02d:%02d %s", + 1+(pTm->tm_hour%12), + pTm->tm_min, + pTm->tm_sec, + pTm->tm_hour > 12 ? "PM" : "AM" + ); + break; + case 'D': + case 'x': + /* Same as "%m/%d/%y" */ + jx9_result_string_format(pCtx, "%02d/%02d/%02d", + pTm->tm_mon+1, + pTm->tm_mday, + pTm->tm_year%100 + ); + break; + case 'F': + /* Same as "%Y-%m-%d" */ + jx9_result_string_format(pCtx, "%d-%02d-%02d", + pTm->tm_year, + pTm->tm_mon+1, + pTm->tm_mday + ); + break; + case 'c': + jx9_result_string_format(pCtx, "%d-%02d-%02d %02d:%02d:%02d", + pTm->tm_year, + pTm->tm_mon+1, + pTm->tm_mday, + pTm->tm_hour, + pTm->tm_min, + pTm->tm_sec + ); + break; + case 's':{ + time_t tt; + /* Seconds since the Unix Epoch */ + time(&tt); + jx9_result_string_format(pCtx, "%u", (unsigned int)tt); + break; + } + default: + /* unknown specifer, simply ignore*/ + break; + } + /* Advance the cursor */ + zIn++; + } + return SXRET_OK; +} +/* + * string date(string $format [, int $timestamp = time() ] ) + * Returns a string formatted according to the given format string using + * the given integer timestamp or the current time if no timestamp is given. + * In other words, timestamp is optional and defaults to the value of time(). + * Parameters + * $format + * The format of the outputted date string (See code above) + * $timestamp + * The optional timestamp parameter is an integer Unix timestamp + * that defaults to the current local time if a timestamp is not given. + * In other words, it defaults to the value of time(). + * Return + * A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned. + */ +static int jx9Builtin_date(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zFormat; + int nLen; + Sytm sTm; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + zFormat = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Don't bother processing return the empty string */ + jx9_result_string(pCtx, "", 0); + } + if( nArg < 2 ){ +#ifdef __WINNT__ + SYSTEMTIME sOS; + GetSystemTime(&sOS); + SYSTEMTIME_TO_SYTM(&sOS, &sTm); +#else + struct tm *pTm; + time_t t; + time(&t); + pTm = localtime(&t); + STRUCT_TM_TO_SYTM(pTm, &sTm); +#endif + }else{ + /* Use the given timestamp */ + time_t t; + struct tm *pTm; + if( jx9_value_is_int(apArg[1]) ){ + t = (time_t)jx9_value_to_int64(apArg[1]); + pTm = localtime(&t); + if( pTm == 0 ){ + time(&t); + } + }else{ + time(&t); + } + pTm = localtime(&t); + STRUCT_TM_TO_SYTM(pTm, &sTm); + } + /* Format the given string */ + DateFormat(pCtx, zFormat, nLen, &sTm); + return JX9_OK; +} +/* + * string strftime(string $format [, int $timestamp = time() ] ) + * Format a local time/date (PLATFORM INDEPENDANT IMPLEENTATION NOT BASED ON LOCALE) + * Parameters + * $format + * The format of the outputted date string (See code above) + * $timestamp + * The optional timestamp parameter is an integer Unix timestamp + * that defaults to the current local time if a timestamp is not given. + * In other words, it defaults to the value of time(). + * Return + * Returns a string formatted according format using the given timestamp + * or the current local time if no timestamp is given. + */ +static int jx9Builtin_strftime(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zFormat; + int nLen; + Sytm sTm; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + zFormat = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Don't bother processing return FALSE */ + jx9_result_bool(pCtx, 0); + } + if( nArg < 2 ){ +#ifdef __WINNT__ + SYSTEMTIME sOS; + GetSystemTime(&sOS); + SYSTEMTIME_TO_SYTM(&sOS, &sTm); +#else + struct tm *pTm; + time_t t; + time(&t); + pTm = localtime(&t); + STRUCT_TM_TO_SYTM(pTm, &sTm); +#endif + }else{ + /* Use the given timestamp */ + time_t t; + struct tm *pTm; + if( jx9_value_is_int(apArg[1]) ){ + t = (time_t)jx9_value_to_int64(apArg[1]); + pTm = localtime(&t); + if( pTm == 0 ){ + time(&t); + } + }else{ + time(&t); + } + pTm = localtime(&t); + STRUCT_TM_TO_SYTM(pTm, &sTm); + } + /* Format the given string */ + jx9Strftime(pCtx, zFormat, nLen, &sTm); + if( jx9_context_result_buf_length(pCtx) < 1 ){ + /* Nothing was formatted, return FALSE */ + jx9_result_bool(pCtx, 0); + } + return JX9_OK; +} +/* + * string gmdate(string $format [, int $timestamp = time() ] ) + * Identical to the date() function except that the time returned + * is Greenwich Mean Time (GMT). + * Parameters + * $format + * The format of the outputted date string (See code above) + * $timestamp + * The optional timestamp parameter is an integer Unix timestamp + * that defaults to the current local time if a timestamp is not given. + * In other words, it defaults to the value of time(). + * Return + * A formatted date string. If a non-numeric value is used for timestamp, FALSE is returned. + */ +static int jx9Builtin_gmdate(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zFormat; + int nLen; + Sytm sTm; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + zFormat = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Don't bother processing return the empty string */ + jx9_result_string(pCtx, "", 0); + } + if( nArg < 2 ){ +#ifdef __WINNT__ + SYSTEMTIME sOS; + GetSystemTime(&sOS); + SYSTEMTIME_TO_SYTM(&sOS, &sTm); +#else + struct tm *pTm; + time_t t; + time(&t); + pTm = gmtime(&t); + STRUCT_TM_TO_SYTM(pTm, &sTm); +#endif + }else{ + /* Use the given timestamp */ + time_t t; + struct tm *pTm; + if( jx9_value_is_int(apArg[1]) ){ + t = (time_t)jx9_value_to_int64(apArg[1]); + pTm = gmtime(&t); + if( pTm == 0 ){ + time(&t); + } + }else{ + time(&t); + } + pTm = gmtime(&t); + STRUCT_TM_TO_SYTM(pTm, &sTm); + } + /* Format the given string */ + DateFormat(pCtx, zFormat, nLen, &sTm); + return JX9_OK; +} +/* + * array localtime([ int $timestamp = time() [, bool $is_associative = false ]]) + * Return the local time. + * Parameter + * $timestamp: The optional timestamp parameter is an integer Unix timestamp + * that defaults to the current local time if a timestamp is not given. + * In other words, it defaults to the value of time(). + * $is_associative + * If set to FALSE or not supplied then the array is returned as a regular, numerically + * indexed array. If the argument is set to TRUE then localtime() returns an associative + * array containing all the different elements of the structure returned by the C function + * call to localtime. The names of the different keys of the associative array are as follows: + * "tm_sec" - seconds, 0 to 59 + * "tm_min" - minutes, 0 to 59 + * "tm_hour" - hours, 0 to 23 + * "tm_mday" - day of the month, 1 to 31 + * "tm_mon" - month of the year, 0 (Jan) to 11 (Dec) + * "tm_year" - years since 1900 + * "tm_wday" - day of the week, 0 (Sun) to 6 (Sat) + * "tm_yday" - day of the year, 0 to 365 + * "tm_isdst" - is daylight savings time in effect? Positive if yes, 0 if not, negative if unknown. + * Returns + * An associative array of information related to the timestamp. + */ +static int jx9Builtin_localtime(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + jx9_value *pValue, *pArray; + int isAssoc = 0; + Sytm sTm; + if( nArg < 1 ){ +#ifdef __WINNT__ + SYSTEMTIME sOS; + GetSystemTime(&sOS); /* TODO(chems): GMT not local */ + SYSTEMTIME_TO_SYTM(&sOS, &sTm); +#else + struct tm *pTm; + time_t t; + time(&t); + pTm = localtime(&t); + STRUCT_TM_TO_SYTM(pTm, &sTm); +#endif + }else{ + /* Use the given timestamp */ + time_t t; + struct tm *pTm; + if( jx9_value_is_int(apArg[0]) ){ + t = (time_t)jx9_value_to_int64(apArg[0]); + pTm = localtime(&t); + if( pTm == 0 ){ + time(&t); + } + }else{ + time(&t); + } + pTm = localtime(&t); + STRUCT_TM_TO_SYTM(pTm, &sTm); + } + /* Element value */ + pValue = jx9_context_new_scalar(pCtx); + if( pValue == 0 ){ + /* Return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + /* Create a new array */ + pArray = jx9_context_new_array(pCtx); + if( pArray == 0 ){ + /* Return NULL */ + jx9_result_null(pCtx); + return JX9_OK; + } + if( nArg > 1 ){ + isAssoc = jx9_value_to_bool(apArg[1]); + } + /* Fill the array */ + /* Seconds */ + jx9_value_int(pValue, sTm.tm_sec); + if( isAssoc ){ + jx9_array_add_strkey_elem(pArray, "tm_sec", pValue); + }else{ + jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); + } + /* Minutes */ + jx9_value_int(pValue, sTm.tm_min); + if( isAssoc ){ + jx9_array_add_strkey_elem(pArray, "tm_min", pValue); + }else{ + jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); + } + /* Hours */ + jx9_value_int(pValue, sTm.tm_hour); + if( isAssoc ){ + jx9_array_add_strkey_elem(pArray, "tm_hour", pValue); + }else{ + jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); + } + /* mday */ + jx9_value_int(pValue, sTm.tm_mday); + if( isAssoc ){ + jx9_array_add_strkey_elem(pArray, "tm_mday", pValue); + }else{ + jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); + } + /* mon */ + jx9_value_int(pValue, sTm.tm_mon); + if( isAssoc ){ + jx9_array_add_strkey_elem(pArray, "tm_mon", pValue); + }else{ + jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); + } + /* year since 1900 */ + jx9_value_int(pValue, sTm.tm_year-1900); + if( isAssoc ){ + jx9_array_add_strkey_elem(pArray, "tm_year", pValue); + }else{ + jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); + } + /* wday */ + jx9_value_int(pValue, sTm.tm_wday); + if( isAssoc ){ + jx9_array_add_strkey_elem(pArray, "tm_wday", pValue); + }else{ + jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); + } + /* yday */ + jx9_value_int(pValue, sTm.tm_yday); + if( isAssoc ){ + jx9_array_add_strkey_elem(pArray, "tm_yday", pValue); + }else{ + jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); + } + /* isdst */ +#ifdef __WINNT__ +#ifdef _MSC_VER +#ifndef _WIN32_WCE + _get_daylight(&sTm.tm_isdst); +#endif +#endif +#endif + jx9_value_int(pValue, sTm.tm_isdst); + if( isAssoc ){ + jx9_array_add_strkey_elem(pArray, "tm_isdst", pValue); + }else{ + jx9_array_add_elem(pArray, 0/* Automatic index */, pValue); + } + /* Return the array */ + jx9_result_value(pCtx, pArray); + return JX9_OK; +} +/* + * int idate(string $format [, int $timestamp = time() ]) + * Returns a number formatted according to the given format string + * using the given integer timestamp or the current local time if + * no timestamp is given. In other words, timestamp is optional and defaults + * to the value of time(). + * Unlike the function date(), idate() accepts just one char in the format + * parameter. + * $Parameters + * Supported format + * d Day of the month + * h Hour (12 hour format) + * H Hour (24 hour format) + * i Minutes + * I (uppercase i)1 if DST is activated, 0 otherwise + * L (uppercase l) returns 1 for leap year, 0 otherwise + * m Month number + * s Seconds + * t Days in current month + * U Seconds since the Unix Epoch - January 1 1970 00:00:00 UTC - this is the same as time() + * w Day of the week (0 on Sunday) + * W ISO-8601 week number of year, weeks starting on Monday + * y Year (1 or 2 digits - check note below) + * Y Year (4 digits) + * z Day of the year + * Z Timezone offset in seconds + * $timestamp + * The optional timestamp parameter is an integer Unix timestamp that defaults + * to the current local time if a timestamp is not given. In other words, it defaults + * to the value of time(). + * Return + * An integer. + */ +static int jx9Builtin_idate(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zFormat; + jx9_int64 iVal = 0; + int nLen; + Sytm sTm; + if( nArg < 1 || !jx9_value_is_string(apArg[0]) ){ + /* Missing/Invalid argument, return -1 */ + jx9_result_int(pCtx, -1); + return JX9_OK; + } + zFormat = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Don't bother processing return -1*/ + jx9_result_int(pCtx, -1); + } + if( nArg < 2 ){ +#ifdef __WINNT__ + SYSTEMTIME sOS; + GetSystemTime(&sOS); + SYSTEMTIME_TO_SYTM(&sOS, &sTm); +#else + struct tm *pTm; + time_t t; + time(&t); + pTm = localtime(&t); + STRUCT_TM_TO_SYTM(pTm, &sTm); +#endif + }else{ + /* Use the given timestamp */ + time_t t; + struct tm *pTm; + if( jx9_value_is_int(apArg[1]) ){ + t = (time_t)jx9_value_to_int64(apArg[1]); + pTm = localtime(&t); + if( pTm == 0 ){ + time(&t); + } + }else{ + time(&t); + } + pTm = localtime(&t); + STRUCT_TM_TO_SYTM(pTm, &sTm); + } + /* Perform the requested operation */ + switch(zFormat[0]){ + case 'd': + /* Day of the month */ + iVal = sTm.tm_mday; + break; + case 'h': + /* Hour (12 hour format)*/ + iVal = 1 + (sTm.tm_hour % 12); + break; + case 'H': + /* Hour (24 hour format)*/ + iVal = sTm.tm_hour; + break; + case 'i': + /*Minutes*/ + iVal = sTm.tm_min; + break; + case 'I': + /* returns 1 if DST is activated, 0 otherwise */ +#ifdef __WINNT__ +#ifdef _MSC_VER +#ifndef _WIN32_WCE + _get_daylight(&sTm.tm_isdst); +#endif +#endif +#endif + iVal = sTm.tm_isdst; + break; + case 'L': + /* returns 1 for leap year, 0 otherwise */ + iVal = IS_LEAP_YEAR(sTm.tm_year); + break; + case 'm': + /* Month number*/ + iVal = sTm.tm_mon; + break; + case 's': + /*Seconds*/ + iVal = sTm.tm_sec; + break; + case 't':{ + /*Days in current month*/ + static const int aMonDays[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int nDays = aMonDays[sTm.tm_mon % 12 ]; + if( sTm.tm_mon == 1 /* 'February' */ && !IS_LEAP_YEAR(sTm.tm_year) ){ + nDays = 28; + } + iVal = nDays; + break; + } + case 'U': + /*Seconds since the Unix Epoch*/ + iVal = (jx9_int64)time(0); + break; + case 'w': + /* Day of the week (0 on Sunday) */ + iVal = sTm.tm_wday; + break; + case 'W': { + /* ISO-8601 week number of year, weeks starting on Monday */ + static const int aISO8601[] = { 7 /* Sunday */, 1 /* Monday */, 2, 3, 4, 5, 6 }; + iVal = aISO8601[sTm.tm_wday % 7 ]; + break; + } + case 'y': + /* Year (2 digits) */ + iVal = sTm.tm_year % 100; + break; + case 'Y': + /* Year (4 digits) */ + iVal = sTm.tm_year; + break; + case 'z': + /* Day of the year */ + iVal = sTm.tm_yday; + break; + case 'Z': + /*Timezone offset in seconds*/ + iVal = sTm.tm_gmtoff; + break; + default: + /* unknown format, throw a warning */ + jx9_context_throw_error(pCtx, JX9_CTX_WARNING, "Unknown date format token"); + break; + } + /* Return the time value */ + jx9_result_int64(pCtx, iVal); + return JX9_OK; +} +/* + * int mktime/gmmktime([ int $hour = date("H") [, int $minute = date("i") [, int $second = date("s") + * [, int $month = date("n") [, int $day = date("j") [, int $year = date("Y") [, int $is_dst = -1 ]]]]]]] ) + * Returns the Unix timestamp corresponding to the arguments given. This timestamp is a 64bit integer + * containing the number of seconds between the Unix Epoch (January 1 1970 00:00:00 GMT) and the time + * specified. + * Arguments may be left out in order from right to left; any arguments thus omitted will be set to + * the current value according to the local date and time. + * Parameters + * $hour + * The number of the hour relevant to the start of the day determined by month, day and year. + * Negative values reference the hour before midnight of the day in question. Values greater + * than 23 reference the appropriate hour in the following day(s). + * $minute + * The number of the minute relevant to the start of the hour. Negative values reference + * the minute in the previous hour. Values greater than 59 reference the appropriate minute + * in the following hour(s). + * $second + * The number of seconds relevant to the start of the minute. Negative values reference + * the second in the previous minute. Values greater than 59 reference the appropriate + * second in the following minute(s). + * $month + * The number of the month relevant to the end of the previous year. Values 1 to 12 reference + * the normal calendar months of the year in question. Values less than 1 (including negative values) + * reference the months in the previous year in reverse order, so 0 is December, -1 is November)... + * $day + * The number of the day relevant to the end of the previous month. Values 1 to 28, 29, 30 or 31 + * (depending upon the month) reference the normal days in the relevant month. Values less than 1 + * (including negative values) reference the days in the previous month, so 0 is the last day + * of the previous month, -1 is the day before that, etc. Values greater than the number of days + * in the relevant month reference the appropriate day in the following month(s). + * $year + * The number of the year, may be a two or four digit value, with values between 0-69 mapping + * to 2000-2069 and 70-100 to 1970-2000. On systems where time_t is a 32bit signed integer, as + * most common today, the valid range for year is somewhere between 1901 and 2038. + * $is_dst + * This parameter can be set to 1 if the time is during daylight savings time (DST), 0 if it is not, + * or -1 (the default) if it is unknown whether the time is within daylight savings time or not. + * Return + * mktime() returns the Unix timestamp of the arguments given. + * If the arguments are invalid, the function returns FALSE + */ +static int jx9Builtin_mktime(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zFunction; + jx9_int64 iVal = 0; + struct tm *pTm; + time_t t; + /* Extract function name */ + zFunction = jx9_function_name(pCtx); + /* Get the current time */ + time(&t); + if( zFunction[0] == 'g' /* gmmktime */ ){ + pTm = gmtime(&t); + }else{ + /* localtime */ + pTm = localtime(&t); + } + if( nArg > 0 ){ + int iVal; + /* Hour */ + iVal = jx9_value_to_int(apArg[0]); + pTm->tm_hour = iVal; + if( nArg > 1 ){ + /* Minutes */ + iVal = jx9_value_to_int(apArg[1]); + pTm->tm_min = iVal; + if( nArg > 2 ){ + /* Seconds */ + iVal = jx9_value_to_int(apArg[2]); + pTm->tm_sec = iVal; + if( nArg > 3 ){ + /* Month */ + iVal = jx9_value_to_int(apArg[3]); + pTm->tm_mon = iVal - 1; + if( nArg > 4 ){ + /* mday */ + iVal = jx9_value_to_int(apArg[4]); + pTm->tm_mday = iVal; + if( nArg > 5 ){ + /* Year */ + iVal = jx9_value_to_int(apArg[5]); + if( iVal > 1900 ){ + iVal -= 1900; + } + pTm->tm_year = iVal; + if( nArg > 6 ){ + /* is_dst */ + iVal = jx9_value_to_bool(apArg[6]); + pTm->tm_isdst = iVal; + } + } + } + } + } + } + } + /* Make the time */ + iVal = (jx9_int64)mktime(pTm); + /* Return the timesatmp as a 64bit integer */ + jx9_result_int64(pCtx, iVal); + return JX9_OK; +} +/* + * Section: + * URL handling Functions. + * Authors: + * Symisc Systems, devel@symisc.net. + * Copyright (C) Symisc Systems, http://jx9.symisc.net + * Status: + * Stable. + */ +/* + * Output consumer callback for the standard Symisc routines. + * [i.e: SyBase64Encode(), SyBase64Decode(), SyUriEncode(), ...]. + */ +static int Consumer(const void *pData, unsigned int nLen, void *pUserData) +{ + /* Store in the call context result buffer */ + jx9_result_string((jx9_context *)pUserData, (const char *)pData, (int)nLen); + return SXRET_OK; +} +/* + * string base64_encode(string $data) + * string convert_uuencode(string $data) + * Encodes data with MIME base64 + * Parameter + * $data + * Data to encode + * Return + * Encoded data or FALSE on failure. + */ +static int jx9Builtin_base64_encode(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zIn; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the input string */ + zIn = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Nothing to process, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the BASE64 encoding */ + SyBase64Encode(zIn, (sxu32)nLen, Consumer, pCtx); + return JX9_OK; +} +/* + * string base64_decode(string $data) + * string convert_uudecode(string $data) + * Decodes data encoded with MIME base64 + * Parameter + * $data + * Encoded data. + * Return + * Returns the original data or FALSE on failure. + */ +static int jx9Builtin_base64_decode(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zIn; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the input string */ + zIn = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Nothing to process, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the BASE64 decoding */ + SyBase64Decode(zIn, (sxu32)nLen, Consumer, pCtx); + return JX9_OK; +} +/* + * string urlencode(string $str) + * URL encoding + * Parameter + * $data + * Input string. + * Return + * Returns a string in which all non-alphanumeric characters except -_. have + * been replaced with a percent (%) sign followed by two hex digits and spaces + * encoded as plus (+) signs. + */ +static int jx9Builtin_urlencode(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zIn; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the input string */ + zIn = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Nothing to process, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the URL encoding */ + SyUriEncode(zIn, (sxu32)nLen, Consumer, pCtx); + return JX9_OK; +} +/* + * string urldecode(string $str) + * Decodes any %## encoding in the given string. + * Plus symbols ('+') are decoded to a space character. + * Parameter + * $data + * Input string. + * Return + * Decoded URL or FALSE on failure. + */ +static int jx9Builtin_urldecode(jx9_context *pCtx, int nArg, jx9_value **apArg) +{ + const char *zIn; + int nLen; + if( nArg < 1 ){ + /* Missing arguments, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Extract the input string */ + zIn = jx9_value_to_string(apArg[0], &nLen); + if( nLen < 1 ){ + /* Nothing to process, return FALSE */ + jx9_result_bool(pCtx, 0); + return JX9_OK; + } + /* Perform the URL decoding */ + SyUriDecode(zIn, (sxu32)nLen, Consumer, pCtx, TRUE); + return JX9_OK; +} +#endif /* JX9_DISABLE_BUILTIN_FUNC */ +/* Table of the built-in functions */ +static const jx9_builtin_func aBuiltInFunc[] = { + /* Variable handling functions */ + { "is_bool" , jx9Builtin_is_bool }, + { "is_float" , jx9Builtin_is_float }, + { "is_real" , jx9Builtin_is_float }, + { "is_double" , jx9Builtin_is_float }, + { "is_int" , jx9Builtin_is_int }, + { "is_integer" , jx9Builtin_is_int }, + { "is_long" , jx9Builtin_is_int }, + { "is_string" , jx9Builtin_is_string }, + { "is_null" , jx9Builtin_is_null }, + { "is_numeric" , jx9Builtin_is_numeric }, + { "is_scalar" , jx9Builtin_is_scalar }, + { "is_array" , jx9Builtin_is_array }, + { "is_object" , jx9Builtin_is_object }, + { "is_resource", jx9Builtin_is_resource }, + { "douleval" , jx9Builtin_floatval }, + { "floatval" , jx9Builtin_floatval }, + { "intval" , jx9Builtin_intval }, + { "strval" , jx9Builtin_strval }, + { "empty" , jx9Builtin_empty }, +#ifndef JX9_DISABLE_BUILTIN_FUNC +#ifdef JX9_ENABLE_MATH_FUNC + /* Math functions */ + { "abs" , jx9Builtin_abs }, + { "sqrt" , jx9Builtin_sqrt }, + { "exp" , jx9Builtin_exp }, + { "floor", jx9Builtin_floor }, + { "cos" , jx9Builtin_cos }, + { "sin" , jx9Builtin_sin }, + { "acos" , jx9Builtin_acos }, + { "asin" , jx9Builtin_asin }, + { "cosh" , jx9Builtin_cosh }, + { "sinh" , jx9Builtin_sinh }, + { "ceil" , jx9Builtin_ceil }, + { "tan" , jx9Builtin_tan }, + { "tanh" , jx9Builtin_tanh }, + { "atan" , jx9Builtin_atan }, + { "atan2", jx9Builtin_atan2 }, + { "log" , jx9Builtin_log }, + { "log10" , jx9Builtin_log10 }, + { "pow" , jx9Builtin_pow }, + { "pi", jx9Builtin_pi }, + { "fmod", jx9Builtin_fmod }, + { "hypot", jx9Builtin_hypot }, +#endif /* JX9_ENABLE_MATH_FUNC */ + { "round", jx9Builtin_round }, + { "dechex", jx9Builtin_dechex }, + { "decoct", jx9Builtin_decoct }, + { "decbin", jx9Builtin_decbin }, + { "hexdec", jx9Builtin_hexdec }, + { "bindec", jx9Builtin_bindec }, + { "octdec", jx9Builtin_octdec }, + { "base_convert", jx9Builtin_base_convert }, + /* String handling functions */ + { "substr", jx9Builtin_substr }, + { "substr_compare", jx9Builtin_substr_compare }, + { "substr_count", jx9Builtin_substr_count }, + { "chunk_split", jx9Builtin_chunk_split}, + { "htmlspecialchars", jx9Builtin_htmlspecialchars }, + { "htmlspecialchars_decode", jx9Builtin_htmlspecialchars_decode }, + { "get_html_translation_table", jx9Builtin_get_html_translation_table }, + { "htmlentities", jx9Builtin_htmlentities}, + { "html_entity_decode", jx9Builtin_html_entity_decode}, + { "strlen" , jx9Builtin_strlen }, + { "strcmp" , jx9Builtin_strcmp }, + { "strcoll" , jx9Builtin_strcmp }, + { "strncmp" , jx9Builtin_strncmp }, + { "strcasecmp" , jx9Builtin_strcasecmp }, + { "strncasecmp", jx9Builtin_strncasecmp}, + { "implode" , jx9Builtin_implode }, + { "join" , jx9Builtin_implode }, + { "implode_recursive" , jx9Builtin_implode_recursive }, + { "join_recursive" , jx9Builtin_implode_recursive }, + { "explode" , jx9Builtin_explode }, + { "trim" , jx9Builtin_trim }, + { "rtrim" , jx9Builtin_rtrim }, + { "chop" , jx9Builtin_rtrim }, + { "ltrim" , jx9Builtin_ltrim }, + { "strtolower", jx9Builtin_strtolower }, + { "mb_strtolower", jx9Builtin_strtolower }, /* Only UTF-8 encoding is supported */ + { "strtoupper", jx9Builtin_strtoupper }, + { "mb_strtoupper", jx9Builtin_strtoupper }, /* Only UTF-8 encoding is supported */ + { "ord", jx9Builtin_ord }, + { "chr", jx9Builtin_chr }, + { "bin2hex", jx9Builtin_bin2hex }, + { "strstr", jx9Builtin_strstr }, + { "stristr", jx9Builtin_stristr }, + { "strchr", jx9Builtin_strstr }, + { "strpos", jx9Builtin_strpos }, + { "stripos", jx9Builtin_stripos }, + { "strrpos", jx9Builtin_strrpos }, + { "strripos", jx9Builtin_strripos }, + { "strrchr", jx9Builtin_strrchr }, + { "strrev", jx9Builtin_strrev }, + { "str_repeat", jx9Builtin_str_repeat }, + { "nl2br", jx9Builtin_nl2br }, + { "sprintf", jx9Builtin_sprintf }, + { "printf", jx9Builtin_printf }, + { "vprintf", jx9Builtin_vprintf }, + { "vsprintf", jx9Builtin_vsprintf }, + { "size_format", jx9Builtin_size_format}, +#if !defined(JX9_DISABLE_HASH_FUNC) + { "md5", jx9Builtin_md5 }, + { "sha1", jx9Builtin_sha1 }, + { "crc32", jx9Builtin_crc32 }, +#endif /* JX9_DISABLE_HASH_FUNC */ + { "str_getcsv", jx9Builtin_str_getcsv }, + { "strip_tags", jx9Builtin_strip_tags }, + { "str_split", jx9Builtin_str_split }, + { "strspn", jx9Builtin_strspn }, + { "strcspn", jx9Builtin_strcspn }, + { "strpbrk", jx9Builtin_strpbrk }, + { "soundex", jx9Builtin_soundex }, + { "wordwrap", jx9Builtin_wordwrap }, + { "strtok", jx9Builtin_strtok }, + { "str_pad", jx9Builtin_str_pad }, + { "str_replace", jx9Builtin_str_replace}, + { "str_ireplace", jx9Builtin_str_replace}, + { "strtr", jx9Builtin_strtr }, + { "parse_ini_string", jx9Builtin_parse_ini_string}, + /* Ctype functions */ + { "ctype_alnum", jx9Builtin_ctype_alnum }, + { "ctype_alpha", jx9Builtin_ctype_alpha }, + { "ctype_cntrl", jx9Builtin_ctype_cntrl }, + { "ctype_digit", jx9Builtin_ctype_digit }, + { "ctype_xdigit", jx9Builtin_ctype_xdigit}, + { "ctype_graph", jx9Builtin_ctype_graph }, + { "ctype_print", jx9Builtin_ctype_print }, + { "ctype_punct", jx9Builtin_ctype_punct }, + { "ctype_space", jx9Builtin_ctype_space }, + { "ctype_lower", jx9Builtin_ctype_lower }, + { "ctype_upper", jx9Builtin_ctype_upper }, + /* Time functions */ + { "time" , jx9Builtin_time }, + { "microtime", jx9Builtin_microtime }, + { "getdate" , jx9Builtin_getdate }, + { "gettimeofday", jx9Builtin_gettimeofday }, + { "date", jx9Builtin_date }, + { "strftime", jx9Builtin_strftime }, + { "idate", jx9Builtin_idate }, + { "gmdate", jx9Builtin_gmdate }, + { "localtime", jx9Builtin_localtime }, + { "mktime", jx9Builtin_mktime }, + { "gmmktime", jx9Builtin_mktime }, + /* URL functions */ + { "base64_encode", jx9Builtin_base64_encode }, + { "base64_decode", jx9Builtin_base64_decode }, + { "convert_uuencode", jx9Builtin_base64_encode }, + { "convert_uudecode", jx9Builtin_base64_decode }, + { "urlencode", jx9Builtin_urlencode }, + { "urldecode", jx9Builtin_urldecode }, + { "rawurlencode", jx9Builtin_urlencode }, + { "rawurldecode", jx9Builtin_urldecode }, +#endif /* JX9_DISABLE_BUILTIN_FUNC */ +}; +/* + * Register the built-in functions defined above, the array functions + * defined in hashmap.c and the IO functions defined in vfs.c. + */ +JX9_PRIVATE void jx9RegisterBuiltInFunction(jx9_vm *pVm) +{ + sxu32 n; + for( n = 0 ; n < SX_ARRAYSIZE(aBuiltInFunc) ; ++n ){ + jx9_create_function(&(*pVm), aBuiltInFunc[n].zName, aBuiltInFunc[n].xFunc, 0); + } + /* Register hashmap functions [i.e: sort(), count(), array_diff(), ...] */ + jx9RegisterHashmapFunctions(&(*pVm)); + /* Register IO functions [i.e: fread(), fwrite(), chdir(), mkdir(), file(), ...] */ + jx9RegisterIORoutine(&(*pVm)); +} -- cgit v1.2.3