aboutsummaryrefslogtreecommitdiffstats
path: root/gawkapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'gawkapi.c')
-rw-r--r--gawkapi.c411
1 files changed, 361 insertions, 50 deletions
diff --git a/gawkapi.c b/gawkapi.c
index 1ef2f79e..4c6a2f8f 100644
--- a/gawkapi.c
+++ b/gawkapi.c
@@ -2,22 +2,22 @@
* gawkapi.c -- Implement the functions defined for gawkapi.h
*/
-/*
+/*
* Copyright (C) 2012-2016 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
@@ -25,7 +25,13 @@
#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 char *valtype2str(awk_valtype_t type);
/*
* api_get_argument --- get the count'th paramater, zero-based.
@@ -71,7 +77,7 @@ api_get_argument(awk_ext_id_t id, size_t count,
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)
@@ -159,6 +165,15 @@ awk_value_to_node(const awk_value_t *retval)
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)
@@ -193,6 +208,20 @@ api_fatal(awk_ext_id_t id, const char *format, ...)
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
@@ -307,7 +336,7 @@ api_unset_ERRNO(awk_ext_id_t id)
static awk_bool_t
api_add_ext_func(awk_ext_id_t id,
const char *namespace,
- const awk_ext_func_t *func)
+ awk_ext_func_t *func)
{
(void) id;
(void) namespace;
@@ -372,6 +401,68 @@ api_awk_atexit(awk_ext_id_t id,
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_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;
+}
+
/* node_to_awk_value --- convert a node into a value for an extension */
static awk_bool_t
@@ -408,52 +499,137 @@ node_to_awk_value(NODE *node, awk_value_t *val, awk_valtype_t wanted)
/* a scalar value */
switch (wanted) {
case AWK_NUMBER:
- val->val_type = AWK_NUMBER;
-
- (void) force_number(node);
- if ((node->flags & NUMCUR) != 0) {
+ if (node->flags & REGEX)
+ val->val_type = AWK_REGEX;
+ else {
+ val->val_type = AWK_NUMBER;
+ (void) force_number(node);
val->num_value = get_number_d(node);
ret = awk_true;
}
break;
- case AWK_STRING:
- val->val_type = AWK_STRING;
+ case AWK_STRNUM:
+ switch (fixtype(node)->flags & (STRING|NUMBER|USER_INPUT|REGEX)) {
+ 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);
- if ((node->flags & STRCUR) != 0) {
- val->str_value.str = node->stptr;
- val->str_value.len = node->stlen;
+ assign_string(node, val, AWK_STRING);
+ ret = awk_true;
+ break;
+
+ case AWK_REGEX:
+ switch (fixtype(node)->flags & (STRING|NUMBER|USER_INPUT|REGEX)) {
+ 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:
+ 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:
- if ((node->flags & NUMBER) != 0) {
- val->val_type = AWK_NUMBER;
- } else if ((node->flags & STRING) != 0) {
+ switch (fixtype(node)->flags & (STRING|NUMBER|USER_INPUT|REGEX)) {
+ case STRING:
val->val_type = AWK_STRING;
- } else
+ 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;
- ret = awk_false;
+ break;
+ }
break;
case AWK_UNDEFINED:
/* return true and actual type for request of undefined */
- if (node == Nnull_string) {
- val->val_type = AWK_UNDEFINED;
+ switch (fixtype(node)->flags & (STRING|NUMBER|USER_INPUT|REGEX)) {
+ case STRING:
+ assign_string(node, val, AWK_STRING);
ret = awk_true;
- } else if ((node->flags & NUMBER) != 0) {
+ break;
+ case NUMBER:
val->val_type = AWK_NUMBER;
val->num_value = get_number_d(node);
ret = awk_true;
- } else if ((node->flags & STRING) != 0) {
- val->val_type = AWK_STRING;
- val->str_value.str = node->stptr;
- val->str_value.len = node->stlen;
+ break;
+ case NUMBER|USER_INPUT:
+ assign_string(node, val, AWK_STRNUM);
ret = awk_true;
- } else
+ 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:
@@ -485,7 +661,7 @@ node_to_awk_value(NODE *node, awk_value_t *val, awk_valtype_t wanted)
* - 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.
+ * to scalar or array.
*/
/*
* Lookup a variable, fills in value. No messing with the value
@@ -556,7 +732,9 @@ api_sym_update(awk_ext_id_t id,
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:
@@ -654,7 +832,9 @@ api_sym_update_scalar(awk_ext_id_t id,
return awk_true;
}
break;
+
case AWK_STRING:
+ case AWK_STRNUM:
if (node->var_value->valref == 1) {
NODE *r = node->var_value;
@@ -668,17 +848,22 @@ api_sym_update_scalar(awk_ext_id_t id,
/* make_str_node(s, l, ALREADY_MALLOCED): */
r->numbr = 0;
r->flags = (MALLOC|STRING|STRCUR);
- r->stfmt = -1;
+ 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;
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;
}
@@ -701,7 +886,9 @@ 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;
@@ -782,7 +969,6 @@ api_set_array_element(awk_ext_id_t id, awk_array_t a_cookie,
tmp = awk_value_to_node(index);
aptr = assoc_lookup(array, tmp);
- unref(tmp);
unref(*aptr);
elem = *aptr = awk_value_to_node(value);
if (elem->type == Node_var_array) {
@@ -790,12 +976,15 @@ api_set_array_element(awk_ext_id_t id, awk_array_t a_cookie,
elem->vname = estrdup(index->str_value.str,
index->str_value.len);
}
+ if (array->astore != NULL)
+ (*array->astore)(array, tmp);
+ unref(tmp);
return awk_true;
}
/*
- * remove_element --- remove an array element
+ * remove_element --- remove an array element
* common code used by multiple functions
*/
@@ -900,12 +1089,13 @@ api_clear_array(awk_ext_id_t id, awk_array_t a_cookie)
return awk_true;
}
-/* api_flatten_array --- flatten out an array so that it can be looped over easily. */
+/* api_flatten_array_typed --- flatten out an array so that it can be looped over easily. */
static awk_bool_t
-api_flatten_array(awk_ext_id_t id,
+api_flatten_array_typed(awk_ext_id_t id,
awk_array_t a_cookie,
- awk_flat_array_t **data)
+ awk_flat_array_t **data,
+ awk_valtype_t index_type, awk_valtype_t value_type)
{
NODE **list;
size_t i, j;
@@ -922,7 +1112,7 @@ api_flatten_array(awk_ext_id_t id,
(array->table_size - 1) * sizeof(awk_element_t);
emalloc(*data, awk_flat_array_t *, alloc_size,
- "api_flatten_array");
+ "api_flatten_array_typed");
memset(*data, 0, alloc_size);
list = assoc_list(array, "@unsorted", ASORTI);
@@ -937,21 +1127,16 @@ api_flatten_array(awk_ext_id_t id,
index = list[i];
value = list[i + 1]; /* number or string or subarray */
- /*
- * Convert index and value to ext types. Force the
- * index to be a string, since indices are always
- * conceptually strings, regardless of internal optimizations
- * to treat them as integers in some cases.
- */
+ /* Convert index and value to API types. */
if (! node_to_awk_value(index,
- & (*data)->elements[j].index, AWK_STRING)) {
- fatal(_("api_flatten_array: could not convert index %d\n"),
- (int) i);
+ & (*data)->elements[j].index, index_type)) {
+ fatal(_("api_flatten_array_typed: could not convert index %d to %s\n"),
+ (int) i, valtype2str(index_type));
}
if (! node_to_awk_value(value,
- & (*data)->elements[j].value, AWK_UNDEFINED)) {
- fatal(_("api_flatten_array: could not convert value %d\n"),
- (int) i);
+ & (*data)->elements[j].value, value_type)) {
+ fatal(_("api_flatten_array_typed: could not convert value %d to %s\n"),
+ (int) i, valtype2str(value_type));
}
}
return awk_true;
@@ -1009,7 +1194,9 @@ api_create_value(awk_ext_id_t id, awk_value_t *value,
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 */
@@ -1033,6 +1220,99 @@ api_release_value(awk_ext_id_t id, awk_value_cookie_t value)
return awk_true;
}
+/* 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.
*/
@@ -1081,6 +1361,7 @@ gawk_api_t api_impl = {
api_fatal,
api_warning,
api_lintwarn,
+ api_nonfatal,
/* updating ERRNO */
api_update_ERRNO_int,
@@ -1110,7 +1391,7 @@ gawk_api_t api_impl = {
api_del_array_element,
api_create_array,
api_clear_array,
- api_flatten_array,
+ api_flatten_array_typed,
api_release_flattened_array,
/* Memory allocation */
@@ -1118,6 +1399,9 @@ gawk_api_t api_impl = {
calloc,
realloc,
free,
+
+ /* Find/open a file */
+ api_get_file,
};
/* init_ext_api --- init the extension API */
@@ -1152,3 +1436,30 @@ print_ext_versions(void)
for (p = vi_head; p != NULL; p = p->next)
printf("%s\n", p->version);
}
+
+/* valtype2str --- return a printable representation of a value type */
+
+static char *
+valtype2str(awk_valtype_t type)
+{
+ static char buf[100];
+
+ // Important: keep in same order as in gawkapi.h!
+ static 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;
+}