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