1673 lines
36 KiB
C
1673 lines
36 KiB
C
/*
|
|
* gawkapi.c -- Implement the functions defined for gawkapi.h
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2012-2019, 2021, 2022, the Free Software Foundation, Inc.
|
|
*
|
|
* This file is part of GAWK, the GNU implementation of the
|
|
* AWK Programming Language.
|
|
*
|
|
* GAWK is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GAWK is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "awk.h"
|
|
|
|
/* Declare some globals used by api_get_file: */
|
|
extern IOBUF *curfile;
|
|
extern INSTRUCTION *main_beginfile;
|
|
extern int currule;
|
|
|
|
static awk_bool_t node_to_awk_value(NODE *node, awk_value_t *result, awk_valtype_t wanted);
|
|
static const char *valtype2str(awk_valtype_t type);
|
|
static NODE *ns_lookup(const char *name_space, const char *name, char **full_name);
|
|
|
|
/*
|
|
* api_get_argument --- get the count'th paramater, zero-based.
|
|
*
|
|
* Returns false if count is out of range, or if actual paramater
|
|
* does not match what is specified in wanted. In the latter
|
|
* case, fills in result->val_type with the actual type.
|
|
*/
|
|
|
|
static awk_bool_t
|
|
api_get_argument(awk_ext_id_t id, size_t count,
|
|
awk_valtype_t wanted, awk_value_t *result)
|
|
{
|
|
#ifdef DYNAMIC
|
|
NODE *arg;
|
|
|
|
if (result == NULL)
|
|
return awk_false;
|
|
|
|
(void) id;
|
|
|
|
/* set up default result */
|
|
memset(result, 0, sizeof(*result));
|
|
result->val_type = AWK_UNDEFINED;
|
|
|
|
/*
|
|
* Song and dance here. get_array_argument() and get_scalar_argument()
|
|
* will force a change in type of a parameter that is Node_var_new.
|
|
*
|
|
* Start by looking at the unadulterated argument as it was passed.
|
|
*/
|
|
arg = get_argument(count);
|
|
if (arg == NULL)
|
|
return awk_false;
|
|
|
|
/* if type is undefined */
|
|
if (arg->type == Node_var_new || arg->type == Node_elem_new) {
|
|
if (wanted == AWK_UNDEFINED)
|
|
return awk_true;
|
|
else if (wanted == AWK_ARRAY) {
|
|
goto array;
|
|
} else {
|
|
goto scalar;
|
|
}
|
|
}
|
|
|
|
/* at this point, we have real type */
|
|
if (arg->type == Node_var_array || arg->type == Node_array_ref) {
|
|
if (wanted != AWK_ARRAY && wanted != AWK_UNDEFINED)
|
|
return awk_false;
|
|
goto array;
|
|
} else
|
|
goto scalar;
|
|
|
|
array:
|
|
/* get the array here */
|
|
arg = get_array_argument(arg, count);
|
|
if (arg == NULL)
|
|
return awk_false;
|
|
|
|
return node_to_awk_value(arg, result, wanted);
|
|
|
|
scalar:
|
|
/* at this point we have a real type that is not an array */
|
|
arg = get_scalar_argument(arg, count);
|
|
if (arg == NULL)
|
|
return awk_false;
|
|
|
|
return node_to_awk_value(arg, result, wanted);
|
|
#else
|
|
return awk_false;
|
|
#endif
|
|
}
|
|
|
|
/* api_set_argument --- convert an argument to an array */
|
|
|
|
static awk_bool_t
|
|
api_set_argument(awk_ext_id_t id,
|
|
size_t count,
|
|
awk_array_t new_array)
|
|
{
|
|
#ifdef DYNAMIC
|
|
NODE *arg;
|
|
NODE *array = (NODE *) new_array;
|
|
|
|
(void) id;
|
|
|
|
if (array == NULL || array->type != Node_var_array)
|
|
return awk_false;
|
|
|
|
if ( (arg = get_argument(count)) == NULL
|
|
|| (arg->type != Node_var_new && arg->type != Node_elem_new))
|
|
return awk_false;
|
|
|
|
arg = get_array_argument(arg, count);
|
|
if (arg == NULL)
|
|
return awk_false;
|
|
|
|
array->vname = arg->vname;
|
|
*arg = *array;
|
|
freenode(array);
|
|
|
|
return awk_true;
|
|
#else
|
|
return awk_false;
|
|
#endif
|
|
}
|
|
|
|
/* awk_value_to_node --- convert a value into a NODE */
|
|
|
|
NODE *
|
|
awk_value_to_node(const awk_value_t *retval)
|
|
{
|
|
NODE *ext_ret_val = NULL;
|
|
NODE *v;
|
|
#ifdef HAVE_MPFR
|
|
int tval = 0;
|
|
#endif
|
|
|
|
if (retval == NULL)
|
|
fatal(_("awk_value_to_node: received null retval"));
|
|
|
|
switch (retval->val_type) {
|
|
case AWK_ARRAY:
|
|
ext_ret_val = (NODE *) retval->array_cookie;
|
|
break;
|
|
case AWK_UNDEFINED:
|
|
ext_ret_val = dupnode(Nnull_string);
|
|
break;
|
|
case AWK_BOOL:
|
|
ext_ret_val = make_bool_node(retval->bool_value != awk_false);
|
|
break;
|
|
case AWK_NUMBER:
|
|
switch (retval->num_type) {
|
|
case AWK_NUMBER_TYPE_DOUBLE:
|
|
ext_ret_val = make_number(retval->num_value);
|
|
break;
|
|
case AWK_NUMBER_TYPE_MPFR:
|
|
#ifdef HAVE_MPFR
|
|
if (! do_mpfr)
|
|
fatal(_("awk_value_to_node: not in MPFR mode"));
|
|
ext_ret_val = make_number_node(MPFN);
|
|
mpfr_init(ext_ret_val->mpg_numbr);
|
|
tval = mpfr_set(ext_ret_val->mpg_numbr, (mpfr_srcptr) retval->num_ptr, ROUND_MODE);
|
|
IEEE_FMT(ext_ret_val->mpg_numbr, tval);
|
|
mpfr_clear(retval->num_ptr);
|
|
#else
|
|
fatal(_("awk_value_to_node: MPFR not supported"));
|
|
#endif
|
|
break;
|
|
case AWK_NUMBER_TYPE_MPZ:
|
|
#ifdef HAVE_MPFR
|
|
if (! do_mpfr)
|
|
fatal(_("awk_value_to_node: not in MPFR mode"));
|
|
ext_ret_val = make_number_node(MPZN);
|
|
mpz_init(ext_ret_val->mpg_i);
|
|
mpz_set(ext_ret_val->mpg_i, (mpz_ptr) retval->num_ptr);
|
|
mpz_clear(retval->num_ptr);
|
|
#else
|
|
fatal(_("awk_value_to_node: MPFR not supported"));
|
|
#endif
|
|
break;
|
|
default:
|
|
fatal(_("awk_value_to_node: invalid number type `%d'"), retval->num_type);
|
|
break;
|
|
}
|
|
break;
|
|
case AWK_STRING:
|
|
ext_ret_val = make_str_node(retval->str_value.str,
|
|
retval->str_value.len, ALREADY_MALLOCED);
|
|
break;
|
|
case AWK_STRNUM:
|
|
ext_ret_val = make_str_node(retval->str_value.str,
|
|
retval->str_value.len, ALREADY_MALLOCED);
|
|
ext_ret_val->flags |= USER_INPUT;
|
|
break;
|
|
case AWK_REGEX:
|
|
ext_ret_val = make_typed_regex(retval->str_value.str,
|
|
retval->str_value.len);
|
|
break;
|
|
case AWK_SCALAR:
|
|
v = (NODE *) retval->scalar_cookie;
|
|
if (v->type != Node_var)
|
|
ext_ret_val = NULL;
|
|
else
|
|
ext_ret_val = dupnode(v->var_value);
|
|
break;
|
|
case AWK_VALUE_COOKIE:
|
|
ext_ret_val = dupnode((NODE *)(retval->value_cookie));
|
|
break;
|
|
default: /* any invalid type */
|
|
ext_ret_val = NULL;
|
|
break;
|
|
}
|
|
|
|
return ext_ret_val;
|
|
}
|
|
|
|
/* Functions to print messages */
|
|
|
|
/* api_fatal --- print a fatal message and exit */
|
|
|
|
static void
|
|
api_fatal(awk_ext_id_t id, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
(void) id;
|
|
|
|
va_start(args, format);
|
|
err(true, _("fatal: "), format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
/* api_nonfatal --- print a non fatal error message */
|
|
|
|
static void
|
|
api_nonfatal(awk_ext_id_t id, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
(void) id;
|
|
|
|
va_start(args, format);
|
|
err(false, _("error: "), format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
/* api_warning --- print a warning message */
|
|
|
|
static void
|
|
api_warning(awk_ext_id_t id, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
(void) id;
|
|
|
|
va_start(args, format);
|
|
err(false, _("warning: "), format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
/* api_lintwarn --- print a lint warning message and exit if appropriate */
|
|
|
|
static void
|
|
api_lintwarn(awk_ext_id_t id, const char *format, ...)
|
|
{
|
|
va_list args;
|
|
|
|
(void) id;
|
|
|
|
va_start(args, format);
|
|
if (lintfunc == r_fatal) {
|
|
err(true, _("fatal: "), format, args);
|
|
} else {
|
|
err(false, _("warning: "), format, args);
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
/* api_register_input_parser --- register an input_parser; for opening files read-only */
|
|
|
|
static void
|
|
api_register_input_parser(awk_ext_id_t id, awk_input_parser_t *input_parser)
|
|
{
|
|
(void) id;
|
|
|
|
if (input_parser == NULL)
|
|
return;
|
|
|
|
register_input_parser(input_parser);
|
|
}
|
|
|
|
/* api_register_output_wrapper --- register an output wrapper, for writing files / two-way pipes */
|
|
|
|
static void api_register_output_wrapper(awk_ext_id_t id,
|
|
awk_output_wrapper_t *output_wrapper)
|
|
{
|
|
(void) id;
|
|
|
|
if (output_wrapper == NULL)
|
|
return;
|
|
|
|
register_output_wrapper(output_wrapper);
|
|
}
|
|
|
|
/* api_register_two_way_processor --- register a processor for two way I/O */
|
|
|
|
static void
|
|
api_register_two_way_processor(awk_ext_id_t id,
|
|
awk_two_way_processor_t *two_way_processor)
|
|
{
|
|
(void) id;
|
|
|
|
if (two_way_processor == NULL)
|
|
return;
|
|
|
|
register_two_way_processor(two_way_processor);
|
|
}
|
|
|
|
/* Functions to update ERRNO */
|
|
|
|
/* api_update_ERRNO_int --- update ERRNO with an integer value */
|
|
|
|
static void
|
|
api_update_ERRNO_int(awk_ext_id_t id, int errno_val)
|
|
{
|
|
(void) id;
|
|
|
|
update_ERRNO_int(errno_val);
|
|
}
|
|
|
|
/* api_update_ERRNO_string --- update ERRNO with a string value */
|
|
|
|
static void
|
|
api_update_ERRNO_string(awk_ext_id_t id,
|
|
const char *string)
|
|
{
|
|
(void) id;
|
|
|
|
if (string == NULL)
|
|
return;
|
|
|
|
update_ERRNO_string(string);
|
|
}
|
|
|
|
/* api_unset_ERRNO --- unset ERRNO */
|
|
|
|
static void
|
|
api_unset_ERRNO(awk_ext_id_t id)
|
|
{
|
|
(void) id;
|
|
|
|
unset_ERRNO();
|
|
}
|
|
|
|
|
|
/* api_add_ext_func --- add a function to the interpreter, returns true upon success */
|
|
|
|
static awk_bool_t
|
|
api_add_ext_func(awk_ext_id_t id,
|
|
const char *name_space,
|
|
awk_ext_func_t *func)
|
|
{
|
|
(void) id;
|
|
|
|
if (func == NULL)
|
|
return awk_false;
|
|
|
|
if (name_space == NULL)
|
|
fatal(_("add_ext_func: received NULL name_space parameter"));
|
|
|
|
#ifdef DYNAMIC
|
|
return make_builtin(name_space, func);
|
|
#else
|
|
return awk_false;
|
|
#endif
|
|
}
|
|
|
|
/* Stuff for exit handler - do it as linked list */
|
|
|
|
struct ext_exit_handler {
|
|
struct ext_exit_handler *next;
|
|
void (*funcp)(void *data, int exit_status);
|
|
void *arg0;
|
|
};
|
|
static struct ext_exit_handler *list_head = NULL;
|
|
|
|
/* run_ext_exit_handlers --- run the extension exit handlers, LIFO order */
|
|
|
|
void
|
|
run_ext_exit_handlers(int exitval)
|
|
{
|
|
struct ext_exit_handler *p, *next;
|
|
|
|
for (p = list_head; p != NULL; p = next) {
|
|
next = p->next;
|
|
p->funcp(p->arg0, exitval);
|
|
free(p);
|
|
}
|
|
list_head = NULL;
|
|
}
|
|
|
|
/* api_awk_atexit --- add an exit call back */
|
|
|
|
static void
|
|
api_awk_atexit(awk_ext_id_t id,
|
|
void (*funcp)(void *data, int exit_status),
|
|
void *arg0)
|
|
{
|
|
struct ext_exit_handler *p;
|
|
|
|
(void) id;
|
|
|
|
if (funcp == NULL)
|
|
return;
|
|
|
|
/* allocate memory */
|
|
emalloc(p, struct ext_exit_handler *, sizeof(struct ext_exit_handler), "api_awk_atexit");
|
|
|
|
/* fill it in */
|
|
p->funcp = funcp;
|
|
p->arg0 = arg0;
|
|
|
|
/* add to linked list, LIFO order */
|
|
p->next = list_head;
|
|
list_head = p;
|
|
}
|
|
|
|
static struct {
|
|
char **strings;
|
|
size_t i, size;
|
|
} scopy;
|
|
|
|
/* free_api_string_copies --- release memory used by string copies */
|
|
|
|
void
|
|
free_api_string_copies()
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < scopy.i; i++)
|
|
free(scopy.strings[i]);
|
|
scopy.i = 0;
|
|
}
|
|
|
|
/* assign_string --- return a string node with NUL termination */
|
|
|
|
static inline void
|
|
assign_string(NODE *node, awk_value_t *val, awk_valtype_t val_type)
|
|
{
|
|
val->val_type = val_type;
|
|
if (node->stptr[node->stlen] != '\0') {
|
|
/*
|
|
* This is an unterminated field string, so make a copy.
|
|
* This should happen only for $n where n > 0 and n < NF.
|
|
*/
|
|
char *s;
|
|
|
|
assert((node->flags & MALLOC) == 0);
|
|
if (scopy.i == scopy.size) {
|
|
/* expand list */
|
|
if (scopy.size == 0)
|
|
scopy.size = 8; /* initial size */
|
|
else
|
|
scopy.size *= 2;
|
|
erealloc(scopy.strings, char **, scopy.size * sizeof(char *), "assign_string");
|
|
}
|
|
emalloc(s, char *, node->stlen + 1, "assign_string");
|
|
memcpy(s, node->stptr, node->stlen);
|
|
s[node->stlen] = '\0';
|
|
val->str_value.str = scopy.strings[scopy.i++] = s;
|
|
}
|
|
else
|
|
val->str_value.str = node->stptr;
|
|
val->str_value.len = node->stlen;
|
|
}
|
|
|
|
/* assign_number -- return a number node */
|
|
|
|
#define assign_double(val) \
|
|
val->num_value = node->numbr; \
|
|
val->num_type = AWK_NUMBER_TYPE_DOUBLE; \
|
|
val->num_ptr = NULL
|
|
|
|
static inline void
|
|
assign_number(NODE *node, awk_value_t *val)
|
|
{
|
|
val->val_type = AWK_NUMBER;
|
|
|
|
#ifndef HAVE_MPFR
|
|
assign_double(val);
|
|
#else
|
|
switch (node->flags & (MPFN|MPZN)) {
|
|
case 0:
|
|
assign_double(val);
|
|
break;
|
|
case MPFN:
|
|
val->num_value = mpfr_get_d(node->mpg_numbr, ROUND_MODE);
|
|
val->num_type = AWK_NUMBER_TYPE_MPFR;
|
|
val->num_ptr = &node->mpg_numbr;
|
|
break;
|
|
case MPZN:
|
|
val->num_value = mpz_get_d(node->mpg_i);
|
|
val->num_type = AWK_NUMBER_TYPE_MPZ;
|
|
val->num_ptr = &node->mpg_i;
|
|
break;
|
|
default:
|
|
fatal(_("node_to_awk_value: detected invalid numeric flags combination `%s'; please file a bug report"), flags2str(node->flags));
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
#undef assign_double
|
|
|
|
/* assign_regex --- return a regex node */
|
|
|
|
static inline void
|
|
assign_regex(NODE *node, awk_value_t *val)
|
|
{
|
|
/* a REGEX node cannot be an unterminated field string */
|
|
assert((node->flags & MALLOC) != 0);
|
|
assert(node->stptr[node->stlen] == '\0');
|
|
val->str_value.str = node->stptr;
|
|
val->str_value.len = node->stlen;
|
|
val->val_type = AWK_REGEX;
|
|
}
|
|
|
|
/* assign_bool --- return a bool node */
|
|
|
|
static inline void
|
|
assign_bool(NODE *node, awk_value_t *val)
|
|
{
|
|
assert((node->flags & BOOLVAL) != 0);
|
|
val->val_type = AWK_BOOL;
|
|
val->bool_value = get_number_si(node) != 0 ? awk_true : awk_false;
|
|
}
|
|
|
|
/* node_to_awk_value --- convert a node into a value for an extension */
|
|
|
|
static awk_bool_t
|
|
node_to_awk_value(NODE *node, awk_value_t *val, awk_valtype_t wanted)
|
|
{
|
|
awk_bool_t ret = awk_false;
|
|
|
|
if (node == NULL)
|
|
fatal(_("node_to_awk_value: received null node"));
|
|
|
|
if (val == NULL)
|
|
fatal(_("node_to_awk_value: received null val"));
|
|
|
|
switch (node->type) {
|
|
case Node_var_new: /* undefined variable */
|
|
case Node_elem_new: /* undefined element */
|
|
val->val_type = AWK_UNDEFINED;
|
|
if (wanted == AWK_UNDEFINED) {
|
|
ret = awk_true;
|
|
}
|
|
break;
|
|
|
|
case Node_var:
|
|
/* a scalar value */
|
|
if (wanted == AWK_SCALAR) {
|
|
val->val_type = AWK_SCALAR;
|
|
val->scalar_cookie = (void *) node;
|
|
ret = awk_true;
|
|
break;
|
|
}
|
|
|
|
node = node->var_value;
|
|
/* FALL THROUGH */
|
|
case Node_val:
|
|
/* a scalar value */
|
|
switch (wanted) {
|
|
case AWK_BOOL:
|
|
if ((node->flags & BOOLVAL) != 0) {
|
|
assign_bool(node, val);
|
|
ret = awk_true;
|
|
} else
|
|
ret = awk_false;
|
|
break;
|
|
|
|
case AWK_NUMBER:
|
|
if ((node->flags & REGEX) != 0)
|
|
val->val_type = AWK_REGEX;
|
|
else {
|
|
(void) force_number(node);
|
|
assign_number(node, val);
|
|
ret = awk_true;
|
|
}
|
|
break;
|
|
|
|
case AWK_STRNUM:
|
|
switch (fixtype(node)->flags & (STRING|NUMBER|USER_INPUT|REGEX|BOOLVAL)) {
|
|
case NUMBER|BOOLVAL:
|
|
val->val_type = AWK_BOOL;
|
|
break;
|
|
case STRING:
|
|
val->val_type = AWK_STRING;
|
|
break;
|
|
case NUMBER:
|
|
(void) force_string(node);
|
|
/* fall through */
|
|
case NUMBER|USER_INPUT:
|
|
assign_string(node, val, AWK_STRNUM);
|
|
ret = awk_true;
|
|
break;
|
|
case REGEX:
|
|
val->val_type = AWK_REGEX;
|
|
break;
|
|
case NUMBER|STRING:
|
|
if (node == Nnull_string) {
|
|
val->val_type = AWK_UNDEFINED;
|
|
break;
|
|
}
|
|
/* fall through */
|
|
default:
|
|
warning(_("node_to_awk_value detected invalid flags combination `%s'; please file a bug report"), flags2str(node->flags));
|
|
val->val_type = AWK_UNDEFINED;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case AWK_STRING:
|
|
(void) force_string(node);
|
|
assign_string(node, val, AWK_STRING);
|
|
ret = awk_true;
|
|
break;
|
|
|
|
case AWK_REGEX:
|
|
switch (fixtype(node)->flags & (STRING|NUMBER|USER_INPUT|REGEX|BOOLVAL)) {
|
|
case STRING:
|
|
val->val_type = AWK_STRING;
|
|
break;
|
|
case NUMBER|BOOLVAL:
|
|
val->val_type = AWK_BOOL;
|
|
break;
|
|
case NUMBER:
|
|
val->val_type = AWK_NUMBER;
|
|
break;
|
|
case NUMBER|USER_INPUT:
|
|
val->val_type = AWK_STRNUM;
|
|
break;
|
|
case REGEX:
|
|
assign_regex(node, val);
|
|
ret = awk_true;
|
|
break;
|
|
case NUMBER|STRING:
|
|
if (node == Nnull_string) {
|
|
val->val_type = AWK_UNDEFINED;
|
|
break;
|
|
}
|
|
/* fall through */
|
|
default:
|
|
warning(_("node_to_awk_value detected invalid flags combination `%s'; please file a bug report"), flags2str(node->flags));
|
|
val->val_type = AWK_UNDEFINED;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case AWK_SCALAR:
|
|
switch (fixtype(node)->flags & (STRING|NUMBER|USER_INPUT|REGEX|BOOLVAL)) {
|
|
case NUMBER|BOOLVAL:
|
|
val->val_type = AWK_BOOL;
|
|
break;
|
|
case STRING:
|
|
val->val_type = AWK_STRING;
|
|
break;
|
|
case NUMBER:
|
|
val->val_type = AWK_NUMBER;
|
|
break;
|
|
case NUMBER|USER_INPUT:
|
|
val->val_type = AWK_STRNUM;
|
|
break;
|
|
case REGEX:
|
|
val->val_type = AWK_REGEX;
|
|
break;
|
|
case NUMBER|STRING:
|
|
if (node == Nnull_string) {
|
|
val->val_type = AWK_UNDEFINED;
|
|
break;
|
|
}
|
|
/* fall through */
|
|
default:
|
|
warning(_("node_to_awk_value detected invalid flags combination `%s'; please file a bug report"), flags2str(node->flags));
|
|
val->val_type = AWK_UNDEFINED;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case AWK_UNDEFINED:
|
|
/* return true and actual type for request of undefined */
|
|
switch (fixtype(node)->flags & (STRING|NUMBER|USER_INPUT|REGEX|BOOLVAL)) {
|
|
case NUMBER|BOOLVAL:
|
|
assign_bool(node, val);
|
|
ret = awk_true;
|
|
break;
|
|
case STRING:
|
|
assign_string(node, val, AWK_STRING);
|
|
ret = awk_true;
|
|
break;
|
|
case NUMBER:
|
|
assign_number(node, val);
|
|
ret = awk_true;
|
|
break;
|
|
case NUMBER|USER_INPUT:
|
|
assign_string(node, val, AWK_STRNUM);
|
|
ret = awk_true;
|
|
break;
|
|
case REGEX:
|
|
assign_regex(node, val);
|
|
ret = awk_true;
|
|
break;
|
|
case NUMBER|STRING:
|
|
if (node == Nnull_string) {
|
|
val->val_type = AWK_UNDEFINED;
|
|
ret = awk_true;
|
|
break;
|
|
}
|
|
/* fall through */
|
|
default:
|
|
warning(_("node_to_awk_value detected invalid flags combination `%s'; please file a bug report"), flags2str(node->flags));
|
|
val->val_type = AWK_UNDEFINED;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case AWK_ARRAY:
|
|
case AWK_VALUE_COOKIE:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case Node_var_array:
|
|
val->val_type = AWK_ARRAY;
|
|
if (wanted == AWK_ARRAY || wanted == AWK_UNDEFINED) {
|
|
val->array_cookie = node;
|
|
ret = awk_true;
|
|
} else
|
|
ret = awk_false;
|
|
break;
|
|
|
|
default:
|
|
val->val_type = AWK_UNDEFINED;
|
|
ret = awk_false;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Symbol table access:
|
|
* - No access to special variables (NF, etc.)
|
|
* - One special exception: PROCINFO.
|
|
* - Use sym_update() to change a value, including from UNDEFINED
|
|
* to scalar or array.
|
|
*/
|
|
/*
|
|
* Lookup a variable, fills in value. No messing with the value
|
|
* returned. Returns false if the variable doesn't exist
|
|
* or the wrong type was requested.
|
|
* In the latter case, fills in vaule->val_type with the real type.
|
|
* Built-in variables (except PROCINFO) may not be accessed by an extension.
|
|
*/
|
|
|
|
/* api_sym_lookup --- look up a symbol */
|
|
|
|
static awk_bool_t
|
|
api_sym_lookup(awk_ext_id_t id,
|
|
const char *name_space,
|
|
const char *name,
|
|
awk_valtype_t wanted,
|
|
awk_value_t *result)
|
|
{
|
|
NODE *node;
|
|
|
|
update_global_values(); /* make sure stuff like NF, NR, are up to date */
|
|
|
|
if ( name == NULL
|
|
|| *name == '\0'
|
|
|| result == NULL
|
|
|| ! is_valid_identifier(name)
|
|
|| name_space == NULL
|
|
|| (name_space[0] != '\0' && ! is_valid_identifier(name_space)))
|
|
return awk_false;
|
|
|
|
if ((node = ns_lookup(name_space, name, NULL)) == NULL)
|
|
return awk_false;
|
|
|
|
if (is_off_limits_var(name)) /* a built-in variable */
|
|
node->flags |= NO_EXT_SET;
|
|
|
|
return node_to_awk_value(node, result, wanted);
|
|
}
|
|
|
|
/* api_sym_lookup_scalar --- retrieve the current value of a scalar */
|
|
|
|
static awk_bool_t
|
|
api_sym_lookup_scalar(awk_ext_id_t id,
|
|
awk_scalar_t cookie,
|
|
awk_valtype_t wanted,
|
|
awk_value_t *result)
|
|
{
|
|
NODE *node = (NODE *) cookie;
|
|
|
|
if (node == NULL
|
|
|| result == NULL
|
|
|| node->type != Node_var)
|
|
return awk_false;
|
|
|
|
update_global_values(); /* make sure stuff like NF, NR, are up to date */
|
|
|
|
return node_to_awk_value(node, result, wanted);
|
|
}
|
|
|
|
/* api_sym_update --- update a symbol's value, see gawkapi.h for semantics */
|
|
|
|
static awk_bool_t
|
|
api_sym_update(awk_ext_id_t id,
|
|
const char *name_space,
|
|
const char *name,
|
|
awk_value_t *value)
|
|
{
|
|
NODE *node;
|
|
NODE *array_node;
|
|
|
|
if ( name == NULL
|
|
|| *name == '\0'
|
|
|| value == NULL
|
|
|| ! is_valid_identifier(name)
|
|
|| name_space == NULL
|
|
|| (name_space[0] != '\0' && ! is_valid_identifier(name_space)))
|
|
return awk_false;
|
|
|
|
switch (value->val_type) {
|
|
case AWK_NUMBER:
|
|
case AWK_STRNUM:
|
|
case AWK_STRING:
|
|
case AWK_REGEX:
|
|
case AWK_UNDEFINED:
|
|
case AWK_ARRAY:
|
|
case AWK_SCALAR:
|
|
case AWK_VALUE_COOKIE:
|
|
break;
|
|
|
|
default:
|
|
/* fatal(_("api_sym_update: invalid value for type of new value (%d)"), value->val_type); */
|
|
return awk_false;
|
|
}
|
|
|
|
char *full_name = NULL;
|
|
node = ns_lookup(name_space, name, & full_name);
|
|
|
|
if (node == NULL) {
|
|
/* new value to be installed */
|
|
if (value->val_type == AWK_ARRAY) {
|
|
array_node = awk_value_to_node(value);
|
|
node = install_symbol(full_name, Node_var_array);
|
|
array_node->vname = node->vname;
|
|
*node = *array_node;
|
|
freenode(array_node);
|
|
value->array_cookie = node; /* pass new cookie back to extension */
|
|
} else {
|
|
/* regular variable */
|
|
node = install_symbol(full_name, Node_var);
|
|
node->var_value = awk_value_to_node(value);
|
|
}
|
|
|
|
return awk_true;
|
|
}
|
|
|
|
/*
|
|
* If we get here, then it exists already. Any valid type is
|
|
* OK except for AWK_ARRAY (unless it is in Node_var_new undefined
|
|
* state, in which case an array is OK).
|
|
*/
|
|
if ( (node->flags & NO_EXT_SET) != 0
|
|
|| is_off_limits_var(full_name)) { /* most built-in vars not allowed */
|
|
node->flags |= NO_EXT_SET;
|
|
efree((void *) full_name);
|
|
return awk_false;
|
|
}
|
|
|
|
efree((void *) full_name);
|
|
|
|
if ((node->type == Node_var && value->val_type != AWK_ARRAY)
|
|
|| node->type == Node_var_new
|
|
|| node->type == Node_elem_new) {
|
|
unref(node->var_value);
|
|
node->var_value = awk_value_to_node(value);
|
|
if ((node->type == Node_var_new || node->type == Node_elem_new)
|
|
&& value->val_type != AWK_UNDEFINED)
|
|
node->type = Node_var;
|
|
|
|
return awk_true;
|
|
}
|
|
|
|
return awk_false;
|
|
}
|
|
|
|
/* api_sym_update_scalar --- update a scalar cookie */
|
|
|
|
static awk_bool_t
|
|
api_sym_update_scalar(awk_ext_id_t id,
|
|
awk_scalar_t cookie,
|
|
awk_value_t *value)
|
|
{
|
|
NODE *node = (NODE *) cookie;
|
|
|
|
if (value == NULL
|
|
|| node == NULL
|
|
|| node->type != Node_var
|
|
|| (node->flags & NO_EXT_SET) != 0)
|
|
return awk_false;
|
|
|
|
/*
|
|
* Optimization: if valref is 1, and the new value is a string or
|
|
* a number, we can avoid calling unref and then making a new node
|
|
* by simply installing the new value. First, we follow the same
|
|
* recipe used by node.c:r_unref to wipe the current values, and then
|
|
* we copy the logic from r_make_number or make_str_node to install
|
|
* the new value.
|
|
*/
|
|
switch (value->val_type) {
|
|
case AWK_NUMBER:
|
|
if (node->var_value->valref == 1 && ! do_mpfr) {
|
|
NODE *r = node->var_value;
|
|
|
|
/* r_unref: */
|
|
if ((r->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR))
|
|
efree(r->stptr);
|
|
free_wstr(r);
|
|
|
|
/* r_make_number: */
|
|
r->numbr = value->num_value;
|
|
r->flags = MALLOC|NUMBER|NUMCUR;
|
|
r->stptr = NULL;
|
|
r->stlen = 0;
|
|
return awk_true;
|
|
}
|
|
break;
|
|
|
|
case AWK_STRING:
|
|
case AWK_STRNUM:
|
|
if (node->var_value->valref == 1) {
|
|
NODE *r = node->var_value;
|
|
|
|
/* r_unref: */
|
|
if ((r->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR))
|
|
efree(r->stptr);
|
|
|
|
mpfr_unset(r);
|
|
free_wstr(r);
|
|
|
|
/* make_str_node(s, l, ALREADY_MALLOCED): */
|
|
r->numbr = 0;
|
|
r->flags = (MALLOC|STRING|STRCUR);
|
|
if (value->val_type == AWK_STRNUM)
|
|
r->flags |= USER_INPUT;
|
|
r->stfmt = STFMT_UNUSED;
|
|
r->stptr = value->str_value.str;
|
|
r->stlen = value->str_value.len;
|
|
#ifdef HAVE_MPFR
|
|
r->strndmode = MPFR_round_mode;
|
|
#endif
|
|
return awk_true;
|
|
}
|
|
break;
|
|
|
|
case AWK_REGEX:
|
|
case AWK_UNDEFINED:
|
|
case AWK_SCALAR:
|
|
case AWK_VALUE_COOKIE:
|
|
break;
|
|
|
|
|
|
default: /* AWK_ARRAY or invalid type */
|
|
return awk_false;
|
|
}
|
|
|
|
/* do it the hard (slow) way */
|
|
unref(node->var_value);
|
|
node->var_value = awk_value_to_node(value);
|
|
return awk_true;
|
|
}
|
|
|
|
/*
|
|
* valid_subscript_type --- test if a type is allowed for an array subscript.
|
|
*
|
|
* Any scalar value is fine, so only AWK_ARRAY (or an invalid type) is illegal.
|
|
*/
|
|
|
|
static inline bool
|
|
valid_subscript_type(awk_valtype_t valtype)
|
|
{
|
|
switch (valtype) {
|
|
case AWK_UNDEFINED:
|
|
case AWK_NUMBER:
|
|
case AWK_STRNUM:
|
|
case AWK_STRING:
|
|
case AWK_REGEX:
|
|
case AWK_SCALAR:
|
|
case AWK_VALUE_COOKIE:
|
|
return true;
|
|
default: /* AWK_ARRAY or an invalid type */
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Array management */
|
|
/*
|
|
* api_get_array_element --- teturn the value of an element - read only!
|
|
*
|
|
* Use set_array_element to change it.
|
|
*/
|
|
|
|
static awk_bool_t
|
|
api_get_array_element(awk_ext_id_t id,
|
|
awk_array_t a_cookie,
|
|
const awk_value_t *const index,
|
|
awk_valtype_t wanted,
|
|
awk_value_t *result)
|
|
{
|
|
NODE *array = (NODE *) a_cookie;
|
|
NODE *subscript;
|
|
NODE **aptr;
|
|
|
|
/* don't check for index len zero, null str is ok as index */
|
|
if ( array == NULL
|
|
|| array->type != Node_var_array
|
|
|| result == NULL
|
|
|| index == NULL
|
|
|| ! valid_subscript_type(index->val_type))
|
|
return awk_false;
|
|
|
|
subscript = awk_value_to_node(index);
|
|
|
|
/* if it doesn't exist, return false */
|
|
if (in_array(array, subscript) == NULL) {
|
|
unref(subscript);
|
|
return awk_false;
|
|
}
|
|
|
|
aptr = assoc_lookup(array, subscript);
|
|
|
|
if (aptr == NULL) { /* can't happen */
|
|
unref(subscript);
|
|
return awk_false;
|
|
}
|
|
|
|
unref(subscript);
|
|
|
|
return node_to_awk_value(*aptr, result, wanted);
|
|
}
|
|
|
|
/*
|
|
* api_set_array_element --- change (or create) element in existing array
|
|
* with element->index and element->value.
|
|
*/
|
|
|
|
static awk_bool_t
|
|
api_set_array_element(awk_ext_id_t id, awk_array_t a_cookie,
|
|
const awk_value_t *const index,
|
|
const awk_value_t *const value)
|
|
{
|
|
NODE *array = (NODE *)a_cookie;
|
|
NODE *tmp;
|
|
NODE *elem;
|
|
|
|
/* don't check for index len zero, null str is ok as index */
|
|
if ( array == NULL
|
|
|| array->type != Node_var_array
|
|
|| (array->flags & NO_EXT_SET) != 0
|
|
|| index == NULL
|
|
|| value == NULL
|
|
|| ! valid_subscript_type(index->val_type))
|
|
return awk_false;
|
|
|
|
tmp = awk_value_to_node(index);
|
|
elem = awk_value_to_node(value);
|
|
if (elem->type == Node_var_array) {
|
|
elem->parent_array = array;
|
|
elem->vname = estrdup(index->str_value.str,
|
|
index->str_value.len);
|
|
}
|
|
assoc_set(array, tmp, elem);
|
|
|
|
return awk_true;
|
|
}
|
|
|
|
/*
|
|
* remove_element --- remove an array element
|
|
* common code used by multiple functions
|
|
*/
|
|
|
|
static void
|
|
remove_element(NODE *array, NODE *subscript)
|
|
{
|
|
NODE *val;
|
|
|
|
if (array == NULL)
|
|
fatal(_("remove_element: received null array"));
|
|
|
|
if (subscript == NULL)
|
|
fatal(_("remove_element: received null subscript"));
|
|
|
|
val = in_array(array, subscript);
|
|
|
|
if (val == NULL)
|
|
return;
|
|
|
|
if (val->type == Node_var_array) {
|
|
assoc_clear(val);
|
|
/* cleared a sub-array, free Node_var_array */
|
|
efree(val->vname);
|
|
freenode(val);
|
|
} else
|
|
unref(val);
|
|
|
|
(void) assoc_remove(array, subscript);
|
|
}
|
|
|
|
/*
|
|
* api_del_array_element --- remove the element with the given index.
|
|
* Return success if removed or if element did not exist.
|
|
*/
|
|
|
|
static awk_bool_t
|
|
api_del_array_element(awk_ext_id_t id,
|
|
awk_array_t a_cookie, const awk_value_t* const index)
|
|
{
|
|
NODE *array, *sub;
|
|
|
|
array = (NODE *) a_cookie;
|
|
if ( array == NULL
|
|
|| array->type != Node_var_array
|
|
|| (array->flags & NO_EXT_SET) != 0
|
|
|| index == NULL
|
|
|| ! valid_subscript_type(index->val_type))
|
|
return awk_false;
|
|
|
|
sub = awk_value_to_node(index);
|
|
remove_element(array, sub);
|
|
unref(sub);
|
|
|
|
return awk_true;
|
|
}
|
|
|
|
/*
|
|
* api_get_element_count --- retrieve total number of elements in array.
|
|
* Return false if some kind of error.
|
|
*/
|
|
|
|
static awk_bool_t
|
|
api_get_element_count(awk_ext_id_t id,
|
|
awk_array_t a_cookie, size_t *count)
|
|
{
|
|
NODE *node = (NODE *) a_cookie;
|
|
|
|
if (count == NULL || node == NULL || node->type != Node_var_array)
|
|
return awk_false;
|
|
|
|
*count = node->table_size;
|
|
return awk_true;
|
|
}
|
|
|
|
/* api_create_array --- create a new array cookie to which elements may be added */
|
|
|
|
static awk_array_t
|
|
api_create_array(awk_ext_id_t id)
|
|
{
|
|
NODE *n;
|
|
|
|
getnode(n);
|
|
memset(n, 0, sizeof(NODE));
|
|
null_array(n);
|
|
|
|
return (awk_array_t) n;
|
|
}
|
|
|
|
/* api_clear_array --- clear out an array */
|
|
|
|
static awk_bool_t
|
|
api_clear_array(awk_ext_id_t id, awk_array_t a_cookie)
|
|
{
|
|
NODE *node = (NODE *) a_cookie;
|
|
|
|
if ( node == NULL
|
|
|| node->type != Node_var_array
|
|
|| (node->flags & NO_EXT_SET) != 0)
|
|
return awk_false;
|
|
|
|
assoc_clear(node);
|
|
return awk_true;
|
|
}
|
|
|
|
/* api_destroy_array --- destroy an array */
|
|
|
|
static awk_bool_t
|
|
api_destroy_array(awk_ext_id_t id, awk_array_t a_cookie)
|
|
{
|
|
if (! api_clear_array(id, a_cookie))
|
|
return awk_false;
|
|
freenode((NODE *) a_cookie);
|
|
return awk_true;
|
|
}
|
|
|
|
/* api_flatten_array_typed --- flatten out an array so that it can be looped over easily. */
|
|
|
|
static awk_bool_t
|
|
api_flatten_array_typed(awk_ext_id_t id,
|
|
awk_array_t a_cookie,
|
|
awk_flat_array_t **data,
|
|
awk_valtype_t index_type, awk_valtype_t value_type)
|
|
{
|
|
NODE **list;
|
|
size_t i, j;
|
|
NODE *array = (NODE *) a_cookie;
|
|
size_t alloc_size;
|
|
|
|
if ( array == NULL
|
|
|| array->type != Node_var_array
|
|
|| assoc_empty(array)
|
|
|| data == NULL)
|
|
return awk_false;
|
|
|
|
alloc_size = sizeof(awk_flat_array_t) +
|
|
(array->table_size - 1) * sizeof(awk_element_t);
|
|
|
|
ezalloc(*data, awk_flat_array_t *, alloc_size,
|
|
"api_flatten_array_typed");
|
|
|
|
list = assoc_list(array, "@unsorted", ASORTI);
|
|
|
|
(*data)->opaque1 = array;
|
|
(*data)->opaque2 = list;
|
|
(*data)->count = array->table_size;
|
|
|
|
for (i = j = 0; i < 2 * array->table_size; i += 2, j++) {
|
|
NODE *index, *value;
|
|
|
|
index = list[i];
|
|
value = list[i + 1]; /* number or string or subarray */
|
|
|
|
/* Convert index and value to API types. */
|
|
if (! node_to_awk_value(index,
|
|
& (*data)->elements[j].index, index_type)) {
|
|
fatal(_("api_flatten_array_typed: could not convert index %d to %s"),
|
|
(int) i, valtype2str(index_type));
|
|
}
|
|
if (! node_to_awk_value(value,
|
|
& (*data)->elements[j].value, value_type)) {
|
|
fatal(_("api_flatten_array_typed: could not convert value %d to %s"),
|
|
(int) i, valtype2str(value_type));
|
|
}
|
|
}
|
|
return awk_true;
|
|
}
|
|
|
|
/*
|
|
* api_release_flattened_array --- release array memory,
|
|
* delete any marked elements. Count must match what
|
|
* gawk thinks the size is.
|
|
*/
|
|
|
|
static awk_bool_t
|
|
api_release_flattened_array(awk_ext_id_t id,
|
|
awk_array_t a_cookie,
|
|
awk_flat_array_t *data)
|
|
{
|
|
NODE *array = (NODE *) a_cookie;
|
|
NODE **list;
|
|
size_t i, j, k;
|
|
|
|
if ( array == NULL
|
|
|| array->type != Node_var_array
|
|
|| data == NULL
|
|
|| array != (NODE *) data->opaque1
|
|
|| data->count != array->table_size
|
|
|| data->opaque2 == NULL)
|
|
return awk_false;
|
|
|
|
list = (NODE **) data->opaque2;
|
|
|
|
/* free index nodes */
|
|
for (i = j = 0, k = 2 * array->table_size; i < k; i += 2, j++) {
|
|
/* Delete items flagged for delete. */
|
|
if ( (data->elements[j].flags & AWK_ELEMENT_DELETE) != 0
|
|
&& (array->flags & NO_EXT_SET) == 0) {
|
|
remove_element(array, list[i]);
|
|
}
|
|
unref(list[i]);
|
|
}
|
|
|
|
efree(list);
|
|
efree(data);
|
|
|
|
return awk_true;
|
|
}
|
|
|
|
/* api_create_value --- create a cached value */
|
|
|
|
static awk_bool_t
|
|
api_create_value(awk_ext_id_t id, awk_value_t *value,
|
|
awk_value_cookie_t *result)
|
|
{
|
|
if (value == NULL || result == NULL)
|
|
return awk_false;
|
|
|
|
switch (value->val_type) {
|
|
case AWK_NUMBER:
|
|
case AWK_STRNUM:
|
|
case AWK_STRING:
|
|
case AWK_REGEX:
|
|
break;
|
|
default:
|
|
/* reject anything other than a simple scalar */
|
|
return awk_false;
|
|
}
|
|
|
|
return (awk_bool_t) ((*result = awk_value_to_node(value)) != NULL);
|
|
}
|
|
|
|
/* api_release_value --- release a cached value */
|
|
|
|
static awk_bool_t
|
|
api_release_value(awk_ext_id_t id, awk_value_cookie_t value)
|
|
{
|
|
NODE *val = (NODE *) value;
|
|
|
|
if (val == NULL)
|
|
return awk_false;
|
|
|
|
unref(val);
|
|
return awk_true;
|
|
}
|
|
|
|
/* api_get_mpfr --- allocate an mpfr_ptr */
|
|
|
|
static void *
|
|
api_get_mpfr(awk_ext_id_t id)
|
|
{
|
|
#ifdef HAVE_MPFR
|
|
mpfr_ptr p;
|
|
emalloc(p, mpfr_ptr, sizeof(mpfr_t), "api_get_mpfr");
|
|
mpfr_init(p);
|
|
return p;
|
|
#else
|
|
fatal(_("api_get_mpfr: MPFR not supported"));
|
|
return NULL; // silence compiler warning
|
|
#endif
|
|
}
|
|
|
|
/* api_get_mpz --- allocate an mpz_ptr */
|
|
|
|
static void *
|
|
api_get_mpz(awk_ext_id_t id)
|
|
{
|
|
#ifdef HAVE_MPFR
|
|
mpz_ptr p;
|
|
emalloc(p, mpz_ptr, sizeof (mpz_t), "api_get_mpz");
|
|
|
|
mpz_init(p);
|
|
return p;
|
|
#else
|
|
fatal(_("api_get_mpfr: MPFR not supported"));
|
|
return NULL; // silence compiler warning
|
|
#endif
|
|
}
|
|
|
|
/* api_get_file --- return a handle to an existing or newly opened file */
|
|
|
|
static awk_bool_t
|
|
api_get_file(awk_ext_id_t id, const char *name, size_t namelen, const char *filetype,
|
|
int fd, const awk_input_buf_t **ibufp, const awk_output_buf_t **obufp)
|
|
{
|
|
const struct redirect *f;
|
|
int flag; /* not used, sigh */
|
|
enum redirval redirtype;
|
|
|
|
if (name == NULL || namelen == 0) {
|
|
if (curfile == NULL) {
|
|
INSTRUCTION *pc;
|
|
int save_rule;
|
|
char *save_source;
|
|
|
|
if (nextfile(& curfile, false) <= 0)
|
|
return awk_false;
|
|
|
|
pc = main_beginfile;
|
|
/* save execution state */
|
|
save_rule = currule;
|
|
save_source = source;
|
|
|
|
for (;;) {
|
|
if (pc == NULL)
|
|
fatal(_("cannot find end of BEGINFILE rule"));
|
|
if (pc->opcode == Op_after_beginfile)
|
|
break;
|
|
pc = pc->nexti;
|
|
}
|
|
pc->opcode = Op_stop;
|
|
(void) (*interpret)(main_beginfile);
|
|
pc->opcode = Op_after_beginfile;
|
|
after_beginfile(& curfile);
|
|
/* restore execution state */
|
|
currule = save_rule;
|
|
source = save_source;
|
|
}
|
|
*ibufp = &curfile->public;
|
|
*obufp = NULL;
|
|
|
|
return awk_true;
|
|
}
|
|
|
|
redirtype = redirect_none;
|
|
switch (filetype[0]) {
|
|
case '<':
|
|
if (filetype[1] == '\0')
|
|
redirtype = redirect_input;
|
|
break;
|
|
case '>':
|
|
switch (filetype[1]) {
|
|
case '\0':
|
|
redirtype = redirect_output;
|
|
break;
|
|
case '>':
|
|
if (filetype[2] == '\0')
|
|
redirtype = redirect_append;
|
|
break;
|
|
}
|
|
break;
|
|
case '|':
|
|
if (filetype[2] == '\0') {
|
|
switch (filetype[1]) {
|
|
case '>':
|
|
redirtype = redirect_pipe;
|
|
break;
|
|
case '<':
|
|
redirtype = redirect_pipein;
|
|
break;
|
|
case '&':
|
|
redirtype = redirect_twoway;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (redirtype == redirect_none) {
|
|
warning(_("cannot open unrecognized file type `%s' for `%s'"),
|
|
filetype, name);
|
|
return awk_false;
|
|
}
|
|
|
|
if ((f = redirect_string(name, namelen, 0, redirtype, &flag, fd, false)) == NULL)
|
|
return awk_false;
|
|
|
|
*ibufp = f->iop ? & f->iop->public : NULL;
|
|
*obufp = f->output.fp ? & f->output : NULL;
|
|
return awk_true;
|
|
}
|
|
|
|
/*
|
|
* Register a version string for this extension with gawk.
|
|
*/
|
|
|
|
struct version_info {
|
|
const char *version;
|
|
struct version_info *next;
|
|
};
|
|
|
|
static struct version_info *vi_head;
|
|
|
|
/* api_register_ext_version --- add an extension version string to the list */
|
|
|
|
static void
|
|
api_register_ext_version(awk_ext_id_t id, const char *version)
|
|
{
|
|
struct version_info *info;
|
|
|
|
if (version == NULL)
|
|
return;
|
|
|
|
(void) id;
|
|
|
|
emalloc(info, struct version_info *, sizeof(struct version_info), "register_ext_version");
|
|
info->version = version;
|
|
info->next = vi_head;
|
|
vi_head = info;
|
|
}
|
|
|
|
/* the struct api */
|
|
gawk_api_t api_impl = {
|
|
/* data */
|
|
GAWK_API_MAJOR_VERSION, /* major and minor versions */
|
|
GAWK_API_MINOR_VERSION,
|
|
|
|
#ifdef HAVE_MPFR
|
|
__GNU_MP_VERSION,
|
|
__GNU_MP_VERSION_MINOR,
|
|
MPFR_VERSION_MAJOR,
|
|
MPFR_VERSION_MINOR,
|
|
#else
|
|
0, 0, 0, 0,
|
|
#endif
|
|
|
|
{ 0 }, /* do_flags */
|
|
|
|
/* registration functions */
|
|
api_add_ext_func,
|
|
api_register_input_parser,
|
|
api_register_output_wrapper,
|
|
api_register_two_way_processor,
|
|
api_awk_atexit,
|
|
api_register_ext_version,
|
|
|
|
/* message printing functions */
|
|
api_fatal,
|
|
api_warning,
|
|
api_lintwarn,
|
|
api_nonfatal,
|
|
|
|
/* updating ERRNO */
|
|
api_update_ERRNO_int,
|
|
api_update_ERRNO_string,
|
|
api_unset_ERRNO,
|
|
|
|
/* Function arguments */
|
|
api_get_argument,
|
|
api_set_argument,
|
|
|
|
/* Accessing and installing variables and constants */
|
|
api_sym_lookup,
|
|
api_sym_update,
|
|
|
|
/* Accessing and modifying variables via scalar cookies */
|
|
api_sym_lookup_scalar,
|
|
api_sym_update_scalar,
|
|
|
|
/* Cached values */
|
|
api_create_value,
|
|
api_release_value,
|
|
|
|
/* Array management */
|
|
api_get_element_count,
|
|
api_get_array_element,
|
|
api_set_array_element,
|
|
api_del_array_element,
|
|
api_create_array,
|
|
api_clear_array,
|
|
api_flatten_array_typed,
|
|
api_release_flattened_array,
|
|
|
|
/* Memory allocation */
|
|
malloc,
|
|
calloc,
|
|
realloc,
|
|
free,
|
|
api_get_mpfr,
|
|
api_get_mpz,
|
|
|
|
/* Find/open a file */
|
|
api_get_file,
|
|
|
|
/* Additional array hook to destroy an array */
|
|
api_destroy_array,
|
|
};
|
|
|
|
/* init_ext_api --- init the extension API */
|
|
|
|
void
|
|
init_ext_api()
|
|
{
|
|
/* force values to 1 / 0 */
|
|
api_impl.do_flags[0] = (do_lint ? 1 : 0);
|
|
api_impl.do_flags[1] = (do_traditional ? 1 : 0);
|
|
api_impl.do_flags[2] = (do_profile ? 1 : 0);
|
|
api_impl.do_flags[3] = (do_sandbox ? 1 : 0);
|
|
api_impl.do_flags[4] = (do_debug ? 1 : 0);
|
|
api_impl.do_flags[5] = (do_mpfr ? 1 : 0);
|
|
}
|
|
|
|
/* update_ext_api --- update the variables in the API that can change */
|
|
|
|
void
|
|
update_ext_api()
|
|
{
|
|
api_impl.do_flags[0] = (do_lint ? 1 : 0);
|
|
}
|
|
|
|
/* print_ext_versions --- print the list */
|
|
|
|
extern void
|
|
print_ext_versions(void)
|
|
{
|
|
struct version_info *p;
|
|
|
|
for (p = vi_head; p != NULL; p = p->next)
|
|
printf("%s\n", p->version);
|
|
}
|
|
|
|
/* valtype2str --- return a printable representation of a value type */
|
|
|
|
static const char *
|
|
valtype2str(awk_valtype_t type)
|
|
{
|
|
static char buf[100];
|
|
|
|
// Important: keep in same order as in gawkapi.h!
|
|
static const char *values[] = {
|
|
"AWK_UNDEFINED",
|
|
"AWK_NUMBER",
|
|
"AWK_STRING",
|
|
"AWK_REGEX",
|
|
"AWK_STRNUM",
|
|
"AWK_ARRAY",
|
|
"AWK_SCALAR",
|
|
"AWK_VALUE_COOKIE",
|
|
};
|
|
|
|
if (AWK_UNDEFINED <= type && type <= AWK_VALUE_COOKIE)
|
|
return values[(int) type];
|
|
|
|
sprintf(buf, "unknown type! (%d)", (int) type);
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* ns_lookup --- correctly build name before looking it up */
|
|
|
|
static NODE *
|
|
ns_lookup(const char *name_space, const char *name, char **fullname)
|
|
{
|
|
assert(name_space != NULL);
|
|
assert(name != NULL);
|
|
|
|
if (name_space[0] == '\0' || strcmp(name_space, awk_namespace) == 0) {
|
|
if (fullname != NULL)
|
|
*fullname = estrdup(name, strlen(name));
|
|
return lookup(name);
|
|
}
|
|
|
|
size_t len = strlen(name_space) + 2 + strlen(name) + 1;
|
|
char *buf;
|
|
emalloc(buf, char *, len, "ns_lookup");
|
|
sprintf(buf, "%s::%s", name_space, name);
|
|
|
|
NODE *f = lookup(buf);
|
|
if (fullname != NULL)
|
|
*fullname = buf;
|
|
else
|
|
efree((void *) buf);
|
|
|
|
return f;
|
|
}
|