(root)/
gawk-5.2.2/
extension/
testext.c
       1  /*
       2   * testext.c - tests for the extension API.
       3   */
       4  
       5  /*
       6   * Copyright (C) 2012, 2013, 2014, 2015, 2017, 2018, 2021, 2022,
       7   * the Free Software Foundation, Inc.
       8   *
       9   * This file is part of GAWK, the GNU implementation of the
      10   * AWK Programming Language.
      11   *
      12   * GAWK is free software; you can redistribute it and/or modify
      13   * it under the terms of the GNU General Public License as published by
      14   * the Free Software Foundation; either version 3 of the License, or
      15   * (at your option) any later version.
      16   *
      17   * GAWK is distributed in the hope that it will be useful,
      18   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      19   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      20   * GNU General Public License for more details.
      21   *
      22   * You should have received a copy of the GNU General Public License
      23   * along with this program; if not, write to the Free Software
      24   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
      25   */
      26  
      27  #ifdef HAVE_CONFIG_H
      28  #include <config.h>
      29  #endif
      30  
      31  #include <stdio.h>
      32  #include <assert.h>
      33  #include <errno.h>
      34  #include <stdlib.h>
      35  #include <string.h>
      36  #include <unistd.h>
      37  
      38  #include <sys/types.h>
      39  #include <sys/stat.h>
      40  #include <fcntl.h>
      41  
      42  #ifdef HAVE_MPFR
      43  #include <gmp.h>
      44  #include <mpfr.h>
      45  #endif
      46  
      47  #include "gawkapi.h"
      48  
      49  static const gawk_api_t *api;	/* for convenience macros to work */
      50  static awk_ext_id_t ext_id;
      51  static const char *ext_version = "testext extension: version 1.0";
      52  
      53  int plugin_is_GPL_compatible;
      54  
      55  static void fill_in_array(awk_value_t *value);
      56  static int populate_array(awk_array_t);
      57  
      58  #ifdef __MINGW32__
      59  unsigned int
      60  getuid (void)
      61  {
      62    /* See pc/getid.c.  */
      63    return 0;
      64  }
      65  #endif
      66  
      67  /* valrep2str --- turn a value into a string */
      68  
      69  static const char *
      70  valrep2str(const awk_value_t *value)
      71  {
      72  	static char buf[BUFSIZ];
      73  	int size = BUFSIZ - 3;
      74  
      75  	switch (value->val_type) {
      76  	case AWK_UNDEFINED:
      77  		strcpy(buf, "<undefined>");
      78  		break;
      79  	case AWK_ARRAY:
      80  		strcpy(buf, "<array>");
      81  		break;
      82  	case AWK_SCALAR:
      83  		strcpy(buf, "<scalar>");
      84  		break;
      85  	case AWK_VALUE_COOKIE:
      86  		strcpy(buf, "<value-cookie>");
      87  		break;
      88  	case AWK_REGEX:
      89  	case AWK_STRNUM:
      90  	case AWK_STRING:
      91  		if (value->str_value.len < size)
      92  			size = value->str_value.len;
      93  		sprintf(buf, "\"%.*s\"",
      94  				size,
      95  				value->str_value.str);
      96  		break;
      97  	case AWK_BOOL:
      98  		if (value->str_value.len + 8 < size)
      99  			size = value->str_value.len;
     100  		sprintf(buf, "<bool>: %.*s",
     101  				size,
     102  				value->str_value.str);
     103  		break;
     104  	case AWK_NUMBER:
     105  		sprintf(buf, "%g", value->num_value);
     106  		break;
     107  	}
     108  	return buf;
     109  }
     110  
     111  /*
     112   * The awk code for these tests is embedded in this file and then extracted
     113   * dynamically to create the script that is run together with this extension.
     114   * Extraction requires the format for awk code where test code is enclosed
     115   * in a BEGIN block, with the BEGIN and close brace on lines by themselves
     116   * and at the front of the lines.
     117   */
     118  
     119  /*
     120  @load "testext"
     121  BEGIN {
     122  	n = split("blacky rusty sophie raincloud lucky", pets)
     123  	printf("pets has %d elements\n", length(pets))
     124  	ret = dump_array_and_delete("pets", "3")
     125  	printf("dump_array_and_delete(pets) returned %d\n", ret)
     126  	if ("3" in pets)
     127  		printf("dump_array_and_delete() did NOT remove index \"3\"!\n")
     128  	else
     129  		printf("dump_array_and_delete() did remove index \"3\"!\n")
     130  	print ""
     131  }
     132  */
     133  static awk_value_t *
     134  dump_array_and_delete(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     135  {
     136  	awk_value_t value, value2, value3;
     137  	awk_flat_array_t *flat_array;
     138  	size_t count;
     139  	char *name;
     140  	int i;
     141  
     142  	assert(result != NULL);
     143  	make_number(0.0, result);
     144  
     145  	if (nargs != 2) {
     146  		printf("dump_array_and_delete: nargs not right (%d should be 2)\n", nargs);
     147  		goto out;
     148  	}
     149  
     150  	/* get argument named array as flat array and print it */
     151  	if (get_argument(0, AWK_STRING, & value)) {
     152  		name = value.str_value.str;
     153  		if (sym_lookup(name, AWK_ARRAY, & value2))
     154  			printf("dump_array_and_delete: sym_lookup of %s passed\n", name);
     155  		else {
     156  			printf("dump_array_and_delete: sym_lookup of %s failed\n", name);
     157  			goto out;
     158  		}
     159  	} else {
     160  		printf("dump_array_and_delete: get_argument(0) failed\n");
     161  		goto out;
     162  	}
     163  
     164  	if (! get_element_count(value2.array_cookie, & count)) {
     165  		printf("dump_array_and_delete: get_element_count failed\n");
     166  		goto out;
     167  	}
     168  
     169  	printf("dump_array_and_delete: incoming size is %lu\n", (unsigned long) count);
     170  
     171  	if (! flatten_array(value2.array_cookie, & flat_array)) {
     172  		printf("dump_array_and_delete: could not flatten array\n");
     173  		goto out;
     174  	}
     175  
     176  	if (flat_array->count != count) {
     177  		printf("dump_array_and_delete: flat_array->count (%lu) != count (%lu)\n",
     178  				(unsigned long) flat_array->count,
     179  				(unsigned long) count);
     180  		goto out;
     181  	}
     182  
     183  	if (! get_argument(1, AWK_STRING, & value3)) {
     184  		printf("dump_array_and_delete: get_argument(1) failed\n");
     185  		goto out;
     186  	}
     187  
     188  	for (i = 0; i < flat_array->count; i++) {
     189  		printf("\t%s[\"%.*s\"] = %s\n",
     190  			name,
     191  			(int) flat_array->elements[i].index.str_value.len,
     192  			flat_array->elements[i].index.str_value.str,
     193  			valrep2str(& flat_array->elements[i].value));
     194  
     195  		if (strcmp(value3.str_value.str, flat_array->elements[i].index.str_value.str) == 0) {
     196  			flat_array->elements[i].flags |= AWK_ELEMENT_DELETE;
     197  			printf("dump_array_and_delete: marking element \"%s\" for deletion\n",
     198  				flat_array->elements[i].index.str_value.str);
     199  		}
     200  	}
     201  
     202  	if (! release_flattened_array(value2.array_cookie, flat_array)) {
     203  		printf("dump_array_and_delete: could not release flattened array\n");
     204  		goto out;
     205  	}
     206  
     207  	make_number(1.0, result);
     208  out:
     209  	return result;
     210  }
     211  
     212  /*
     213  BEGIN {
     214  	ENVIRON["testext"]++
     215  	try_modify_environ()
     216  	if ("testext" in ENVIRON)
     217  		print "try_del_environ() could not delete element - pass"
     218  	else
     219  		print "try_del_environ() deleted element! - fail"
     220  	if ("testext2" in ENVIRON)
     221  		print "try_del_environ() added an element - fail"
     222  	else
     223  		print "try_del_environ() could not add an element - pass"
     224  }
     225  */
     226  
     227  static awk_value_t *
     228  try_modify_environ(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     229  {
     230  	awk_value_t value, index, newvalue;
     231  	awk_flat_array_t *flat_array;
     232  	awk_array_t environ_array;
     233  	size_t count;
     234  	int i;
     235  
     236  	assert(result != NULL);
     237  	make_number(0.0, result);
     238  
     239  	if (nargs != 0) {
     240  		printf("try_modify_environ: nargs not right (%d should be 0)\n", nargs);
     241  		goto out;
     242  	}
     243  
     244  	/* get ENVIRON array */
     245  	if (sym_lookup("ENVIRON", AWK_ARRAY, & value))
     246  		printf("try_modify_environ: sym_lookup of ENVIRON passed\n");
     247  	else {
     248  		printf("try_modify_environ: sym_lookup of ENVIRON failed\n");
     249  		goto out;
     250  	}
     251  
     252  	environ_array = value.array_cookie;
     253  	if (! get_element_count(environ_array, & count)) {
     254  		printf("try_modify_environ: get_element_count failed\n");
     255  		goto out;
     256  	}
     257  
     258  	/* setting an array element should fail */
     259  	(void) make_const_string("testext2", 8, & index);
     260  	(void) make_const_string("a value", 7, & value);
     261  	if (set_array_element(environ_array, & index, & newvalue)) {
     262  		printf("try_modify_environ: set_array_element of ENVIRON passed\n");
     263  	} else {
     264  		printf("try_modify_environ: set_array_element of ENVIRON failed\n");
     265  		gawk_free(index.str_value.str);
     266  		gawk_free(value.str_value.str);
     267  	}
     268  
     269  	if (! flatten_array(environ_array, & flat_array)) {
     270  		printf("try_modify_environ: could not flatten array\n");
     271  		goto out;
     272  	}
     273  
     274  	if (flat_array->count != count) {
     275  		printf("try_modify_environ: flat_array->count (%lu) != count (%lu)\n",
     276  				(unsigned long) flat_array->count,
     277  				(unsigned long) count);
     278  		goto out;
     279  	}
     280  
     281  	for (i = 0; i < flat_array->count; i++) {
     282  		/* don't print */
     283  	/*
     284  		printf("\t%s[\"%.*s\"] = %s\n",
     285  			name,
     286  			(int) flat_array->elements[i].index.str_value.len,
     287  			flat_array->elements[i].index.str_value.str,
     288  			valrep2str(& flat_array->elements[i].value));
     289  	*/
     290  		if (strcmp("testext", flat_array->elements[i].index.str_value.str) == 0) {
     291  			flat_array->elements[i].flags |= AWK_ELEMENT_DELETE;
     292  			printf("try_modify_environ: marking element \"%s\" for deletion\n",
     293  				flat_array->elements[i].index.str_value.str);
     294  		}
     295  	}
     296  
     297  	if (! release_flattened_array(environ_array, flat_array)) {
     298  		printf("try_modify_environ: could not release flattened array\n");
     299  		goto out;
     300  	}
     301  
     302  	make_number(1.0, result);
     303  out:
     304  	return result;
     305  }
     306  
     307  /*
     308  BEGIN {
     309  	testvar = "One Adam Twelve"
     310  	ret = var_test("testvar")
     311  	printf("var_test() returned %d, test_var = %s\n", ret, testvar)
     312  	print ""
     313  }
     314  */
     315  
     316  static awk_value_t *
     317  var_test(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     318  {
     319  	awk_value_t value, value2;
     320  	awk_value_t *valp;
     321  
     322  	assert(result != NULL);
     323  	make_number(0.0, result);
     324  
     325  	if (nargs != 1) {
     326  		printf("var_test: nargs not right (%d should be 1)\n", nargs);
     327  		goto out;
     328  	}
     329  
     330  	/* look up PROCINFO - should succeed */
     331  	if (sym_lookup("PROCINFO", AWK_ARRAY, & value))
     332  		printf("var_test: sym_lookup of PROCINFO passed - got a value!\n");
     333  	else
     334  		printf("var_test: sym_lookup of PROCINFO failed - did not get a value\n");
     335  
     336  	/* look up a reserved variable - should pass */
     337  	if (sym_lookup("ARGC", AWK_NUMBER, & value))
     338  		printf("var_test: sym_lookup of ARGC passed - got a value!\n");
     339  	else
     340  		printf("var_test: sym_lookup of ARGC failed - did not get a value\n");
     341  
     342  	/* now try to set it - should fail */
     343  	value.num_value++;
     344  	if (sym_update("ARGC", & value))
     345  		printf("var_test: sym_update of ARGC passed and should not have!\n");
     346  	else
     347  		printf("var_test: sym_update of ARGC failed - correctly\n");
     348  
     349  	/* look up variable whose name is passed in, should pass */
     350  	if (get_argument(0, AWK_STRING, & value)) {
     351  		if (sym_lookup(value.str_value.str, AWK_STRING, & value2)) {
     352  			/* change the value, should be reflected in awk script */
     353  			valp = make_number(42.0, & value2);
     354  
     355  			if (sym_update(value.str_value.str, valp)) {
     356  				printf("var_test: sym_update(\"%s\") succeeded\n", value.str_value.str);
     357  			} else {
     358  				printf("var_test: sym_update(\"%s\") failed\n", value.str_value.str);
     359  				goto out;
     360  			}
     361  		} else {
     362  			printf("var_test: sym_lookup(\"%s\") failed\n", value.str_value.str);
     363  			goto out;
     364  		}
     365  	} else {
     366  		printf("var_test: get_argument() failed\n");
     367  		goto out;
     368  	}
     369  
     370  	make_number(1.0, result);
     371  out:
     372  	return result;
     373  }
     374  
     375  /*
     376  BEGIN {
     377  	ERRNO = ""
     378  	ret = test_errno()
     379  	printf("test_errno() returned %d, ERRNO = %s\n", ret, ERRNO)
     380  	print ""
     381  }
     382  */
     383  static awk_value_t *
     384  test_errno(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     385  {
     386  	assert(result != NULL);
     387  	make_number(0.0, result);
     388  
     389  	if (nargs != 0) {
     390  		printf("test_errno: nargs not right (%d should be 0)\n", nargs);
     391  		goto out;
     392  	}
     393  
     394  	update_ERRNO_int(ECHILD);
     395  
     396  	make_number(1.0, result);
     397  out:
     398  	return result;
     399  }
     400  
     401  /*
     402   * 3/2015: This test is no longer strictly necessary,
     403   * since PROCINFO is no longer a deferred variable.
     404   * But we leave it in for safety, anyway.
     405   */
     406  /*
     407  BEGIN {
     408  	print "test_deferred returns", test_deferred()
     409  	print ""
     410  }
     411  */
     412  static awk_value_t *
     413  test_deferred(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     414  {
     415  	awk_value_t arr;
     416  	awk_value_t index, value;
     417  	const struct nval {
     418  		const char *name;
     419  		double val;
     420  	} seed[] = {
     421  		{ "fubar", 9.0, },
     422  		{ "rumpus", -5.0, },
     423  	};
     424  	struct nval sysval[] = {
     425  		{ "uid", getuid(), },
     426  		{ "api_major", GAWK_API_MAJOR_VERSION, },
     427  	};
     428  	size_t i;
     429  
     430  	assert(result != NULL);
     431  	make_number(0.0, result);
     432  
     433  	if (nargs != 0) {
     434  		printf("test_deferred: nargs not right (%d should be 0)\n", nargs);
     435  		goto out;
     436  	}
     437  
     438  	if (! sym_lookup("PROCINFO", AWK_ARRAY, & arr)) {
     439  		printf("test_deferred: %d: sym_lookup failed\n", __LINE__);
     440  		goto out;
     441  	}
     442  
     443  	for (i = 0; i < sizeof(seed)/sizeof(seed[0]); i++) {
     444  		make_const_string(seed[i].name, strlen(seed[i].name), & index);
     445  		make_number(seed[i].val, & value);
     446  		if (! set_array_element(arr.array_cookie, & index, & value)) {
     447  			printf("test_deferred: %d: set_array_element(%s) failed\n", __LINE__, seed[i].name);
     448  			goto out;
     449  		}
     450  	}
     451  
     452  	/* test that it still contains the values we loaded */
     453  	for (i = 0; i < sizeof(seed)/sizeof(seed[0]); i++) {
     454  		make_const_string(seed[i].name, strlen(seed[i].name), & index);
     455  		make_null_string(& value);
     456  		if (! get_array_element(arr.array_cookie, &index, AWK_NUMBER, & value)) {
     457  			printf("test_deferred: %d: get_array_element(%s) failed\n", __LINE__, seed[i].name);
     458  			goto out;
     459  		}
     460  		printf("%s = %g\n", seed[i].name, value.num_value);
     461  	}
     462  
     463  	/* check a few automatically-supplied values */
     464  	for (i = 0; i < sizeof(sysval)/sizeof(sysval[0]); i++) {
     465  		make_const_string(sysval[i].name, strlen(sysval[i].name), & index);
     466  		make_null_string(& value);
     467  		if (! get_array_element(arr.array_cookie, &index, AWK_NUMBER, & value)) {
     468  			printf("test_deferred: %d: get_array_element(%s) failed\n", __LINE__, sysval[i].name);
     469  			goto out;
     470  		}
     471  		printf("%s matches %d\n", sysval[i].name, (value.num_value == sysval[i].val));
     472  	}
     473  
     474  	make_number(1.0, result);
     475  out:
     476  	return result;
     477  }
     478  
     479  /*
     480  BEGIN {
     481  	for (i = 1; i <= 10; i++)
     482  		test_array[i] = i + 2
     483  
     484  	printf("length of test_array is %d, should be 10\n", length(test_array))
     485  	ret = test_array_size(test_array);
     486  	printf("test_array_size() returned %d, length is now %d\n", ret, length(test_array))
     487  	print ""
     488  }
     489  */
     490  
     491  static awk_value_t *
     492  test_array_size(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     493  {
     494  	awk_value_t value;
     495  	size_t count = 0;
     496  
     497  	assert(result != NULL);
     498  	make_number(0.0, result);
     499  
     500  	if (nargs != 1) {
     501  		printf("test_array_size: nargs not right (%d should be 1)\n", nargs);
     502  		goto out;
     503  	}
     504  
     505  	/* get element count and print it; should match length(array) from awk script */
     506  	if (! get_argument(0, AWK_ARRAY, & value)) {
     507  		printf("test_array_size: get_argument failed\n");
     508  		goto out;
     509  	}
     510  
     511  	if (! get_element_count(value.array_cookie, & count)) {
     512  		printf("test_array_size: get_element_count failed\n");
     513  		goto out;
     514  	}
     515  
     516  	printf("test_array_size: incoming size is %lu\n", (unsigned long) count);
     517  
     518  	/* clear array - length(array) should then go to zero in script */
     519  	if (! clear_array(value.array_cookie)) {
     520  		printf("test_array_size: clear_array failed\n");
     521  		goto out;
     522  	}
     523  
     524  	make_number(1.0, result);
     525  
     526  out:
     527  	return result;
     528  }
     529  
     530  /*
     531  BEGIN {
     532  	n = split("one two three four five six", test_array2)
     533  	ret = test_array_elem(test_array2, "3")
     534  	printf("test_array_elem() returned %d, test_array2[3] = %g\n", ret, test_array2[3])
     535  	if ("5" in test_array2)
     536  		printf("error: test_array_elem() did not remove element \"5\"\n")
     537  	else
     538  		printf("test_array_elem() did remove element \"5\"\n")
     539  	if ("7" in test_array2)
     540  		printf("test_array_elem() added element \"7\" --> %s\n", test_array2[7])
     541  	else
     542  		printf("test_array_elem() did not add element \"7\"\n")
     543  	if ("subarray" in test_array2) {
     544  		if (isarray(test_array2["subarray"])) {
     545  			for (i in test_array2["subarray"])
     546  				printf("test_array2[\"subarray\"][\"%s\"] = %s\n",
     547  					i, test_array2["subarray"][i])
     548  		} else
     549  			printf("test_array_elem() added element \"subarray\" as scalar\n")
     550  	} else
     551  		printf("test_array_elem() did not add element \"subarray\"\n")
     552  	print ""
     553  }
     554  */
     555  static awk_value_t *
     556  test_array_elem(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     557  {
     558  	awk_value_t array, index, index2, value;
     559  
     560  	make_number(0.0, result);	/* default return until full success */
     561  
     562  	assert(result != NULL);
     563  
     564  	if (nargs != 2) {
     565  		printf("test_array_elem: nargs not right (%d should be 2)\n", nargs);
     566  		goto out;
     567  	}
     568  
     569  	/* look up an array element and print the value */
     570  	if (! get_argument(0, AWK_ARRAY, & array)) {
     571  		printf("test_array_elem: get_argument 0 (array) failed\n");
     572  		goto out;
     573  	}
     574  	if (! get_argument(1, AWK_STRING, & index)) {
     575  		printf("test_array_elem: get_argument 1 (index) failed\n");
     576  		goto out;
     577  	}
     578  	(void) make_const_string(index.str_value.str, index.str_value.len, & index2);
     579  	if (! get_array_element(array.array_cookie, & index2, AWK_UNDEFINED, & value)) {
     580  		printf("test_array_elem: get_array_element failed\n");
     581  		goto out;
     582  	}
     583  	printf("test_array_elem: a[\"%.*s\"] = %s\n",
     584  			(int) index.str_value.len,
     585  			index.str_value.str,
     586  			valrep2str(& value));
     587  
     588  	/* change the element - "3" */
     589  	(void) make_number(42.0, & value);
     590  	(void) make_const_string(index.str_value.str, index.str_value.len, & index2);
     591  	if (! set_array_element(array.array_cookie, & index2, & value)) {
     592  		printf("test_array_elem: set_array_element failed\n");
     593  		goto out;
     594  	}
     595  
     596  	/* delete another element - "5" */
     597  	(void) make_const_string("5", 1, & index);
     598  	if (! del_array_element(array.array_cookie, & index)) {
     599  		printf("test_array_elem: del_array_element failed\n");
     600  		goto out;
     601  	}
     602  
     603  	/* add a new element - "7" */
     604  	(void) make_const_string("7", 1, & index);
     605  	(void) make_const_string("seven", 5, & value);
     606  	if (! set_array_element(array.array_cookie, & index, & value)) {
     607  		printf("test_array_elem: set_array_element failed\n");
     608  		goto out;
     609  	}
     610  
     611  	/* add a subarray */
     612  	(void) make_const_string("subarray", 8, & index);
     613  	fill_in_array(& value);
     614  	if (! set_array_element(array.array_cookie, & index, & value)) {
     615  		printf("test_array_elem: set_array_element (subarray) failed\n");
     616  		goto out;
     617  	}
     618  
     619  	/* change and deletion should be reflected in awk script */
     620  	make_number(1.0, result);
     621  out:
     622  	return result;
     623  }
     624  
     625  /*
     626  BEGIN {
     627  	ret = test_array_param(a_new_array)
     628  	printf("test_array_param() returned %d\n", ret)
     629  	printf("isarray(a_new_array) = %d\n", isarray(a_new_array))
     630  	if (isarray(a_new_array))
     631  		for (i in a_new_array)
     632  			printf("a_new_array[\"%s\"] = %s\n",
     633  				i, a_new_array[i])
     634  
     635  	a_scalar = 42
     636  	ret = test_array_param(a_scalar)
     637  	printf("test_array_param() returned %d\n", ret)
     638  	printf("isarray(a_scalar) = %d\n", isarray(a_scalar))
     639  	print ""
     640  }
     641  */
     642  
     643  static awk_value_t *
     644  test_array_param(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     645  {
     646  	awk_value_t new_array = { 0 };	// init to zero, silences warnings
     647  	awk_value_t arg0;
     648  
     649  	(void) nargs;		/* silence warnings */
     650  	make_number(0.0, result);
     651  
     652  	if (! get_argument(0, AWK_UNDEFINED, & arg0)) {
     653  		printf("test_array_param: could not get argument\n");
     654  		goto out;
     655  	}
     656  
     657  	if (arg0.val_type != AWK_UNDEFINED) {
     658  		printf("test_array_param: argument is not undefined (%d)\n",
     659  				arg0.val_type);
     660  		goto out;
     661  	}
     662  
     663  	fill_in_array(& new_array);
     664  	if (! set_argument(0, new_array.array_cookie)) {
     665  		printf("test_array_param: could not change type of argument\n");
     666  		goto out;
     667  	}
     668  
     669  	make_number(1.0, result);
     670  out:
     671  	return result;	/* for now */
     672  }
     673  
     674  /*
     675  function tfunc(f) {
     676  	if (isarray(f)) {
     677  		print "good: we have an array"
     678  		print "hello element value inside function is", f["hello"]
     679  	}
     680  }
     681  
     682  BEGIN {
     683  	printf "test_array_create returned %d\n", test_array_create("testarr")
     684  	tfunc(testarr)
     685  	print "hello element global scope is", testarr["hello"]
     686  }
     687  */
     688  
     689  static awk_value_t *
     690  test_array_create(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     691  {
     692  	awk_value_t new_array;
     693  	awk_value_t arg0;
     694  
     695  	(void) nargs;		/* silence warnings */
     696  	make_number(0.0, result);
     697  
     698  	if (! get_argument(0, AWK_STRING, & arg0)) {
     699  		printf("test_array_create: could not get argument\n");
     700  		goto out;
     701  	}
     702  
     703  	if (arg0.val_type != AWK_STRING) {
     704  		printf("test_array_create: argument is not string (%d)\n",
     705  				arg0.val_type);
     706  		goto out;
     707  	}
     708  
     709  	new_array.val_type = AWK_ARRAY;
     710  	new_array.array_cookie = create_array();
     711  	if (! sym_update(arg0.str_value.str, & new_array)) {
     712  		printf("test_array_create: sym_update(\"%s\") failed!\n", arg0.str_value.str);
     713  		goto out;
     714  	}
     715  	if (populate_array(new_array.array_cookie) < 0) {
     716  		printf("test_array_create: populate(\"%s\") failed!\n", arg0.str_value.str);
     717  		goto out;
     718  	}
     719  
     720  	make_number(1.0, result);
     721  out:
     722  	return result;
     723  }
     724  
     725  /*
     726  BEGIN {
     727  	printf("Initial value of LINT is %d\n", LINT)
     728  	ret = print_do_lint();
     729  	printf("print_do_lint() returned %d\n", ret)
     730  	LINT = ! LINT
     731  	printf("Changed value of LINT is %d\n", LINT)
     732  	ret = print_do_lint();
     733  	printf("print_do_lint() returned %d\n", ret)
     734  	print ""
     735  }
     736  */
     737  static awk_value_t *
     738  print_do_lint(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     739  {
     740  	assert(result != NULL);
     741  	make_number(0.0, result);
     742  
     743  	if (nargs != 0) {
     744  		printf("print_do_lint: nargs not right (%d should be 0)\n", nargs);
     745  		goto out;
     746  	}
     747  
     748  	printf("print_do_lint: lint = %d\n", do_lint);
     749  
     750  	make_number(1.0, result);
     751  
     752  out:
     753  	return result;
     754  }
     755  
     756  /*
     757  BEGIN {
     758  	n = split("1 3 5 7 9 11", nums)
     759  	m = split("the quick brown fox jumps over the lazy dog", strings)
     760  	for (i in nums) {
     761  		ret = test_scalar(nums[i] + 0)
     762  		printf("test_scalar(%d) returned %d, the_scalar is %d\n", nums[i], ret, the_scalar)
     763  	}
     764  	for (i in strings) {
     765  		ret = test_scalar(strings[i])
     766  		printf("test_scalar(%s) returned %d, the_scalar is %s\n", strings[i], ret, the_scalar)
     767  	}
     768  }
     769  */
     770  
     771  /* test_scalar --- test scalar cookie */
     772  
     773  static awk_value_t *
     774  test_scalar(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     775  {
     776  	awk_value_t new_value, new_value2;
     777  	awk_value_t the_scalar;
     778  #ifdef HAVE_MPFR
     779  	mpz_t mpz_val;
     780  	mpfr_t mpfr_val;
     781  #endif
     782  
     783  	(void) nargs;		/* silence warnings */
     784  	make_number(0.0, result);
     785  
     786  	if (! sym_lookup("the_scalar", AWK_SCALAR, & the_scalar)) {
     787  		printf("test_scalar: could not get scalar cookie\n");
     788  		goto out;
     789  	}
     790  
     791  	if (! get_argument(0, AWK_UNDEFINED, & new_value)) {
     792  		printf("test_scalar: could not get argument\n");
     793  		goto out;
     794  	} else if (new_value.val_type != AWK_STRING && new_value.val_type != AWK_NUMBER) {
     795  		printf("test_scalar: argument is not a scalar\n");
     796  		goto out;
     797  	}
     798  
     799  	if (new_value.val_type == AWK_STRING) {
     800  		make_const_string(new_value.str_value.str, new_value.str_value.len, & new_value2);
     801  	} else {	/* AWK_NUMBER */
     802  #ifdef HAVE_MPFR
     803  		switch (new_value.num_type) {
     804  		case AWK_NUMBER_TYPE_MPZ:
     805  			mpz_init(mpz_val);
     806  			mpz_set(mpz_val, new_value.num_ptr);
     807  			make_number_mpz(mpz_val, & new_value2);
     808  			break;
     809  		case AWK_NUMBER_TYPE_MPFR:
     810  			mpfr_init(mpfr_val);
     811  			mpfr_set(mpfr_val, (mpfr_ptr) new_value.num_ptr, mpfr_get_default_rounding_mode());
     812  			make_number_mpfr(mpfr_val, & new_value2);
     813  			break;
     814  		default:
     815  			new_value2 = new_value;
     816  			break;
     817  		}
     818  #else
     819  		new_value2 = new_value;
     820  #endif
     821  	}
     822  
     823  	if (! sym_update_scalar(the_scalar.scalar_cookie, & new_value2)) {
     824  		printf("test_scalar: could not update new_value2!\n");
     825  		goto out;
     826  	}
     827  
     828  	make_number(1.0, result);
     829  
     830  out:
     831  	return result;
     832  }
     833  
     834  /*
     835  BEGIN {
     836  	test_scalar_reserved()
     837  }
     838  */
     839  
     840  /* test_scalar_reserved --- test scalar cookie on special variable */
     841  
     842  static awk_value_t *
     843  test_scalar_reserved(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     844  {
     845  	awk_value_t new_value;
     846  	awk_value_t the_scalar;
     847  
     848  	(void) nargs;		/* silence warnings */
     849  	make_number(0.0, result);
     850  
     851  	/* look up a reserved variable - should pass */
     852  	if (sym_lookup("ARGC", AWK_SCALAR, & the_scalar)) {
     853  		printf("test_scalar_reserved: sym_lookup of ARGC passed - got a value!\n");
     854  	} else {
     855  		printf("test_scalar_reserved: sym_lookup of ARGC failed - did not get a value\n");
     856  		goto out;
     857  	}
     858  
     859  	/* updating it should fail */
     860  	make_number(42.0, & new_value);
     861  	if (! sym_update_scalar(the_scalar.scalar_cookie, & new_value)) {
     862  		printf("test_scalar_reserved: could not update new_value2 for ARGC - pass\n");
     863  	} else {
     864  		printf("test_scalar_reserved: was able to update new_value2 for ARGC - fail\n");
     865  		goto out;
     866  	}
     867  
     868  	make_number(1.0, result);
     869  
     870  out:
     871  	return result;
     872  }
     873  
     874  /*
     875  BEGIN {
     876  	print "line 1" > "testexttmp.txt"
     877  	print "line 2" > "testexttmp.txt"
     878  	print "line 3" > "testexttmp.txt"
     879  	close("testexttmp.txt")
     880  	ARGV[1] = "testexttmp.txt"
     881  	ARGC = 2
     882  	getline
     883  	getline
     884  	getline		# now NR should be 3
     885  #	system("rm testexttmp.txt")
     886  	ret = test_indirect_vars()	# should get correct value of NR
     887  	printf("test_indirect_var() return %d\n", ret)
     888  	delete ARGV[1]
     889  	print ""
     890  }
     891  */
     892  
     893  /* test_indirect_vars --- test that access to NR, NF, get correct vales */
     894  
     895  static awk_value_t *
     896  test_indirect_vars(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     897  {
     898  	awk_value_t value;
     899  	char *name = "NR";
     900  
     901  	(void) nargs;		/* silence warnings */
     902  	assert(result != NULL);
     903  	make_number(0.0, result);
     904  
     905  	/* system("rm testexttmp.txt") */
     906  	(void) unlink("testexttmp.txt");
     907  
     908  	if (sym_lookup(name, AWK_NUMBER, & value))
     909  		printf("test_indirect_var: sym_lookup of %s passed\n", name);
     910  	else {
     911  		printf("test_indirect_var: sym_lookup of %s failed\n", name);
     912  		goto out;
     913  	}
     914  
     915  	printf("test_indirect_var: value of NR is %g\n", value.num_value);
     916  
     917  	make_number(1.0, result);
     918  out:
     919  	return result;
     920  }
     921  
     922  /*
     923  BEGIN {
     924  	outfile = "testexttmp.txt"
     925  	alias = ".test.alias"
     926  	print "line 1" > outfile
     927  	print "line 2" > outfile
     928  	print "line 3" > outfile
     929  	close(outfile)
     930  	ret = test_get_file(outfile, alias)
     931  	printf "test_get_file returned %d\n", ret
     932  	nr = 0
     933  	while ((getline < alias) > 0)
     934  		printf "File [%s] nr [%s]: %s\n", alias, ++nr, $0
     935  	close(alias)
     936  #	system("rm " outfile)
     937  	print ""
     938  }
     939  */
     940  
     941  /* test_get_file --- test that we can create a file */
     942  
     943  static awk_value_t *
     944  test_get_file(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     945  {
     946  	awk_value_t filename, alias;
     947  	int fd;
     948  	const awk_input_buf_t *ibuf;
     949  	const awk_output_buf_t *obuf;
     950  
     951  	if (nargs != 2) {
     952  		printf("%s: nargs not right (%d should be 2)\n", "test_get_file", nargs);
     953  		return make_number(-1.0, result);
     954  	}
     955  
     956  	if (! get_argument(0, AWK_STRING, & filename)) {
     957  		printf("%s: cannot get first arg\n", "test_get_file");
     958  		return make_number(-1.0, result);
     959  	}
     960  	if (! get_argument(1, AWK_STRING, & alias)) {
     961  		printf("%s: cannot get second arg\n", "test_get_file");
     962  		return make_number(-1.0, result);
     963  	}
     964  	if ((fd = open(filename.str_value.str, O_RDONLY)) < 0) {
     965  		printf("%s: open(%s) failed\n", "test_get_file", filename.str_value.str);
     966  		return make_number(-1.0, result);
     967  	}
     968  	if (! get_file(alias.str_value.str, strlen(alias.str_value.str), "<", fd, &ibuf, &obuf)) {
     969  		printf("%s: get_file(%s) failed\n", "test_get_file", alias.str_value.str);
     970  		return make_number(-1.0, result);
     971  	}
     972  	if (! ibuf || ibuf->fd != fd) {
     973  		printf("%s: get_file(%s) returned fd %d instead of %d\n", "test_get_file", alias.str_value.str, ibuf ? ibuf->fd : -1, fd);
     974  		return make_number(-1.0, result);
     975  	}
     976  	return make_number(0.0, result);
     977  }
     978  
     979  /* do_get_file --- provide access to get_file API */
     980  
     981  static awk_value_t *
     982  do_get_file(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     983  {
     984  	awk_value_t filename, filetype, fd, res;
     985  	const awk_input_buf_t *ibuf;
     986  	const awk_output_buf_t *obuf;
     987  
     988  	if (nargs != 4) {
     989  		printf("%s: nargs not right (%d should be 4)\n", "get_file", nargs);
     990  		return make_number(-1.0, result);
     991  	}
     992  
     993  	if (! get_argument(0, AWK_STRING, & filename)) {
     994  		printf("%s: cannot get first arg\n", "get_file");
     995  		return make_number(-1.0, result);
     996  	}
     997  	if (! get_argument(1, AWK_STRING, & filetype)) {
     998  		printf("%s: cannot get second arg\n", "get_file");
     999  		return make_number(-1.0, result);
    1000  	}
    1001  	if (! get_argument(2, AWK_NUMBER, & fd)) {
    1002  		printf("%s: cannot get third arg\n", "get_file");
    1003  		return make_number(-1.0, result);
    1004  	}
    1005  	if (! get_argument(3, AWK_ARRAY, & res)) {
    1006  		printf("%s: cannot get fourth arg\n", "get_file");
    1007  		return make_number(-1.0, result);
    1008  	}
    1009  	clear_array(res.array_cookie);
    1010  
    1011  	if (! get_file(filename.str_value.str, strlen(filename.str_value.str), filetype.str_value.str, fd.num_value, &ibuf, &obuf)) {
    1012  		printf("%s: get_file(%s, %s, %d) failed\n", "get_file", filename.str_value.str, filetype.str_value.str, (int)(fd.num_value));
    1013  		return make_number(0.0, result);
    1014  	}
    1015  
    1016  	if (ibuf) {
    1017  		awk_value_t idx, val;
    1018  		set_array_element(res.array_cookie,
    1019  				  make_const_string("input", 5, & idx),
    1020  				  make_number(ibuf->fd, & val));
    1021  		if (ibuf->name)
    1022  			set_array_element(res.array_cookie,
    1023  					  make_const_string("input_name", 10, & idx),
    1024  					  make_const_string(ibuf->name, strlen(ibuf->name), & val));
    1025  	}
    1026  	if (obuf) {
    1027  		awk_value_t idx, val;
    1028  		set_array_element(res.array_cookie,
    1029  				  make_const_string("output", 6, & idx),
    1030  				  make_number(obuf->fp ? fileno(obuf->fp) : -1,
    1031  				  	      & val));
    1032  		if (obuf->name)
    1033  			set_array_element(res.array_cookie,
    1034  					  make_const_string("output_name", 11, & idx),
    1035  					  make_const_string(obuf->name, strlen(obuf->name), & val));
    1036  	}
    1037  	return make_number(1.0, result);
    1038  }
    1039  
    1040  /* populate_array --- fill in some array values */
    1041  
    1042  static int
    1043  populate_array(awk_array_t a_cookie)
    1044  {
    1045  	awk_value_t index, value;
    1046  
    1047  	(void) make_const_string("hello", 5, & index);
    1048  	(void) make_const_string("world", 5, & value);
    1049  	if (! set_array_element(a_cookie, & index, & value)) {
    1050  		printf("fill_in_array:%d: set_array_element failed\n", __LINE__);
    1051  		return -1;
    1052  	}
    1053  
    1054  	(void) make_const_string("answer", 6, & index);
    1055  	(void) make_number(42.0, & value);
    1056  	if (! set_array_element(a_cookie, & index, & value)) {
    1057  		printf("fill_in_array:%d: set_array_element failed\n", __LINE__);
    1058  		return -1;
    1059  	}
    1060  	return 0;
    1061  }
    1062  
    1063  /* fill_in_array --- fill in a new array */
    1064  
    1065  static void
    1066  fill_in_array(awk_value_t *new_array)
    1067  {
    1068  	awk_array_t a_cookie;
    1069  
    1070  	a_cookie = create_array();
    1071  
    1072  	if (populate_array(a_cookie) < 0)
    1073  		return;
    1074  
    1075  	new_array->val_type = AWK_ARRAY;
    1076  	new_array->array_cookie = a_cookie;
    1077  }
    1078  
    1079  /* create_new_array --- create a named array */
    1080  
    1081  static void
    1082  create_new_array()
    1083  {
    1084  	awk_value_t value;
    1085  
    1086  	fill_in_array(& value);
    1087  	if (! sym_update("new_array", & value))
    1088  		printf("create_new_array: sym_update(\"new_array\") failed!\n");
    1089  }
    1090  
    1091  /* at_exit0 --- first at_exit program, runs last */
    1092  
    1093  static void at_exit0(void *data, int exit_status)
    1094  {
    1095  	printf("at_exit0 called (should be third):");
    1096  	if (data)
    1097  		printf(" data = %p,", data);
    1098  	else
    1099  		printf(" data = NULL,");
    1100  	printf(" exit_status = %d\n", exit_status);
    1101  }
    1102  
    1103  /* at_exit1 --- second at_exit program, runs second */
    1104  
    1105  static int data_for_1 = 0xDeadBeef;
    1106  static void at_exit1(void *data, int exit_status)
    1107  {
    1108  	int *data_p = (int *) data;
    1109  
    1110  	printf("at_exit1 called (should be second):");
    1111  	if (data) {
    1112  		if (data == & data_for_1)
    1113  			printf(" (data is & data_for_1),");
    1114  		else
    1115  			printf(" (data is NOT & data_for_1),");
    1116  		printf(" data value = %#x,", *data_p);
    1117  	} else
    1118  		printf(" data = NULL,");
    1119  	printf(" exit_status = %d\n", exit_status);
    1120  }
    1121  
    1122  /* at_exit2 --- third at_exit program, runs first */
    1123  
    1124  static void at_exit2(void *data, int exit_status)
    1125  {
    1126  	printf("at_exit2 called (should be first):");
    1127  	if (data)
    1128  		printf(" data = %p,", data);
    1129  	else
    1130  		printf(" data = NULL,");
    1131  	printf(" exit_status = %d\n", exit_status);
    1132  }
    1133  
    1134  /* do_test_function --- test function for test namespace */
    1135  
    1136  static awk_value_t *
    1137  do_test_function(int nargs, awk_value_t *result, struct awk_ext_func *unused)
    1138  {
    1139  	printf("test::test_function() called.\n");
    1140  	fflush(stdout);
    1141  
    1142  	return make_number(0.0, result);
    1143  }
    1144  
    1145  static awk_ext_func_t func_table[] = {
    1146  	{ "dump_array_and_delete", dump_array_and_delete, 2, 2, awk_false, NULL },
    1147  	{ "try_modify_environ", try_modify_environ, 0, 0, awk_false, NULL },
    1148  	{ "var_test", var_test, 1, 1, awk_false, NULL },
    1149  	{ "test_deferred", test_deferred, 0, 0, awk_false, NULL },
    1150  	{ "test_errno", test_errno, 0, 0, awk_false, NULL },
    1151  	{ "test_array_size", test_array_size, 1, 1, awk_false, NULL },
    1152  	{ "test_array_elem", test_array_elem, 2, 2, awk_false, NULL },
    1153  	{ "test_array_param", test_array_param, 1, 1, awk_false, NULL },
    1154  	{ "test_array_create", test_array_create, 1, 1, awk_false, NULL },
    1155  	{ "print_do_lint", print_do_lint, 0, 0, awk_false, NULL },
    1156  	{ "test_scalar", test_scalar, 1, 1, awk_false, NULL },
    1157  	{ "test_scalar_reserved", test_scalar_reserved, 0, 0, awk_false, NULL },
    1158  	{ "test_indirect_vars", test_indirect_vars, 0, 0, awk_false, NULL },
    1159  	{ "test_get_file", test_get_file, 2, 2, awk_false, NULL },
    1160  	{ "get_file", do_get_file, 4, 4, awk_false, NULL },
    1161  };
    1162  
    1163  static awk_ext_func_t ns_test_func = {
    1164  	"test_function", do_test_function, 0, 0, awk_false, NULL
    1165  };
    1166  
    1167  /* init_testext --- additional initialization function */
    1168  
    1169  static awk_bool_t init_testext(void)
    1170  {
    1171  	awk_value_t value;
    1172  	static const char message[] = "hello, world";	/* of course */
    1173  	static const char message2[] = "i am a scalar";
    1174  	static const char message3[] = "in namespace test";
    1175  
    1176  	/* This is used by the getfile test */
    1177  	if (sym_lookup("TESTEXT_QUIET", AWK_NUMBER, & value))
    1178  		return awk_true;
    1179  
    1180  	/* add at_exit functions */
    1181  	awk_atexit(at_exit0, NULL);
    1182  	awk_atexit(at_exit1, & data_for_1);
    1183  	awk_atexit(at_exit2, NULL);
    1184  
    1185  /*
    1186  BEGIN {
    1187  	printf("answer_num = %g\n", answer_num);
    1188  	printf("message_string = %s\n", message_string);
    1189  	for (i in new_array)
    1190  		printf("new_array[\"%s\"] = \"%s\"\n", i, new_array[i])
    1191  	print ""
    1192  	printf("test::testval = %s\n", test::testval)
    1193  	test::test_function()
    1194  	print ""
    1195  }
    1196  */
    1197  
    1198  	/* install some variables */
    1199  	if (! sym_update("answer_num", make_number(42, & value)))
    1200  		printf("testext: sym_update(\"answer_num\") failed!\n");
    1201  
    1202  	if (! sym_update("message_string",
    1203  			make_const_string(message, strlen(message), & value)))
    1204  		printf("testext: sym_update(\"answer_num\") failed!\n");
    1205  
    1206  	if (! sym_update("the_scalar",
    1207  			make_const_string(message2, strlen(message2), & value)))
    1208  		printf("testext: sym_update(\"the_scalar\") failed!\n");
    1209  
    1210  	create_new_array();
    1211  
    1212  	if (! sym_update_ns("test", "testval",
    1213  			make_const_string(message3, strlen(message3), & value)))
    1214  		printf("testext: sym_update_ns(\"test\", \"testval\") failed!\n");
    1215  
    1216  	if (! add_ext_func("test", & ns_test_func))
    1217  		printf("testext: add_ext_func(\"test\", ns_test_func) failed!\n");
    1218  
    1219  	return awk_true;
    1220  }
    1221  
    1222  static awk_bool_t (*init_func)(void) = init_testext;
    1223  
    1224  dl_load_func(func_table, testext, "")