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