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, "")