(root)/
gawk-5.2.2/
gawkapi.c
       1  /*
       2   * gawkapi.c -- Implement the functions defined for gawkapi.h
       3   */
       4  
       5  /*
       6   * Copyright (C) 2012-2019, 2021, 2022, the Free Software Foundation, Inc.
       7   *
       8   * This file is part of GAWK, the GNU implementation of the
       9   * AWK Programming Language.
      10   *
      11   * GAWK is free software; you can redistribute it and/or modify
      12   * it under the terms of the GNU General Public License as published by
      13   * the Free Software Foundation; either version 3 of the License, or
      14   * (at your option) any later version.
      15   *
      16   * GAWK is distributed in the hope that it will be useful,
      17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19   * GNU General Public License for more details.
      20   *
      21   * You should have received a copy of the GNU General Public License
      22   * along with this program; if not, write to the Free Software
      23   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
      24   */
      25  
      26  #include "awk.h"
      27  
      28  /* Declare some globals used by api_get_file: */
      29  extern IOBUF *curfile;
      30  extern INSTRUCTION *main_beginfile;
      31  extern int currule;
      32  
      33  static awk_bool_t node_to_awk_value(NODE *node, awk_value_t *result, awk_valtype_t wanted);
      34  static const char *valtype2str(awk_valtype_t type);
      35  static NODE *ns_lookup(const char *name_space, const char *name, char **full_name);
      36  
      37  /*
      38   * api_get_argument --- get the count'th paramater, zero-based.
      39   *
      40   * Returns false if count is out of range, or if actual paramater
      41   * does not match what is specified in wanted. In the latter
      42   * case, fills in result->val_type with the actual type.
      43   */
      44  
      45  static awk_bool_t
      46  api_get_argument(awk_ext_id_t id, size_t count,
      47  			awk_valtype_t wanted, awk_value_t *result)
      48  {
      49  #ifdef DYNAMIC
      50  	NODE *arg;
      51  
      52  	if (result == NULL)
      53  		return awk_false;
      54  
      55  	(void) id;
      56  
      57  	/* set up default result */
      58  	memset(result, 0, sizeof(*result));
      59  	result->val_type = AWK_UNDEFINED;
      60  
      61  	/*
      62  	 * Song and dance here.  get_array_argument() and get_scalar_argument()
      63  	 * will force a change in type of a parameter that is Node_var_new.
      64  	 *
      65  	 * Start by looking at the unadulterated argument as it was passed.
      66  	 */
      67  	arg = get_argument(count);
      68  	if (arg == NULL)
      69  		return awk_false;
      70  
      71  	/* if type is undefined */
      72  	if (arg->type == Node_var_new || arg->type == Node_elem_new) {
      73  		if (wanted == AWK_UNDEFINED)
      74  			return awk_true;
      75  		else if (wanted == AWK_ARRAY) {
      76  			goto array;
      77  		} else {
      78  			goto scalar;
      79  		}
      80  	}
      81  
      82  	/* at this point, we have real type */
      83  	if (arg->type == Node_var_array || arg->type == Node_array_ref) {
      84  		if (wanted != AWK_ARRAY && wanted != AWK_UNDEFINED)
      85  			return awk_false;
      86  		goto array;
      87  	} else
      88  		goto scalar;
      89  
      90  array:
      91  	/* get the array here */
      92  	arg = get_array_argument(arg, count);
      93  	if (arg == NULL)
      94  		return awk_false;
      95  
      96  	return node_to_awk_value(arg, result, wanted);
      97  
      98  scalar:
      99  	/* at this point we have a real type that is not an array */
     100  	arg = get_scalar_argument(arg, count);
     101  	if (arg == NULL)
     102  		return awk_false;
     103  
     104  	return node_to_awk_value(arg, result, wanted);
     105  #else
     106  	return awk_false;
     107  #endif
     108  }
     109  
     110  /* api_set_argument --- convert an argument to an array */
     111  
     112  static awk_bool_t
     113  api_set_argument(awk_ext_id_t id,
     114  		size_t count,
     115  		awk_array_t new_array)
     116  {
     117  #ifdef DYNAMIC
     118  	NODE *arg;
     119  	NODE *array = (NODE *) new_array;
     120  
     121  	(void) id;
     122  
     123  	if (array == NULL || array->type != Node_var_array)
     124  		return awk_false;
     125  
     126  	if (   (arg = get_argument(count)) == NULL
     127  	    || (arg->type != Node_var_new && arg->type != Node_elem_new))
     128  		return awk_false;
     129  
     130  	arg = get_array_argument(arg, count);
     131  	if (arg == NULL)
     132  		return awk_false;
     133  
     134  	array->vname = arg->vname;
     135  	*arg = *array;
     136  	freenode(array);
     137  
     138  	return awk_true;
     139  #else
     140  	return awk_false;
     141  #endif
     142  }
     143  
     144  /* awk_value_to_node --- convert a value into a NODE */
     145  
     146  NODE *
     147  awk_value_to_node(const awk_value_t *retval)
     148  {
     149  	NODE *ext_ret_val = NULL;
     150  	NODE *v;
     151  #ifdef HAVE_MPFR
     152  	int tval = 0;
     153  #endif
     154  
     155  	if (retval == NULL)
     156  		fatal(_("awk_value_to_node: received null retval"));
     157  
     158  	switch (retval->val_type) {
     159  	case AWK_ARRAY:
     160  		ext_ret_val = (NODE *) retval->array_cookie;
     161  		break;
     162  	case AWK_UNDEFINED:
     163  		ext_ret_val = dupnode(Nnull_string);
     164  		break;
     165  	case AWK_BOOL:
     166  		ext_ret_val = make_bool_node(retval->bool_value != awk_false);
     167  		break;
     168  	case AWK_NUMBER:
     169  		switch (retval->num_type) {
     170  		case AWK_NUMBER_TYPE_DOUBLE:
     171  			ext_ret_val = make_number(retval->num_value);
     172  			break;
     173  		case AWK_NUMBER_TYPE_MPFR:
     174  #ifdef HAVE_MPFR
     175  			if (! do_mpfr)
     176  				fatal(_("awk_value_to_node: not in MPFR mode"));
     177  			ext_ret_val = make_number_node(MPFN);
     178  			mpfr_init(ext_ret_val->mpg_numbr);
     179  			tval = mpfr_set(ext_ret_val->mpg_numbr, (mpfr_srcptr) retval->num_ptr, ROUND_MODE);
     180  			IEEE_FMT(ext_ret_val->mpg_numbr, tval);
     181  			mpfr_clear(retval->num_ptr);
     182  #else
     183  			fatal(_("awk_value_to_node: MPFR not supported"));
     184  #endif
     185  			break;
     186  		case AWK_NUMBER_TYPE_MPZ:
     187  #ifdef HAVE_MPFR
     188  			if (! do_mpfr)
     189  				fatal(_("awk_value_to_node: not in MPFR mode"));
     190  			ext_ret_val = make_number_node(MPZN);
     191  			mpz_init(ext_ret_val->mpg_i);
     192  			mpz_set(ext_ret_val->mpg_i, (mpz_ptr) retval->num_ptr);
     193  			mpz_clear(retval->num_ptr);
     194  #else
     195  			fatal(_("awk_value_to_node: MPFR not supported"));
     196  #endif
     197  			break;
     198  		default:
     199  			fatal(_("awk_value_to_node: invalid number type `%d'"), retval->num_type);
     200  			break;
     201  		}
     202  		break;
     203  	case AWK_STRING:
     204  		ext_ret_val = make_str_node(retval->str_value.str,
     205  				retval->str_value.len, ALREADY_MALLOCED);
     206  		break;
     207  	case AWK_STRNUM:
     208  		ext_ret_val = make_str_node(retval->str_value.str,
     209  				retval->str_value.len, ALREADY_MALLOCED);
     210  		ext_ret_val->flags |= USER_INPUT;
     211  		break;
     212  	case AWK_REGEX:
     213  		ext_ret_val = make_typed_regex(retval->str_value.str,
     214  				retval->str_value.len);
     215  		break;
     216  	case AWK_SCALAR:
     217  		v = (NODE *) retval->scalar_cookie;
     218  		if (v->type != Node_var)
     219  			ext_ret_val = NULL;
     220  		else
     221  			ext_ret_val = dupnode(v->var_value);
     222  		break;
     223  	case AWK_VALUE_COOKIE:
     224  		ext_ret_val = dupnode((NODE *)(retval->value_cookie));
     225  		break;
     226  	default:	/* any invalid type */
     227  		ext_ret_val = NULL;
     228  		break;
     229  	}
     230  
     231  	return ext_ret_val;
     232  }
     233  
     234  /* Functions to print messages */
     235  
     236  /* api_fatal --- print a fatal message and exit */
     237  
     238  static void
     239  api_fatal(awk_ext_id_t id, const char *format, ...)
     240  {
     241  	va_list args;
     242  
     243  	(void) id;
     244  
     245  	va_start(args, format);
     246  	err(true, _("fatal: "), format, args);
     247  	va_end(args);
     248  }
     249  
     250  /* api_nonfatal --- print a non fatal error message */
     251  
     252  static void
     253  api_nonfatal(awk_ext_id_t id, const char *format, ...)
     254  {
     255  	va_list args;
     256  
     257  	(void) id;
     258  
     259  	va_start(args, format);
     260  	err(false, _("error: "), format, args);
     261  	va_end(args);
     262  }
     263  
     264  /* api_warning --- print a warning message */
     265  
     266  static void
     267  api_warning(awk_ext_id_t id, const char *format, ...)
     268  {
     269  	va_list args;
     270  
     271  	(void) id;
     272  
     273  	va_start(args, format);
     274  	err(false, _("warning: "), format, args);
     275  	va_end(args);
     276  }
     277  
     278  /* api_lintwarn --- print a lint warning message and exit if appropriate */
     279  
     280  static void
     281  api_lintwarn(awk_ext_id_t id, const char *format, ...)
     282  {
     283  	va_list args;
     284  
     285  	(void) id;
     286  
     287  	va_start(args, format);
     288  	if (lintfunc == r_fatal) {
     289  		err(true, _("fatal: "), format, args);
     290  	} else {
     291  		err(false, _("warning: "), format, args);
     292  	}
     293  	va_end(args);
     294  }
     295  
     296  /* api_register_input_parser --- register an input_parser; for opening files read-only */
     297  
     298  static void
     299  api_register_input_parser(awk_ext_id_t id, awk_input_parser_t *input_parser)
     300  {
     301  	(void) id;
     302  
     303  	if (input_parser == NULL)
     304  		return;
     305  
     306  	register_input_parser(input_parser);
     307  }
     308  
     309  /* api_register_output_wrapper --- register an output wrapper, for writing files / two-way pipes */
     310  
     311  static void api_register_output_wrapper(awk_ext_id_t id,
     312  		awk_output_wrapper_t *output_wrapper)
     313  {
     314  	(void) id;
     315  
     316  	if (output_wrapper == NULL)
     317  		return;
     318  
     319  	register_output_wrapper(output_wrapper);
     320  }
     321  
     322  /* api_register_two_way_processor --- register a processor for two way I/O */
     323  
     324  static void
     325  api_register_two_way_processor(awk_ext_id_t id,
     326  		awk_two_way_processor_t *two_way_processor)
     327  {
     328  	(void) id;
     329  
     330  	if (two_way_processor == NULL)
     331  		return;
     332  
     333  	register_two_way_processor(two_way_processor);
     334  }
     335  
     336  /* Functions to update ERRNO */
     337  
     338  /* api_update_ERRNO_int --- update ERRNO with an integer value */
     339  
     340  static void
     341  api_update_ERRNO_int(awk_ext_id_t id, int errno_val)
     342  {
     343  	(void) id;
     344  
     345  	update_ERRNO_int(errno_val);
     346  }
     347  
     348  /* api_update_ERRNO_string --- update ERRNO with a string value */
     349  
     350  static void
     351  api_update_ERRNO_string(awk_ext_id_t id,
     352  			const char *string)
     353  {
     354  	(void) id;
     355  
     356  	if (string == NULL)
     357  		return;
     358  
     359  	update_ERRNO_string(string);
     360  }
     361  
     362  /* api_unset_ERRNO --- unset ERRNO */
     363  
     364  static void
     365  api_unset_ERRNO(awk_ext_id_t id)
     366  {
     367  	(void) id;
     368  
     369  	unset_ERRNO();
     370  }
     371  
     372  
     373  /* api_add_ext_func --- add a function to the interpreter, returns true upon success */
     374  
     375  static awk_bool_t
     376  api_add_ext_func(awk_ext_id_t id,
     377  		const char *name_space,
     378  		awk_ext_func_t *func)
     379  {
     380  	(void) id;
     381  
     382  	if (func == NULL)
     383  		return awk_false;
     384  
     385  	if (name_space == NULL)
     386  		fatal(_("add_ext_func: received NULL name_space parameter"));
     387  
     388  #ifdef DYNAMIC
     389  	return make_builtin(name_space, func);
     390  #else
     391  	return awk_false;
     392  #endif
     393  }
     394  
     395  /* Stuff for exit handler - do it as linked list */
     396  
     397  struct ext_exit_handler {
     398  	struct ext_exit_handler *next;
     399  	void (*funcp)(void *data, int exit_status);
     400  	void *arg0;
     401  };
     402  static struct ext_exit_handler *list_head = NULL;
     403  
     404  /* run_ext_exit_handlers --- run the extension exit handlers, LIFO order */
     405  
     406  void
     407  run_ext_exit_handlers(int exitval)
     408  {
     409  	struct ext_exit_handler *p, *next;
     410  
     411  	for (p = list_head; p != NULL; p = next) {
     412  		next = p->next;
     413  		p->funcp(p->arg0, exitval);
     414  		free(p);
     415  	}
     416  	list_head = NULL;
     417  }
     418  
     419  /* api_awk_atexit --- add an exit call back */
     420  
     421  static void
     422  api_awk_atexit(awk_ext_id_t id,
     423  		void (*funcp)(void *data, int exit_status),
     424  		void *arg0)
     425  {
     426  	struct ext_exit_handler *p;
     427  
     428  	(void) id;
     429  
     430  	if (funcp == NULL)
     431  		return;
     432  
     433  	/* allocate memory */
     434  	emalloc(p, struct ext_exit_handler *, sizeof(struct ext_exit_handler), "api_awk_atexit");
     435  
     436  	/* fill it in */
     437  	p->funcp = funcp;
     438  	p->arg0 = arg0;
     439  
     440  	/* add to linked list, LIFO order */
     441  	p->next = list_head;
     442  	list_head = p;
     443  }
     444  
     445  static struct {
     446  	char **strings;
     447  	size_t i, size;
     448  } scopy;
     449  
     450  /* free_api_string_copies --- release memory used by string copies */
     451  
     452  void
     453  free_api_string_copies()
     454  {
     455  	size_t i;
     456  
     457  	for (i = 0; i < scopy.i; i++)
     458  		free(scopy.strings[i]);
     459  	scopy.i = 0;
     460  }
     461  
     462  /* assign_string --- return a string node with NUL termination */
     463  
     464  static inline void
     465  assign_string(NODE *node, awk_value_t *val, awk_valtype_t val_type)
     466  {
     467  	val->val_type = val_type;
     468  	if (node->stptr[node->stlen] != '\0') {
     469  		/*
     470  		 * This is an unterminated field string, so make a copy.
     471  		 * This should happen only for $n where n > 0 and n < NF.
     472  		 */
     473  		char *s;
     474  
     475  		assert((node->flags & MALLOC) == 0);
     476  		if (scopy.i == scopy.size) {
     477  			/* expand list */
     478  			if (scopy.size == 0)
     479  				scopy.size = 8;	/* initial size */
     480  			else
     481  				scopy.size *= 2;
     482  			erealloc(scopy.strings, char **, scopy.size * sizeof(char *), "assign_string");
     483  		}
     484  		emalloc(s, char *, node->stlen + 1, "assign_string");
     485  		memcpy(s, node->stptr, node->stlen);
     486  		s[node->stlen] = '\0';
     487  		val->str_value.str = scopy.strings[scopy.i++] = s;
     488  	}
     489  	else
     490  		val->str_value.str = node->stptr;
     491  	val->str_value.len = node->stlen;
     492  }
     493  
     494  /* assign_number -- return a number node */
     495  
     496  #define assign_double(val) \
     497  	val->num_value = node->numbr; \
     498  	val->num_type = AWK_NUMBER_TYPE_DOUBLE; \
     499  	val->num_ptr = NULL
     500  
     501  static inline void
     502  assign_number(NODE *node, awk_value_t *val)
     503  {
     504  	val->val_type = AWK_NUMBER;
     505  
     506  #ifndef HAVE_MPFR
     507  	assign_double(val);
     508  #else
     509  	switch (node->flags & (MPFN|MPZN)) {
     510  	case 0:
     511  		assign_double(val);
     512  		break;
     513  	case MPFN:
     514  		val->num_value = mpfr_get_d(node->mpg_numbr, ROUND_MODE);
     515  		val->num_type = AWK_NUMBER_TYPE_MPFR;
     516  		val->num_ptr = &node->mpg_numbr;
     517  		break;
     518  	case MPZN:
     519  		val->num_value = mpz_get_d(node->mpg_i);
     520  		val->num_type = AWK_NUMBER_TYPE_MPZ;
     521  		val->num_ptr = &node->mpg_i;
     522  		break;
     523  	default:
     524  		fatal(_("node_to_awk_value: detected invalid numeric flags combination `%s'; please file a bug report"), flags2str(node->flags));
     525  		break;
     526  	}
     527  #endif
     528  }
     529  #undef assign_double
     530  
     531  /* assign_regex --- return a regex node */
     532  
     533  static inline void
     534  assign_regex(NODE *node, awk_value_t *val)
     535  {
     536  	/* a REGEX node cannot be an unterminated field string */
     537  	assert((node->flags & MALLOC) != 0);
     538  	assert(node->stptr[node->stlen] == '\0');
     539  	val->str_value.str = node->stptr;
     540  	val->str_value.len = node->stlen;
     541  	val->val_type = AWK_REGEX;
     542  }
     543  
     544  /* assign_bool --- return a bool node */
     545  
     546  static inline void
     547  assign_bool(NODE *node, awk_value_t *val)
     548  {
     549  	assert((node->flags & BOOLVAL) != 0);
     550  	val->val_type = AWK_BOOL;
     551  	val->bool_value = get_number_si(node) != 0 ? awk_true : awk_false;
     552  }
     553  
     554  /* node_to_awk_value --- convert a node into a value for an extension */
     555  
     556  static awk_bool_t
     557  node_to_awk_value(NODE *node, awk_value_t *val, awk_valtype_t wanted)
     558  {
     559  	awk_bool_t ret = awk_false;
     560  
     561  	if (node == NULL)
     562  		fatal(_("node_to_awk_value: received null node"));
     563  
     564  	if (val == NULL)
     565  		fatal(_("node_to_awk_value: received null val"));
     566  
     567  	switch (node->type) {
     568  	case Node_var_new:	/* undefined variable */
     569  	case Node_elem_new:	/* undefined element */
     570  		val->val_type = AWK_UNDEFINED;
     571  		if (wanted == AWK_UNDEFINED) {
     572  			ret = awk_true;
     573  		}
     574  		break;
     575  
     576  	case Node_var:
     577  		/* a scalar value */
     578  		if (wanted == AWK_SCALAR) {
     579  			val->val_type = AWK_SCALAR;
     580  			val->scalar_cookie = (void *) node;
     581  			ret = awk_true;
     582  			break;
     583  		}
     584  
     585  		node = node->var_value;
     586  		/* FALL THROUGH */
     587  	case Node_val:
     588  		/* a scalar value */
     589  		switch (wanted) {
     590  		case AWK_BOOL:
     591  			if ((node->flags & BOOLVAL) != 0) {
     592  				assign_bool(node, val);
     593  				ret = awk_true;
     594  			} else
     595  				ret = awk_false;
     596  			break;
     597  
     598  		case AWK_NUMBER:
     599  			if ((node->flags & REGEX) != 0)
     600  				val->val_type = AWK_REGEX;
     601  			else {
     602  				(void) force_number(node);
     603  				assign_number(node, val);
     604  				ret = awk_true;
     605  			}
     606  			break;
     607  
     608  		case AWK_STRNUM:
     609  			switch (fixtype(node)->flags & (STRING|NUMBER|USER_INPUT|REGEX|BOOLVAL)) {
     610  			case NUMBER|BOOLVAL:
     611  				val->val_type = AWK_BOOL;
     612  				break;
     613  			case STRING:
     614  				val->val_type = AWK_STRING;
     615  				break;
     616  			case NUMBER:
     617  				(void) force_string(node);
     618  				/* fall through */
     619  			case NUMBER|USER_INPUT:
     620  				assign_string(node, val, AWK_STRNUM);
     621  				ret = awk_true;
     622  				break;
     623  			case REGEX:
     624  				val->val_type = AWK_REGEX;
     625  				break;
     626  			case NUMBER|STRING:
     627  				if (node == Nnull_string) {
     628  					val->val_type = AWK_UNDEFINED;
     629  					break;
     630  				}
     631  				/* fall through */
     632  			default:
     633  				warning(_("node_to_awk_value detected invalid flags combination `%s'; please file a bug report"), flags2str(node->flags));
     634  				val->val_type = AWK_UNDEFINED;
     635  				break;
     636  			}
     637  			break;
     638  
     639  		case AWK_STRING:
     640  			(void) force_string(node);
     641  			assign_string(node, val, AWK_STRING);
     642  			ret = awk_true;
     643  			break;
     644  
     645  		case AWK_REGEX:
     646  			switch (fixtype(node)->flags & (STRING|NUMBER|USER_INPUT|REGEX|BOOLVAL)) {
     647  			case STRING:
     648  				val->val_type = AWK_STRING;
     649  				break;
     650  			case NUMBER|BOOLVAL:
     651  				val->val_type = AWK_BOOL;
     652  				break;
     653  			case NUMBER:
     654  				val->val_type = AWK_NUMBER;
     655  				break;
     656  			case NUMBER|USER_INPUT:
     657  				val->val_type = AWK_STRNUM;
     658  				break;
     659  			case REGEX:
     660  				assign_regex(node, val);
     661  				ret = awk_true;
     662  				break;
     663  			case NUMBER|STRING:
     664  				if (node == Nnull_string) {
     665  					val->val_type = AWK_UNDEFINED;
     666  					break;
     667  				}
     668  				/* fall through */
     669  			default:
     670  				warning(_("node_to_awk_value detected invalid flags combination `%s'; please file a bug report"), flags2str(node->flags));
     671  				val->val_type = AWK_UNDEFINED;
     672  				break;
     673  			}
     674  			break;
     675  
     676  		case AWK_SCALAR:
     677  			switch (fixtype(node)->flags & (STRING|NUMBER|USER_INPUT|REGEX|BOOLVAL)) {
     678  			case NUMBER|BOOLVAL:
     679  				val->val_type = AWK_BOOL;
     680  				break;
     681  			case STRING:
     682  				val->val_type = AWK_STRING;
     683  				break;
     684  			case NUMBER:
     685  				val->val_type = AWK_NUMBER;
     686  				break;
     687  			case NUMBER|USER_INPUT:
     688  				val->val_type = AWK_STRNUM;
     689  				break;
     690  			case REGEX:
     691  				val->val_type = AWK_REGEX;
     692  				break;
     693  			case NUMBER|STRING:
     694  				if (node == Nnull_string) {
     695  					val->val_type = AWK_UNDEFINED;
     696  					break;
     697  				}
     698  				/* fall through */
     699  			default:
     700  				warning(_("node_to_awk_value detected invalid flags combination `%s'; please file a bug report"), flags2str(node->flags));
     701  				val->val_type = AWK_UNDEFINED;
     702  				break;
     703  			}
     704  			break;
     705  
     706  		case AWK_UNDEFINED:
     707  			/* return true and actual type for request of undefined */
     708  			switch (fixtype(node)->flags & (STRING|NUMBER|USER_INPUT|REGEX|BOOLVAL)) {
     709  			case NUMBER|BOOLVAL:
     710  				assign_bool(node, val);
     711  				ret = awk_true;
     712  				break;
     713  			case STRING:
     714  				assign_string(node, val, AWK_STRING);
     715  				ret = awk_true;
     716  				break;
     717  			case NUMBER:
     718  				assign_number(node, val);
     719  				ret = awk_true;
     720  				break;
     721  			case NUMBER|USER_INPUT:
     722  				assign_string(node, val, AWK_STRNUM);
     723  				ret = awk_true;
     724  				break;
     725  			case REGEX:
     726  				assign_regex(node, val);
     727  				ret = awk_true;
     728  				break;
     729  			case NUMBER|STRING:
     730  				if (node == Nnull_string) {
     731  					val->val_type = AWK_UNDEFINED;
     732  					ret = awk_true;
     733  					break;
     734  				}
     735  				/* fall through */
     736  			default:
     737  				warning(_("node_to_awk_value detected invalid flags combination `%s'; please file a bug report"), flags2str(node->flags));
     738  				val->val_type = AWK_UNDEFINED;
     739  				break;
     740  			}
     741  			break;
     742  
     743  		case AWK_ARRAY:
     744  		case AWK_VALUE_COOKIE:
     745  			break;
     746  		}
     747  		break;
     748  
     749  	case Node_var_array:
     750  		val->val_type = AWK_ARRAY;
     751  		if (wanted == AWK_ARRAY || wanted == AWK_UNDEFINED) {
     752  			val->array_cookie = node;
     753  			ret = awk_true;
     754  		} else
     755  			ret = awk_false;
     756  		break;
     757  
     758  	default:
     759  		val->val_type = AWK_UNDEFINED;
     760  		ret = awk_false;
     761  		break;
     762  	}
     763  
     764  	return ret;
     765  }
     766  
     767  /*
     768   * Symbol table access:
     769   * 	- No access to special variables (NF, etc.)
     770   * 	- One special exception: PROCINFO.
     771   *	- Use sym_update() to change a value, including from UNDEFINED
     772   *	  to scalar or array.
     773   */
     774  /*
     775   * Lookup a variable, fills in value. No messing with the value
     776   * returned. Returns false if the variable doesn't exist
     777   * or the wrong type was requested.
     778   * In the latter case, fills in vaule->val_type with the real type.
     779   * Built-in variables (except PROCINFO) may not be accessed by an extension.
     780   */
     781  
     782  /* api_sym_lookup --- look up a symbol */
     783  
     784  static awk_bool_t
     785  api_sym_lookup(awk_ext_id_t id,
     786  		const char *name_space,
     787  		const char *name,
     788  		awk_valtype_t wanted,
     789  		awk_value_t *result)
     790  {
     791  	NODE *node;
     792  
     793  	update_global_values();		/* make sure stuff like NF, NR, are up to date */
     794  
     795  	if (   name == NULL
     796  	    || *name == '\0'
     797  	    || result == NULL
     798  	    || ! is_valid_identifier(name)
     799  	    || name_space == NULL
     800  	    || (name_space[0] != '\0' && ! is_valid_identifier(name_space)))
     801  		return awk_false;
     802  
     803  	if ((node = ns_lookup(name_space, name, NULL)) == NULL)
     804  		return awk_false;
     805  
     806  	if (is_off_limits_var(name))	/* a built-in variable */
     807  		node->flags |= NO_EXT_SET;
     808  
     809  	return node_to_awk_value(node, result, wanted);
     810  }
     811  
     812  /* api_sym_lookup_scalar --- retrieve the current value of a scalar */
     813  
     814  static awk_bool_t
     815  api_sym_lookup_scalar(awk_ext_id_t id,
     816  			awk_scalar_t cookie,
     817  			awk_valtype_t wanted,
     818  			awk_value_t *result)
     819  {
     820  	NODE *node = (NODE *) cookie;
     821  
     822  	if (node == NULL
     823  	    || result == NULL
     824  	    || node->type != Node_var)
     825  		return awk_false;
     826  
     827  	update_global_values();	/* make sure stuff like NF, NR, are up to date */
     828  
     829  	return node_to_awk_value(node, result, wanted);
     830  }
     831  
     832  /* api_sym_update --- update a symbol's value, see gawkapi.h for semantics */
     833  
     834  static awk_bool_t
     835  api_sym_update(awk_ext_id_t id,
     836  		const char *name_space,
     837  		const char *name,
     838  		awk_value_t *value)
     839  {
     840  	NODE *node;
     841  	NODE *array_node;
     842  
     843  	if (   name == NULL
     844  	    || *name == '\0'
     845  	    || value == NULL
     846  	    || ! is_valid_identifier(name)
     847  	    || name_space == NULL
     848  	    || (name_space[0] != '\0' && ! is_valid_identifier(name_space)))
     849  		return awk_false;
     850  
     851  	switch (value->val_type) {
     852  	case AWK_NUMBER:
     853  	case AWK_STRNUM:
     854  	case AWK_STRING:
     855  	case AWK_REGEX:
     856  	case AWK_UNDEFINED:
     857  	case AWK_ARRAY:
     858  	case AWK_SCALAR:
     859  	case AWK_VALUE_COOKIE:
     860  		break;
     861  
     862  	default:
     863  		/* fatal(_("api_sym_update: invalid value for type of new value (%d)"), value->val_type); */
     864  		return awk_false;
     865  	}
     866  
     867  	char *full_name = NULL;
     868  	node = ns_lookup(name_space, name, & full_name);
     869  
     870  	if (node == NULL) {
     871  		/* new value to be installed */
     872  		if (value->val_type == AWK_ARRAY) {
     873  			array_node = awk_value_to_node(value);
     874  			node = install_symbol(full_name, Node_var_array);
     875  			array_node->vname = node->vname;
     876  			*node = *array_node;
     877  			freenode(array_node);
     878  			value->array_cookie = node;	/* pass new cookie back to extension */
     879  		} else {
     880  			/* regular variable */
     881  			node = install_symbol(full_name, Node_var);
     882  			node->var_value = awk_value_to_node(value);
     883  		}
     884  
     885  		return awk_true;
     886  	}
     887  
     888  	/*
     889  	 * If we get here, then it exists already.  Any valid type is
     890  	 * OK except for AWK_ARRAY (unless it is in Node_var_new undefined
     891  	 * state, in which case an array is OK).
     892  	 */
     893  	if (   (node->flags & NO_EXT_SET) != 0
     894  	    || is_off_limits_var(full_name)) {	/* most built-in vars not allowed */
     895  		node->flags |= NO_EXT_SET;
     896  		efree((void *) full_name);
     897  		return awk_false;
     898  	}
     899  
     900  	efree((void *) full_name);
     901  
     902  	if (value->val_type == AWK_ARRAY) {
     903  		if (node->type == Node_var_new) {
     904  			/* special gymnastics to convert untyped to an array */
     905  			array_node = awk_value_to_node(value);
     906  			array_node->vname = node->vname;
     907  			unref(node->var_value);
     908  			*node = *array_node;
     909  			freenode(array_node);
     910  			value->array_cookie = node;	/* pass new cookie back to extension */
     911  			return awk_true;
     912  		}
     913  	} else if (node->type == Node_var
     914  		   || node->type == Node_var_new
     915  		   || node->type == Node_elem_new) {
     916  		unref(node->var_value);
     917  		node->var_value = awk_value_to_node(value);
     918  		if ((node->type == Node_var_new || node->type == Node_elem_new)
     919  		    && value->val_type != AWK_UNDEFINED)
     920  			node->type = Node_var;
     921  
     922  		return awk_true;
     923  	}
     924  
     925  	return awk_false;
     926  }
     927  
     928  /* api_sym_update_scalar --- update a scalar cookie */
     929  
     930  static awk_bool_t
     931  api_sym_update_scalar(awk_ext_id_t id,
     932  			awk_scalar_t cookie,
     933  			awk_value_t *value)
     934  {
     935  	NODE *node = (NODE *) cookie;
     936  
     937  	if (value == NULL
     938  	    || node == NULL
     939  	    || node->type != Node_var
     940  	    || (node->flags & NO_EXT_SET) != 0)
     941  		return awk_false;
     942  
     943  	/*
     944  	 * Optimization: if valref is 1, and the new value is a string or
     945  	 * a number, we can avoid calling unref and then making a new node
     946  	 * by simply installing the new value.  First, we follow the same
     947  	 * recipe used by node.c:r_unref to wipe the current values, and then
     948  	 * we copy the logic from r_make_number or make_str_node to install
     949  	 * the new value.
     950  	 */
     951  	switch (value->val_type) {
     952  	case AWK_NUMBER:
     953  		if (node->var_value->valref == 1 && ! do_mpfr) {
     954  			NODE *r = node->var_value;
     955  
     956  			/* r_unref: */
     957  			if ((r->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR))
     958  				efree(r->stptr);
     959  			free_wstr(r);
     960  
     961  			/* r_make_number: */
     962  			r->numbr = value->num_value;
     963  			r->flags = MALLOC|NUMBER|NUMCUR;
     964  			r->stptr = NULL;
     965  			r->stlen = 0;
     966  			return awk_true;
     967  		}
     968  		break;
     969  
     970  	case AWK_STRING:
     971  	case AWK_STRNUM:
     972  		if (node->var_value->valref == 1) {
     973  			NODE *r = node->var_value;
     974  
     975  			/* r_unref: */
     976  			if ((r->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR))
     977  				efree(r->stptr);
     978  
     979  			mpfr_unset(r);
     980  			free_wstr(r);
     981  
     982  			/* make_str_node(s, l, ALREADY_MALLOCED): */
     983  			r->numbr = 0;
     984  			r->flags = (MALLOC|STRING|STRCUR);
     985  			if (value->val_type == AWK_STRNUM)
     986  				r->flags |= USER_INPUT;
     987  			r->stfmt = STFMT_UNUSED;
     988  			r->stptr = value->str_value.str;
     989  			r->stlen = value->str_value.len;
     990  #ifdef HAVE_MPFR
     991  			r->strndmode = MPFR_round_mode;
     992  #endif
     993  			return awk_true;
     994  		}
     995  		break;
     996  
     997  	case AWK_REGEX:
     998  	case AWK_UNDEFINED:
     999  	case AWK_SCALAR:
    1000  	case AWK_VALUE_COOKIE:
    1001  		break;
    1002  
    1003  
    1004  	default:	/* AWK_ARRAY or invalid type */
    1005  		return awk_false;
    1006  	}
    1007  
    1008  	/* do it the hard (slow) way */
    1009  	unref(node->var_value);
    1010  	node->var_value = awk_value_to_node(value);
    1011  	return awk_true;
    1012  }
    1013  
    1014  /*
    1015   * valid_subscript_type --- test if a type is allowed for an array subscript.
    1016   *
    1017   * Any scalar value is fine, so only AWK_ARRAY (or an invalid type) is illegal.
    1018   */
    1019  
    1020  static inline bool
    1021  valid_subscript_type(awk_valtype_t valtype)
    1022  {
    1023  	switch (valtype) {
    1024  	case AWK_UNDEFINED:
    1025  	case AWK_NUMBER:
    1026  	case AWK_STRNUM:
    1027  	case AWK_STRING:
    1028  	case AWK_REGEX:
    1029  	case AWK_SCALAR:
    1030  	case AWK_VALUE_COOKIE:
    1031  		return true;
    1032  	default:	/* AWK_ARRAY or an invalid type */
    1033  		return false;
    1034  	}
    1035  }
    1036  
    1037  /* Array management */
    1038  /*
    1039   * api_get_array_element --- teturn the value of an element - read only!
    1040   *
    1041   * Use set_array_element to change it.
    1042   */
    1043  
    1044  static awk_bool_t
    1045  api_get_array_element(awk_ext_id_t id,
    1046  		awk_array_t a_cookie,
    1047  		const awk_value_t *const index,
    1048  		awk_valtype_t wanted,
    1049  		awk_value_t *result)
    1050  {
    1051  	NODE *array = (NODE *) a_cookie;
    1052  	NODE *subscript;
    1053  	NODE **aptr;
    1054  
    1055  	/* don't check for index len zero, null str is ok as index */
    1056  	if (   array == NULL
    1057  	    || array->type != Node_var_array
    1058  	    || result == NULL
    1059  	    || index == NULL
    1060  	    || ! valid_subscript_type(index->val_type))
    1061  		return awk_false;
    1062  
    1063  	subscript = awk_value_to_node(index);
    1064  
    1065  	/* if it doesn't exist, return false */
    1066  	if (in_array(array, subscript) == NULL) {
    1067  		unref(subscript);
    1068  		return awk_false;
    1069  	}
    1070  
    1071  	aptr = assoc_lookup(array, subscript);
    1072  
    1073  	if (aptr == NULL) {	/* can't happen */
    1074  		unref(subscript);
    1075  		return awk_false;
    1076  	}
    1077  
    1078  	unref(subscript);
    1079  
    1080  	return node_to_awk_value(*aptr, result, wanted);
    1081  }
    1082  
    1083  /*
    1084   * api_set_array_element --- change (or create) element in existing array
    1085   *	with element->index and element->value.
    1086   */
    1087  
    1088  static awk_bool_t
    1089  api_set_array_element(awk_ext_id_t id, awk_array_t a_cookie,
    1090  					const awk_value_t *const index,
    1091  					const awk_value_t *const value)
    1092  {
    1093  	NODE *array = (NODE *)a_cookie;
    1094  	NODE *tmp;
    1095  	NODE *elem;
    1096  
    1097  	/* don't check for index len zero, null str is ok as index */
    1098  	if (   array == NULL
    1099  	    || array->type != Node_var_array
    1100  	    || (array->flags & NO_EXT_SET) != 0
    1101  	    || index == NULL
    1102  	    || value == NULL
    1103  	    || ! valid_subscript_type(index->val_type))
    1104  		return awk_false;
    1105  
    1106  	tmp = awk_value_to_node(index);
    1107  	elem = awk_value_to_node(value);
    1108  	if (elem->type == Node_var_array) {
    1109  		elem->parent_array = array;
    1110  		elem->vname = estrdup(index->str_value.str,
    1111  					index->str_value.len);
    1112  	}
    1113  	assoc_set(array, tmp, elem);
    1114  
    1115  	return awk_true;
    1116  }
    1117  
    1118  /*
    1119   * remove_element --- remove an array element
    1120   *		common code used by multiple functions
    1121   */
    1122  
    1123  static void
    1124  remove_element(NODE *array, NODE *subscript)
    1125  {
    1126  	NODE *val;
    1127  
    1128  	if (array == NULL)
    1129  		fatal(_("remove_element: received null array"));
    1130  
    1131  	if (subscript == NULL)
    1132  		fatal(_("remove_element: received null subscript"));
    1133  
    1134  	val = in_array(array, subscript);
    1135  
    1136  	if (val == NULL)
    1137  		return;
    1138  
    1139  	if (val->type == Node_var_array) {
    1140  		assoc_clear(val);
    1141  		/* cleared a sub-array, free Node_var_array */
    1142  		efree(val->vname);
    1143  		freenode(val);
    1144  	} else
    1145  		unref(val);
    1146  
    1147  	(void) assoc_remove(array, subscript);
    1148  }
    1149  
    1150  /*
    1151   * api_del_array_element --- remove the element with the given index.
    1152   *	Return success if removed or if element did not exist.
    1153   */
    1154  
    1155  static awk_bool_t
    1156  api_del_array_element(awk_ext_id_t id,
    1157  		awk_array_t a_cookie, const awk_value_t* const index)
    1158  {
    1159  	NODE *array, *sub;
    1160  
    1161  	array = (NODE *) a_cookie;
    1162  	if (   array == NULL
    1163  	    || array->type != Node_var_array
    1164  	    || (array->flags & NO_EXT_SET) != 0
    1165  	    || index == NULL
    1166  	    || ! valid_subscript_type(index->val_type))
    1167  		return awk_false;
    1168  
    1169  	sub = awk_value_to_node(index);
    1170  	remove_element(array, sub);
    1171  	unref(sub);
    1172  
    1173  	return awk_true;
    1174  }
    1175  
    1176  /*
    1177   * api_get_element_count --- retrieve total number of elements in array.
    1178   *	Return false if some kind of error.
    1179   */
    1180  
    1181  static awk_bool_t
    1182  api_get_element_count(awk_ext_id_t id,
    1183  		awk_array_t a_cookie, size_t *count)
    1184  {
    1185  	NODE *node = (NODE *) a_cookie;
    1186  
    1187  	if (count == NULL || node == NULL || node->type != Node_var_array)
    1188  		return awk_false;
    1189  
    1190  	*count = node->table_size;
    1191  	return awk_true;
    1192  }
    1193  
    1194  /* api_create_array --- create a new array cookie to which elements may be added */
    1195  
    1196  static awk_array_t
    1197  api_create_array(awk_ext_id_t id)
    1198  {
    1199  	NODE *n;
    1200  
    1201  	getnode(n);
    1202  	memset(n, 0, sizeof(NODE));
    1203  	null_array(n);
    1204  
    1205  	return (awk_array_t) n;
    1206  }
    1207  
    1208  /* api_clear_array --- clear out an array */
    1209  
    1210  static awk_bool_t
    1211  api_clear_array(awk_ext_id_t id, awk_array_t a_cookie)
    1212  {
    1213  	NODE *node = (NODE *) a_cookie;
    1214  
    1215  	if (   node == NULL
    1216  	    || node->type != Node_var_array
    1217  	    || (node->flags & NO_EXT_SET) != 0)
    1218  		return awk_false;
    1219  
    1220  	assoc_clear(node);
    1221  	return awk_true;
    1222  }
    1223  
    1224  /* api_destroy_array --- destroy an array */
    1225  
    1226  static awk_bool_t
    1227  api_destroy_array(awk_ext_id_t id, awk_array_t a_cookie)
    1228  {
    1229  	if (! api_clear_array(id, a_cookie))
    1230  		return awk_false;
    1231  	freenode((NODE *) a_cookie);
    1232  	return awk_true;
    1233  }
    1234  
    1235  /* api_flatten_array_typed --- flatten out an array so that it can be looped over easily. */
    1236  
    1237  static awk_bool_t
    1238  api_flatten_array_typed(awk_ext_id_t id,
    1239  		awk_array_t a_cookie,
    1240  		awk_flat_array_t **data,
    1241  		awk_valtype_t index_type, awk_valtype_t value_type)
    1242  {
    1243  	NODE **list;
    1244  	size_t i, j;
    1245  	NODE *array = (NODE *) a_cookie;
    1246  	size_t alloc_size;
    1247  
    1248  	if (   array == NULL
    1249  	    || array->type != Node_var_array
    1250  	    || assoc_empty(array)
    1251  	    || data == NULL)
    1252  		return awk_false;
    1253  
    1254  	alloc_size = sizeof(awk_flat_array_t) +
    1255  			(array->table_size - 1) * sizeof(awk_element_t);
    1256  
    1257  	ezalloc(*data, awk_flat_array_t *, alloc_size,
    1258  			"api_flatten_array_typed");
    1259  
    1260  	list = assoc_list(array, "@unsorted", ASORTI);
    1261  
    1262  	(*data)->opaque1 = array;
    1263  	(*data)->opaque2 = list;
    1264  	(*data)->count = array->table_size;
    1265  
    1266  	for (i = j = 0; i < 2 * array->table_size; i += 2, j++) {
    1267  		NODE *index, *value;
    1268  
    1269  		index = list[i];
    1270  		value = list[i + 1]; /* number or string or subarray */
    1271  
    1272  		/* Convert index and value to API types. */
    1273  		if (! node_to_awk_value(index,
    1274  				& (*data)->elements[j].index, index_type)) {
    1275  			fatal(_("api_flatten_array_typed: could not convert index %d to %s"),
    1276  						(int) i, valtype2str(index_type));
    1277  		}
    1278  		if (! node_to_awk_value(value,
    1279  				& (*data)->elements[j].value, value_type)) {
    1280  			fatal(_("api_flatten_array_typed: could not convert value %d to %s"),
    1281  						(int) i, valtype2str(value_type));
    1282  		}
    1283  	}
    1284  	return awk_true;
    1285  }
    1286  
    1287  /*
    1288   * api_release_flattened_array --- release array memory,
    1289   *	delete any marked elements. Count must match what
    1290   *	gawk thinks the size is.
    1291   */
    1292  
    1293  static awk_bool_t
    1294  api_release_flattened_array(awk_ext_id_t id,
    1295  		awk_array_t a_cookie,
    1296  		awk_flat_array_t *data)
    1297  {
    1298  	NODE *array = (NODE *) a_cookie;
    1299  	NODE **list;
    1300  	size_t i, j, k;
    1301  
    1302  	if (   array == NULL
    1303  	    || array->type != Node_var_array
    1304  	    || data == NULL
    1305  	    || array != (NODE *) data->opaque1
    1306  	    || data->count != array->table_size
    1307  	    || data->opaque2 == NULL)
    1308  		return awk_false;
    1309  
    1310  	list = (NODE **) data->opaque2;
    1311  
    1312  	/* free index nodes */
    1313  	for (i = j = 0, k = 2 * array->table_size; i < k; i += 2, j++) {
    1314  		/* Delete items flagged for delete. */
    1315  		if (   (data->elements[j].flags & AWK_ELEMENT_DELETE) != 0
    1316  		    && (array->flags & NO_EXT_SET) == 0) {
    1317  			remove_element(array, list[i]);
    1318  		}
    1319  		unref(list[i]);
    1320  	}
    1321  
    1322  	efree(list);
    1323  	efree(data);
    1324  
    1325  	return awk_true;
    1326  }
    1327  
    1328  /* api_create_value --- create a cached value */
    1329  
    1330  static awk_bool_t
    1331  api_create_value(awk_ext_id_t id, awk_value_t *value,
    1332  		awk_value_cookie_t *result)
    1333  {
    1334  	if (value == NULL || result == NULL)
    1335  		return awk_false;
    1336  
    1337  	switch (value->val_type) {
    1338  	case AWK_NUMBER:
    1339  	case AWK_STRNUM:
    1340  	case AWK_STRING:
    1341  	case AWK_REGEX:
    1342  		break;
    1343  	default:
    1344  		/* reject anything other than a simple scalar */
    1345  		return awk_false;
    1346  	}
    1347  
    1348  	return (awk_bool_t) ((*result = awk_value_to_node(value)) != NULL);
    1349  }
    1350  
    1351  /* api_release_value --- release a cached value */
    1352  
    1353  static awk_bool_t
    1354  api_release_value(awk_ext_id_t id, awk_value_cookie_t value)
    1355  {
    1356  	NODE *val = (NODE *) value;
    1357  
    1358  	if (val == NULL)
    1359  		return awk_false;
    1360  
    1361  	unref(val);
    1362  	return awk_true;
    1363  }
    1364  
    1365  /* api_get_mpfr --- allocate an mpfr_ptr */
    1366  
    1367  static void *
    1368  api_get_mpfr(awk_ext_id_t id)
    1369  {
    1370  #ifdef HAVE_MPFR
    1371  	mpfr_ptr p;
    1372  	emalloc(p, mpfr_ptr, sizeof(mpfr_t), "api_get_mpfr");
    1373  	mpfr_init(p);
    1374  	return p;
    1375  #else
    1376  	fatal(_("api_get_mpfr: MPFR not supported"));
    1377  	return NULL;	// silence compiler warning
    1378  #endif
    1379  }
    1380  
    1381  /* api_get_mpz --- allocate an mpz_ptr */
    1382  
    1383  static void *
    1384  api_get_mpz(awk_ext_id_t id)
    1385  {
    1386  #ifdef HAVE_MPFR
    1387  	mpz_ptr p;
    1388  	emalloc(p, mpz_ptr, sizeof (mpz_t), "api_get_mpz");
    1389  
    1390  	mpz_init(p);
    1391  	return p;
    1392  #else
    1393  	fatal(_("api_get_mpfr: MPFR not supported"));
    1394  	return NULL;	// silence compiler warning
    1395  #endif
    1396  }
    1397  
    1398  /* api_get_file --- return a handle to an existing or newly opened file */
    1399  
    1400  static awk_bool_t
    1401  api_get_file(awk_ext_id_t id, const char *name, size_t namelen, const char *filetype,
    1402  		int fd, const awk_input_buf_t **ibufp, const awk_output_buf_t **obufp)
    1403  {
    1404  	const struct redirect *f;
    1405  	int flag;	/* not used, sigh */
    1406  	enum redirval redirtype;
    1407  
    1408  	if (name == NULL || namelen == 0) {
    1409  		if (curfile == NULL) {
    1410  			INSTRUCTION *pc;
    1411  			int save_rule;
    1412  			char *save_source;
    1413  
    1414  			if (nextfile(& curfile, false) <= 0)
    1415  				return awk_false;
    1416  
    1417  			pc = main_beginfile;
    1418  			/* save execution state */
    1419  			save_rule = currule;
    1420  			save_source = source;
    1421  
    1422  			for (;;) {
    1423  				if (pc == NULL)
    1424  					fatal(_("cannot find end of BEGINFILE rule"));
    1425  				if (pc->opcode == Op_after_beginfile)
    1426  					break;
    1427  				pc = pc->nexti;
    1428  			}
    1429  			pc->opcode = Op_stop;
    1430  		        (void) (*interpret)(main_beginfile);
    1431  			pc->opcode = Op_after_beginfile;
    1432  			after_beginfile(& curfile);
    1433  			/* restore execution state */
    1434  			currule = save_rule;
    1435  			source = save_source;
    1436  		}
    1437  		*ibufp = &curfile->public;
    1438  		*obufp = NULL;
    1439  
    1440  		return awk_true;
    1441  	}
    1442  
    1443  	redirtype = redirect_none;
    1444  	switch (filetype[0]) {
    1445  	case '<':
    1446  		if (filetype[1] == '\0')
    1447  			redirtype = redirect_input;
    1448  		break;
    1449  	case '>':
    1450  		switch (filetype[1]) {
    1451  		case '\0':
    1452  			redirtype = redirect_output;
    1453  			break;
    1454  		case '>':
    1455  			if (filetype[2] == '\0')
    1456  				redirtype = redirect_append;
    1457  			break;
    1458  		}
    1459  		break;
    1460  	case '|':
    1461  		if (filetype[2] == '\0') {
    1462  			switch (filetype[1]) {
    1463  			case '>':
    1464  				redirtype = redirect_pipe;
    1465  				break;
    1466  			case '<':
    1467  				redirtype = redirect_pipein;
    1468  				break;
    1469  			case '&':
    1470  				redirtype = redirect_twoway;
    1471  				break;
    1472  			}
    1473  		}
    1474  		break;
    1475  	}
    1476  
    1477  	if (redirtype == redirect_none) {
    1478  		warning(_("cannot open unrecognized file type `%s' for `%s'"),
    1479  			filetype, name);
    1480  		return awk_false;
    1481  	}
    1482  
    1483  	if ((f = redirect_string(name, namelen, 0, redirtype, &flag, fd, false)) == NULL)
    1484  		return awk_false;
    1485  
    1486  	*ibufp = f->iop ? & f->iop->public : NULL;
    1487  	*obufp = f->output.fp ? & f->output : NULL;
    1488  	return awk_true;
    1489  }
    1490  
    1491  /*
    1492   * Register a version string for this extension with gawk.
    1493   */
    1494  
    1495  struct version_info {
    1496  	const char *version;
    1497  	struct version_info *next;
    1498  };
    1499  
    1500  static struct version_info *vi_head;
    1501  
    1502  /* api_register_ext_version --- add an extension version string to the list */
    1503  
    1504  static void
    1505  api_register_ext_version(awk_ext_id_t id, const char *version)
    1506  {
    1507  	struct version_info *info;
    1508  
    1509  	if (version == NULL)
    1510  		return;
    1511  
    1512  	(void) id;
    1513  
    1514  	emalloc(info, struct version_info *, sizeof(struct version_info), "register_ext_version");
    1515  	info->version = version;
    1516  	info->next = vi_head;
    1517  	vi_head = info;
    1518  }
    1519  
    1520  /* the struct api */
    1521  gawk_api_t api_impl = {
    1522  	/* data */
    1523  	GAWK_API_MAJOR_VERSION,	/* major and minor versions */
    1524  	GAWK_API_MINOR_VERSION,
    1525  
    1526  #ifdef HAVE_MPFR
    1527  	__GNU_MP_VERSION,
    1528  	__GNU_MP_VERSION_MINOR,
    1529  	MPFR_VERSION_MAJOR,
    1530  	MPFR_VERSION_MINOR,
    1531  #else
    1532  	0, 0, 0, 0,
    1533  #endif
    1534  
    1535  	{ 0 },			/* do_flags */
    1536  
    1537  	/* registration functions */
    1538  	api_add_ext_func,
    1539  	api_register_input_parser,
    1540  	api_register_output_wrapper,
    1541  	api_register_two_way_processor,
    1542  	api_awk_atexit,
    1543  	api_register_ext_version,
    1544  
    1545  	/* message printing functions */
    1546  	api_fatal,
    1547  	api_warning,
    1548  	api_lintwarn,
    1549  	api_nonfatal,
    1550  
    1551  	/* updating ERRNO */
    1552  	api_update_ERRNO_int,
    1553  	api_update_ERRNO_string,
    1554  	api_unset_ERRNO,
    1555  
    1556  	/* Function arguments */
    1557  	api_get_argument,
    1558  	api_set_argument,
    1559  
    1560  	/* Accessing and installing variables and constants */
    1561  	api_sym_lookup,
    1562  	api_sym_update,
    1563  
    1564  	/* Accessing and modifying variables via scalar cookies */
    1565  	api_sym_lookup_scalar,
    1566  	api_sym_update_scalar,
    1567  
    1568  	/* Cached values */
    1569  	api_create_value,
    1570  	api_release_value,
    1571  
    1572  	/* Array management */
    1573  	api_get_element_count,
    1574  	api_get_array_element,
    1575  	api_set_array_element,
    1576  	api_del_array_element,
    1577  	api_create_array,
    1578  	api_clear_array,
    1579  	api_flatten_array_typed,
    1580  	api_release_flattened_array,
    1581  
    1582  	/* Memory allocation */
    1583  	malloc,
    1584  	calloc,
    1585  	realloc,
    1586  	free,
    1587  	api_get_mpfr,
    1588  	api_get_mpz,
    1589  
    1590  	/* Find/open a file */
    1591  	api_get_file,
    1592  
    1593  	/* Additional array hook to destroy an array */
    1594  	api_destroy_array,
    1595  };
    1596  
    1597  /* init_ext_api --- init the extension API */
    1598  
    1599  void
    1600  init_ext_api()
    1601  {
    1602  	/* force values to 1 / 0 */
    1603  	api_impl.do_flags[0] = (do_lint ? 1 : 0);
    1604  	api_impl.do_flags[1] = (do_traditional ? 1 : 0);
    1605  	api_impl.do_flags[2] = (do_profile ? 1 : 0);
    1606  	api_impl.do_flags[3] = (do_sandbox ? 1 : 0);
    1607  	api_impl.do_flags[4] = (do_debug ? 1 : 0);
    1608  	api_impl.do_flags[5] = (do_mpfr ? 1 : 0);
    1609  }
    1610  
    1611  /* update_ext_api --- update the variables in the API that can change */
    1612  
    1613  void
    1614  update_ext_api()
    1615  {
    1616  	api_impl.do_flags[0] = (do_lint ? 1 : 0);
    1617  }
    1618  
    1619  /* print_ext_versions --- print the list */
    1620  
    1621  extern void
    1622  print_ext_versions(void)
    1623  {
    1624  	struct version_info *p;
    1625  
    1626  	for (p = vi_head; p != NULL; p = p->next)
    1627  		printf("%s\n", p->version);
    1628  }
    1629  
    1630  /* valtype2str --- return a printable representation of a value type */
    1631  
    1632  static const char *
    1633  valtype2str(awk_valtype_t type)
    1634  {
    1635  	static char buf[100];
    1636  
    1637  	// Important: keep in same order as in gawkapi.h!
    1638  	static const char *values[] = {
    1639  		"AWK_UNDEFINED",
    1640  		"AWK_NUMBER",
    1641  		"AWK_STRING",
    1642  		"AWK_REGEX",
    1643  		"AWK_STRNUM",
    1644  		"AWK_ARRAY",
    1645  		"AWK_SCALAR",
    1646  		"AWK_VALUE_COOKIE",
    1647  	};
    1648  
    1649  	if (AWK_UNDEFINED <= type && type <= AWK_VALUE_COOKIE)
    1650  		return values[(int) type];
    1651  
    1652  	sprintf(buf, "unknown type! (%d)", (int) type);
    1653  
    1654  	return buf;
    1655  }
    1656  
    1657  /* ns_lookup --- correctly build name before looking it up */
    1658  
    1659  static NODE *
    1660  ns_lookup(const char *name_space, const char *name, char **fullname)
    1661  {
    1662  	assert(name_space != NULL);
    1663  	assert(name != NULL);
    1664  
    1665  	if (name_space[0] == '\0' || strcmp(name_space, awk_namespace) == 0) {
    1666  		if (fullname != NULL)
    1667  			*fullname = estrdup(name, strlen(name));
    1668  		return lookup(name);
    1669  	}
    1670  
    1671  	size_t len = strlen(name_space) + 2 + strlen(name) + 1;
    1672  	char *buf;
    1673  	emalloc(buf, char *, len, "ns_lookup");
    1674  	sprintf(buf, "%s::%s", name_space, name);
    1675  
    1676  	NODE *f = lookup(buf);
    1677  	if (fullname != NULL)
    1678  		*fullname = buf;
    1679  	else
    1680  		efree((void *) buf);
    1681  
    1682  	return f;
    1683  }