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