provide /bin/gawk
[platform/upstream/gawk.git] / gawkapi.c
1 /*
2  * gawkapi.c -- Implement the functions defined for gawkapi.h
3  */
4
5 /* 
6  * Copyright (C) 2012-2014 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 static awk_bool_t node_to_awk_value(NODE *node, awk_value_t *result, awk_valtype_t wanted);
29
30 /*
31  * api_get_argument --- get the count'th paramater, zero-based.
32  *
33  * Returns false if count is out of range, or if actual paramater
34  * does not match what is specified in wanted. In the latter
35  * case, fills in result->val_type with the actual type.
36  */
37
38 static awk_bool_t
39 api_get_argument(awk_ext_id_t id, size_t count,
40                         awk_valtype_t wanted, awk_value_t *result)
41 {
42 #ifdef DYNAMIC
43         NODE *arg;
44
45         if (result == NULL)
46                 return awk_false;
47
48         (void) id;
49
50         /* set up default result */
51         memset(result, 0, sizeof(*result));
52         result->val_type = AWK_UNDEFINED;
53
54         /*
55          * Song and dance here.  get_array_argument() and get_scalar_argument()
56          * will force a change in type of a parameter that is Node_var_new.
57          *
58          * Start by looking at the unadulterated argument as it was passed.
59          */
60         arg = get_argument(count);
61         if (arg == NULL)
62                 return awk_false;
63
64         /* if type is undefined */
65         if (arg->type == Node_var_new) {
66                 if (wanted == AWK_UNDEFINED)
67                         return true;
68                 else if (wanted == AWK_ARRAY) {
69                         goto array;
70                 } else {
71                         goto scalar;
72                 }
73         }
74         
75         /* at this point, we have real type */
76         if (arg->type == Node_var_array || arg->type == Node_array_ref) {
77                 if (wanted != AWK_ARRAY && wanted != AWK_UNDEFINED)
78                         return false;
79                 goto array;
80         } else
81                 goto scalar;
82
83 array:
84         /* get the array here */
85         arg = get_array_argument(count, false);
86         if (arg == NULL)
87                 return awk_false;
88
89         return node_to_awk_value(arg, result, wanted);
90
91 scalar:
92         /* at this point we have a real type that is not an array */
93         arg = get_scalar_argument(count, false);
94         if (arg == NULL)
95                 return awk_false;
96
97         return node_to_awk_value(arg, result, wanted);
98 #else
99         return awk_false;
100 #endif
101 }
102
103 /* api_set_argument --- convert an argument to an array */
104
105 static awk_bool_t
106 api_set_argument(awk_ext_id_t id,
107                 size_t count,
108                 awk_array_t new_array)
109 {
110 #ifdef DYNAMIC
111         NODE *arg;
112         NODE *array = (NODE *) new_array;
113
114         (void) id;
115
116         if (array == NULL || array->type != Node_var_array)
117                 return awk_false;
118
119         if (   (arg = get_argument(count)) == NULL
120             || arg->type != Node_var_new)
121                 return awk_false;
122
123         arg = get_array_argument(count, false);
124         if (arg == NULL)
125                 return awk_false;
126
127         array->vname = arg->vname;
128         *arg = *array;
129         freenode(array);
130
131         return awk_true;
132 #else
133         return awk_false;
134 #endif
135 }
136
137 /* awk_value_to_node --- convert a value into a NODE */
138
139 NODE *
140 awk_value_to_node(const awk_value_t *retval)
141 {
142         NODE *ext_ret_val;
143         NODE *v;
144
145         if (retval == NULL)
146                 fatal(_("awk_value_to_node: received null retval"));
147
148         switch (retval->val_type) {
149         case AWK_ARRAY:
150                 ext_ret_val = (NODE *) retval->array_cookie;
151                 break;
152         case AWK_UNDEFINED:
153                 ext_ret_val = dupnode(Nnull_string);
154                 break;
155         case AWK_NUMBER:
156                 ext_ret_val = make_number(retval->num_value);
157                 break;
158         case AWK_STRING:
159                 ext_ret_val = make_str_node(retval->str_value.str,
160                                 retval->str_value.len, ALREADY_MALLOCED);
161                 break;
162         case AWK_SCALAR:
163                 v = (NODE *) retval->scalar_cookie;
164                 if (v->type != Node_var)
165                         ext_ret_val = NULL;
166                 else
167                         ext_ret_val = dupnode(v->var_value);
168                 break;
169         case AWK_VALUE_COOKIE:
170                 ext_ret_val = dupnode((NODE *)(retval->value_cookie));
171                 break;
172         default:        /* any invalid type */
173                 ext_ret_val = NULL;
174                 break;
175         }
176
177         return ext_ret_val;
178 }
179
180 /* Functions to print messages */
181
182 /* api_fatal --- print a fatal message and exit */
183
184 static void
185 api_fatal(awk_ext_id_t id, const char *format, ...)
186 {
187         va_list args;
188
189         (void) id;
190
191         va_start(args, format);
192         err(true, _("fatal: "), format, args);
193         va_end(args);
194 }
195
196 /* api_warning --- print a warning message and exit */
197
198 static void
199 api_warning(awk_ext_id_t id, const char *format, ...)
200 {
201         va_list args;
202
203         (void) id;
204
205         va_start(args, format);
206         err(false, _("warning: "), format, args);
207         va_end(args);
208 }
209
210 /* api_lintwarn --- print a lint warning message and exit if appropriate */
211
212 static void
213 api_lintwarn(awk_ext_id_t id, const char *format, ...)
214 {
215         va_list args;
216
217         (void) id;
218
219         va_start(args, format);
220         if (lintwarn == r_fatal) {
221                 err(true, _("fatal: "), format, args);
222                 va_end(args);
223         } else {
224                 err(false, _("warning: "), format, args);
225                 va_end(args);
226         }
227 }
228
229 /* api_register_input_parser --- register an input_parser; for opening files read-only */
230
231 static void
232 api_register_input_parser(awk_ext_id_t id, awk_input_parser_t *input_parser)
233 {
234         (void) id;
235
236         if (input_parser == NULL)
237                 return;
238
239         register_input_parser(input_parser);
240 }
241
242 /* api_register_output_wrapper --- egister an output wrapper, for writing files / two-way pipes */
243
244 static void api_register_output_wrapper(awk_ext_id_t id,
245                 awk_output_wrapper_t *output_wrapper)
246 {
247         (void) id;
248
249         if (output_wrapper == NULL)
250                 return;
251
252         register_output_wrapper(output_wrapper);
253 }
254
255 /* api_register_two_way_processor --- register a processor for two way I/O */
256
257 static void
258 api_register_two_way_processor(awk_ext_id_t id,
259                 awk_two_way_processor_t *two_way_processor)
260 {
261         (void) id;
262
263         if (two_way_processor == NULL)
264                 return;
265
266         register_two_way_processor(two_way_processor);
267 }
268
269 /* Functions to update ERRNO */
270
271 /* api_update_ERRNO_int --- update ERRNO with an integer value */
272
273 static void
274 api_update_ERRNO_int(awk_ext_id_t id, int errno_val)
275 {
276         (void) id;
277
278         update_ERRNO_int(errno_val);
279 }
280
281 /* api_update_ERRNO_string --- update ERRNO with a string value */
282
283 static void
284 api_update_ERRNO_string(awk_ext_id_t id,
285                         const char *string)
286 {
287         (void) id;
288
289         if (string == NULL)
290                 return;
291
292         update_ERRNO_string(string);
293 }
294
295 /* api_unset_ERRNO --- unset ERRNO */
296
297 static void
298 api_unset_ERRNO(awk_ext_id_t id)
299 {
300         (void) id;
301
302         unset_ERRNO();
303 }
304
305
306 /* api_add_ext_func --- add a function to the interpreter, returns true upon success */
307
308 static awk_bool_t
309 api_add_ext_func(awk_ext_id_t id,
310                 const char *namespace,
311                 const awk_ext_func_t *func)
312 {
313         (void) id;
314         (void) namespace;
315
316         if (func == NULL)
317                 return awk_false;
318
319 #ifdef DYNAMIC
320         return make_builtin(func);
321 #else
322         return awk_false;
323 #endif
324 }
325
326 /* Stuff for exit handler - do it as linked list */
327
328 struct ext_exit_handler {
329         struct ext_exit_handler *next;
330         void (*funcp)(void *data, int exit_status);
331         void *arg0;
332 };
333 static struct ext_exit_handler *list_head = NULL;
334
335 /* run_ext_exit_handlers --- run the extension exit handlers, LIFO order */
336
337 void
338 run_ext_exit_handlers(int exitval)
339 {
340         struct ext_exit_handler *p, *next;
341
342         for (p = list_head; p != NULL; p = next) {
343                 next = p->next;
344                 p->funcp(p->arg0, exitval);
345                 free(p);
346         }
347         list_head = NULL;
348 }
349
350 /* api_awk_atexit --- add an exit call back */
351
352 static void
353 api_awk_atexit(awk_ext_id_t id,
354                 void (*funcp)(void *data, int exit_status),
355                 void *arg0)
356 {
357         struct ext_exit_handler *p;
358
359         (void) id;
360
361         if (funcp == NULL)
362                 return;
363
364         /* allocate memory */
365         emalloc(p, struct ext_exit_handler *, sizeof(struct ext_exit_handler), "api_awk_atexit");
366
367         /* fill it in */
368         p->funcp = funcp;
369         p->arg0 = arg0;
370
371         /* add to linked list, LIFO order */
372         p->next = list_head;
373         list_head = p;
374 }
375
376 /* node_to_awk_value --- convert a node into a value for an extension */
377
378 static awk_bool_t
379 node_to_awk_value(NODE *node, awk_value_t *val, awk_valtype_t wanted)
380 {
381         awk_bool_t ret = awk_false;
382
383         if (node == NULL)
384                 fatal(_("node_to_awk_value: received null node"));
385
386         if (val == NULL)
387                 fatal(_("node_to_awk_value: received null val"));
388
389         switch (node->type) {
390         case Node_var_new:      /* undefined variable */
391                 val->val_type = AWK_UNDEFINED;
392                 if (wanted == AWK_UNDEFINED) {
393                         ret = awk_true;
394                 }
395                 break;
396
397         case Node_var:
398                 /* a scalar value */
399                 if (wanted == AWK_SCALAR) {
400                         val->val_type = AWK_SCALAR;
401                         val->scalar_cookie = (void *) node;
402                         ret = awk_true;
403                         break;
404                 }
405
406                 node = node->var_value;
407                 /* FALL THROUGH */
408         case Node_val:
409                 /* a scalar value */
410                 switch (wanted) {
411                 case AWK_NUMBER:
412                         val->val_type = AWK_NUMBER;
413
414                         (void) force_number(node);
415                         if ((node->flags & NUMCUR) != 0) {
416                                 val->num_value = get_number_d(node);
417                                 ret = awk_true;
418                         }
419                         break;
420
421                 case AWK_STRING:
422                         val->val_type = AWK_STRING;
423
424                         (void) force_string(node);
425                         if ((node->flags & STRCUR) != 0) {
426                                 val->str_value.str = node->stptr;
427                                 val->str_value.len = node->stlen;
428                                 ret = awk_true;
429                         }
430                         break;
431
432                 case AWK_SCALAR:
433                         if ((node->flags & NUMBER) != 0) {
434                                 val->val_type = AWK_NUMBER;
435                         } else if ((node->flags & STRING) != 0) {
436                                 val->val_type = AWK_STRING;
437                         } else
438                                 val->val_type = AWK_UNDEFINED;
439                         ret = awk_false;
440                         break;
441
442                 case AWK_UNDEFINED:
443                         /* return true and actual type for request of undefined */
444                         if ((node->flags & NUMBER) != 0) {
445                                 val->val_type = AWK_NUMBER;
446                                 val->num_value = get_number_d(node);
447                                 ret = awk_true;
448                         } else if ((node->flags & STRING) != 0) {
449                                 val->val_type = AWK_STRING;
450                                 val->str_value.str = node->stptr;
451                                 val->str_value.len = node->stlen;
452                                 ret = awk_true;
453                         } else
454                                 val->val_type = AWK_UNDEFINED;
455                         break;
456
457                 case AWK_ARRAY:
458                 case AWK_VALUE_COOKIE:
459                         break;
460                 }
461                 break;
462
463         case Node_var_array:
464                 val->val_type = AWK_ARRAY;
465                 if (wanted == AWK_ARRAY || wanted == AWK_UNDEFINED) {
466                         val->array_cookie = node;
467                         ret = awk_true;
468                 } else
469                         ret = awk_false;
470                 break;
471
472         default:
473                 val->val_type = AWK_UNDEFINED;
474                 ret = awk_false;
475                 break;
476         }
477
478         return ret;
479 }
480
481 /*
482  * Symbol table access:
483  *      - No access to special variables (NF, etc.)
484  *      - One special exception: PROCINFO.
485  *      - Use sym_update() to change a value, including from UNDEFINED
486  *        to scalar or array. 
487  */
488 /*
489  * Lookup a variable, fills in value. No messing with the value
490  * returned. Returns false if the variable doesn't exist
491  * or the wrong type was requested.
492  * In the latter case, fills in vaule->val_type with the real type.
493  * Built-in variables (except PROCINFO) may not be accessed by an extension.
494  */
495
496 /* api_sym_lookup --- look up a symbol */
497
498 static awk_bool_t
499 api_sym_lookup(awk_ext_id_t id,
500                 const char *name,
501                 awk_valtype_t wanted,
502                 awk_value_t *result)
503 {
504         NODE *node;
505
506         update_global_values();         /* make sure stuff like NF, NR, are up to date */
507
508         if (   name == NULL
509             || *name == '\0'
510             || result == NULL
511             || (node = lookup(name)) == NULL)
512                 return awk_false;
513
514         if (is_off_limits_var(name))    /* a built-in variable */
515                 node->flags |= NO_EXT_SET;
516
517         return node_to_awk_value(node, result, wanted);
518 }
519
520 /* api_sym_lookup_scalar --- retrieve the current value of a scalar */
521
522 static awk_bool_t
523 api_sym_lookup_scalar(awk_ext_id_t id,
524                         awk_scalar_t cookie,
525                         awk_valtype_t wanted,
526                         awk_value_t *result)
527 {
528         NODE *node = (NODE *) cookie;
529
530         if (node == NULL
531             || result == NULL
532             || node->type != Node_var)
533                 return awk_false;
534
535         update_global_values(); /* make sure stuff like NF, NR, are up to date */
536
537         return node_to_awk_value(node, result, wanted);
538 }
539
540 /* api_sym_update --- update a symbol's value, see gawkapi.h for semantics */
541
542 static awk_bool_t
543 api_sym_update(awk_ext_id_t id,
544                 const char *name,
545                 awk_value_t *value)
546 {
547         NODE *node;
548         NODE *array_node;
549
550         if (   name == NULL
551             || *name == '\0'
552             || value == NULL)
553                 return awk_false;
554
555         switch (value->val_type) {
556         case AWK_NUMBER:
557         case AWK_STRING:
558         case AWK_UNDEFINED:
559         case AWK_ARRAY:
560         case AWK_SCALAR:
561         case AWK_VALUE_COOKIE:
562                 break;
563
564         default:
565                 /* fatal(_("api_sym_update: invalid value for type of new value (%d)"), value->val_type); */
566                 return awk_false;
567         }
568
569         node = lookup(name);
570
571         if (node == NULL) {
572                 /* new value to be installed */
573                 if (value->val_type == AWK_ARRAY) {
574                         array_node = awk_value_to_node(value);
575                         node = install_symbol(estrdup((char *) name, strlen(name)),
576                                         Node_var_array);
577                         array_node->vname = node->vname;
578                         *node = *array_node;
579                         freenode(array_node);
580                         value->array_cookie = node;     /* pass new cookie back to extension */
581                 } else {
582                         /* regular variable */
583                         node = install_symbol(estrdup((char *) name, strlen(name)),
584                                         Node_var);
585                         node->var_value = awk_value_to_node(value);
586                 }
587
588                 return awk_true;
589         }
590
591         /*
592          * If we get here, then it exists already.  Any valid type is
593          * OK except for AWK_ARRAY.
594          */
595         if (   (node->flags & NO_EXT_SET) != 0
596             || is_off_limits_var(name)) {       /* most built-in vars not allowed */
597                 node->flags |= NO_EXT_SET;
598                 return awk_false;
599         }
600
601         if (    value->val_type != AWK_ARRAY
602             && (node->type == Node_var || node->type == Node_var_new)) {
603                 unref(node->var_value);
604                 node->var_value = awk_value_to_node(value);
605                 if (node->type == Node_var_new && value->val_type != AWK_UNDEFINED)
606                         node->type = Node_var;
607
608                 return awk_true;
609         }
610
611         return awk_false;
612 }
613
614 /* api_sym_update_scalar --- update a scalar cookie */
615
616 static awk_bool_t
617 api_sym_update_scalar(awk_ext_id_t id,
618                         awk_scalar_t cookie,
619                         awk_value_t *value)
620 {
621         NODE *node = (NODE *) cookie;
622
623         if (value == NULL
624             || node == NULL
625             || node->type != Node_var
626             || (node->flags & NO_EXT_SET) != 0)
627                 return awk_false;
628
629         /*
630          * Optimization: if valref is 1, and the new value is a string or
631          * a number, we can avoid calling unref and then making a new node
632          * by simply installing the new value.  First, we follow the same
633          * recipe used by node.c:r_unref to wipe the current values, and then
634          * we copy the logic from r_make_number or make_str_node to install
635          * the new value.
636          */
637         switch (value->val_type) {
638         case AWK_NUMBER:
639                 if (node->var_value->valref == 1 && ! do_mpfr) {
640                         NODE *r = node->var_value;
641
642                         /* r_unref: */
643                         if ((r->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR))
644                                 efree(r->stptr);
645                         free_wstr(r);
646
647                         /* r_make_number: */
648                         r->numbr = value->num_value;
649                         r->flags = MALLOC|NUMBER|NUMCUR;
650                         r->stptr = NULL;
651                         r->stlen = 0;
652                         return awk_true;
653                 }
654                 break;
655         case AWK_STRING:
656                 if (node->var_value->valref == 1) {
657                         NODE *r = node->var_value;
658
659                         /* r_unref: */
660                         if ((r->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR))
661                                 efree(r->stptr);
662
663                         mpfr_unset(r);
664                         free_wstr(r);
665
666                         /* make_str_node(s, l, ALREADY_MALLOCED): */
667                         r->numbr = 0;
668                         r->flags = (MALLOC|STRING|STRCUR);
669                         r->stfmt = -1;
670                         r->stptr = value->str_value.str;
671                         r->stlen = value->str_value.len;
672                         return awk_true;
673                 }
674                 break;
675         case AWK_UNDEFINED:
676         case AWK_SCALAR:
677         case AWK_VALUE_COOKIE:
678                 break;
679
680         default:        /* AWK_ARRAY or invalid type */
681                 return awk_false;
682         }
683
684         /* do it the hard (slow) way */
685         unref(node->var_value);
686         node->var_value = awk_value_to_node(value);
687         return awk_true;
688 }
689
690 /*
691  * valid_subscript_type --- test if a type is allowed for an array subscript.
692  *
693  * Any scalar value is fine, so only AWK_ARRAY (or an invalid type) is illegal.
694  */
695
696 static inline bool
697 valid_subscript_type(awk_valtype_t valtype)
698 {
699         switch (valtype) {
700         case AWK_UNDEFINED:
701         case AWK_NUMBER:
702         case AWK_STRING:
703         case AWK_SCALAR:
704         case AWK_VALUE_COOKIE:
705                 return true;
706         default:        /* AWK_ARRAY or an invalid type */
707                 return false;
708         }
709 }
710
711 /* Array management */
712 /*
713  * api_get_array_element --- teturn the value of an element - read only!
714  *
715  * Use set_array_element to change it.
716  */
717
718 static awk_bool_t
719 api_get_array_element(awk_ext_id_t id,
720                 awk_array_t a_cookie,
721                 const awk_value_t *const index,
722                 awk_valtype_t wanted,
723                 awk_value_t *result)
724 {
725         NODE *array = (NODE *) a_cookie;
726         NODE *subscript;
727         NODE **aptr;
728
729         /* don't check for index len zero, null str is ok as index */
730         if (   array == NULL
731             || array->type != Node_var_array
732             || result == NULL
733             || index == NULL
734             || ! valid_subscript_type(index->val_type))
735                 return awk_false;
736
737         subscript = awk_value_to_node(index);
738
739         /* if it doesn't exist, return false */
740         if (in_array(array, subscript) == NULL) {
741                 unref(subscript);
742                 return awk_false;
743         }
744
745         aptr = assoc_lookup(array, subscript);
746
747         if (aptr == NULL) {     /* can't happen */
748                 unref(subscript);
749                 return awk_false;
750         }
751
752         unref(subscript);
753
754         return node_to_awk_value(*aptr, result, wanted);
755 }
756
757 /*
758  * api_set_array_element --- change (or create) element in existing array
759  *      with element->index and element->value.
760  */
761
762 static awk_bool_t
763 api_set_array_element(awk_ext_id_t id, awk_array_t a_cookie,
764                                         const awk_value_t *const index,
765                                         const awk_value_t *const value)
766 {
767         NODE *array = (NODE *)a_cookie;
768         NODE *tmp;
769         NODE *elem;
770         NODE **aptr;
771
772         /* don't check for index len zero, null str is ok as index */
773         if (   array == NULL
774             || array->type != Node_var_array
775             || (array->flags & NO_EXT_SET) != 0
776             || index == NULL
777             || value == NULL
778             || ! valid_subscript_type(index->val_type))
779                 return awk_false;
780
781         tmp = awk_value_to_node(index);
782         aptr = assoc_lookup(array, tmp);
783         unref(tmp);
784         unref(*aptr);
785         elem = *aptr = awk_value_to_node(value);
786         if (elem->type == Node_var_array) {
787                 elem->parent_array = array;
788                 elem->vname = estrdup(index->str_value.str,
789                                         index->str_value.len);
790                 make_aname(elem);
791         }
792
793         return awk_true;
794 }
795
796 /*
797  * remove_element --- remove an array element 
798  *              common code used by multiple functions
799  */
800
801 static void
802 remove_element(NODE *array, NODE *subscript)
803 {
804         NODE *val;
805
806         if (array == NULL)
807                 fatal(_("remove_element: received null array"));
808
809         if (subscript == NULL)
810                 fatal(_("remove_element: received null subscript"));
811
812         val = in_array(array, subscript);
813
814         if (val == NULL)
815                 return;
816
817         if (val->type == Node_var_array) {
818                 assoc_clear(val);
819                 /* cleared a sub-array, free Node_var_array */
820                 efree(val->vname);
821                 freenode(val);
822         } else
823                 unref(val);
824
825         (void) assoc_remove(array, subscript);
826 }
827
828 /*
829  * api_del_array_element --- remove the element with the given index.
830  *      Return success if removed or if element did not exist.
831  */
832
833 static awk_bool_t
834 api_del_array_element(awk_ext_id_t id,
835                 awk_array_t a_cookie, const awk_value_t* const index)
836 {
837         NODE *array, *sub;
838
839         array = (NODE *) a_cookie;
840         if (   array == NULL
841             || array->type != Node_var_array
842             || (array->flags & NO_EXT_SET) != 0
843             || index == NULL
844             || ! valid_subscript_type(index->val_type))
845                 return awk_false;
846
847         sub = awk_value_to_node(index);
848         remove_element(array, sub);
849         unref(sub);
850
851         return awk_true;
852 }
853
854 /*
855  * api_get_element_count --- retrieve total number of elements in array.
856  *      Return false if some kind of error.
857  */
858
859 static awk_bool_t
860 api_get_element_count(awk_ext_id_t id,
861                 awk_array_t a_cookie, size_t *count)
862 {
863         NODE *node = (NODE *) a_cookie;
864
865         if (count == NULL || node == NULL || node->type != Node_var_array)
866                 return awk_false;
867
868         *count = node->table_size;
869         return awk_true;
870 }
871
872 /* api_create_array --- create a new array cookie to which elements may be added */
873
874 static awk_array_t
875 api_create_array(awk_ext_id_t id)
876 {
877         NODE *n;
878
879         getnode(n);
880         memset(n, 0, sizeof(NODE));
881         null_array(n);
882
883         return (awk_array_t) n;
884 }
885
886 /* api_clear_array --- clear out an array */
887
888 static awk_bool_t
889 api_clear_array(awk_ext_id_t id, awk_array_t a_cookie)
890 {
891         NODE *node = (NODE *) a_cookie;
892
893         if (   node == NULL
894             || node->type != Node_var_array
895             || (node->flags & NO_EXT_SET) != 0)
896                 return awk_false;
897
898         assoc_clear(node);
899         return awk_true;
900 }
901
902 /* api_flatten_array --- flatten out an array so that it can be looped over easily. */
903
904 static awk_bool_t
905 api_flatten_array(awk_ext_id_t id,
906                 awk_array_t a_cookie,
907                 awk_flat_array_t **data)
908 {
909         NODE **list;
910         size_t i, j;
911         NODE *array = (NODE *) a_cookie;
912         size_t alloc_size;
913
914         if (   array == NULL
915             || array->type != Node_var_array
916             || array->table_size == 0
917             || data == NULL)
918                 return awk_false;
919
920         alloc_size = sizeof(awk_flat_array_t) +
921                         (array->table_size - 1) * sizeof(awk_element_t);
922
923         emalloc(*data, awk_flat_array_t *, alloc_size,
924                         "api_flatten_array");
925         memset(*data, 0, alloc_size);
926
927         list = assoc_list(array, "@unsorted", ASORTI);
928
929         (*data)->opaque1 = array;
930         (*data)->opaque2 = list;
931         (*data)->count = array->table_size;
932
933         for (i = j = 0; i < 2 * array->table_size; i += 2, j++) {
934                 NODE *index, *value;
935
936                 index = list[i];
937                 value = list[i + 1]; /* number or string or subarray */
938
939                 /*
940                  * Convert index and value to ext types.  Force the
941                  * index to be a string, since indices are always
942                  * conceptually strings, regardless of internal optimizations
943                  * to treat them as integers in some cases.
944                  */
945                 if (! node_to_awk_value(index,
946                                 & (*data)->elements[j].index, AWK_STRING)) {
947                         fatal(_("api_flatten_array: could not convert index %d\n"),
948                                                 (int) i);
949                 }
950                 if (! node_to_awk_value(value,
951                                 & (*data)->elements[j].value, AWK_UNDEFINED)) {
952                         fatal(_("api_flatten_array: could not convert value %d\n"),
953                                                 (int) i);
954                 }
955         }
956         return awk_true;
957 }
958
959 /*
960  * api_release_flattened_array --- release array memory,
961  *      delete any marked elements. Count must match what
962  *      gawk thinks the size is.
963  */
964
965 static awk_bool_t
966 api_release_flattened_array(awk_ext_id_t id,
967                 awk_array_t a_cookie,
968                 awk_flat_array_t *data)
969 {
970         NODE *array = a_cookie;
971         NODE **list;
972         size_t i, j, k;
973
974         if (   array == NULL
975             || array->type != Node_var_array
976             || data == NULL
977             || array != (NODE *) data->opaque1
978             || data->count != array->table_size
979             || data->opaque2 == NULL)
980                 return awk_false;
981
982         list = (NODE **) data->opaque2;
983
984         /* free index nodes */
985         for (i = j = 0, k = 2 * array->table_size; i < k; i += 2, j++) {
986                 /* Delete items flagged for delete. */
987                 if (   (data->elements[j].flags & AWK_ELEMENT_DELETE) != 0
988                     && (array->flags & NO_EXT_SET) == 0) {
989                         remove_element(array, list[i]);
990                 }
991                 unref(list[i]);
992         }
993
994         efree(list);
995         efree(data);
996
997         return awk_true;
998 }
999
1000 /* api_create_value --- create a cached value */
1001
1002 static awk_bool_t
1003 api_create_value(awk_ext_id_t id, awk_value_t *value,
1004                 awk_value_cookie_t *result)
1005 {
1006         if (value == NULL || result == NULL)
1007                 return awk_false;
1008
1009         switch (value->val_type) {
1010         case AWK_NUMBER:
1011         case AWK_STRING:
1012                 break;
1013         default:
1014                 /* reject anything other than a simple scalar */
1015                 return awk_false;
1016         }
1017
1018         return (awk_bool_t) ((*result = awk_value_to_node(value)) != NULL);
1019 }
1020
1021 /* api_release_value --- release a cached value */
1022
1023 static awk_bool_t
1024 api_release_value(awk_ext_id_t id, awk_value_cookie_t value)
1025 {
1026         NODE *val = (NODE *) value;
1027
1028         if (val == NULL)
1029                 return awk_false;
1030
1031         unref(val);
1032         return awk_true;
1033 }
1034
1035 /*
1036  * Register a version string for this extension with gawk.
1037  */
1038
1039 struct version_info {
1040         const char *version;
1041         struct version_info *next;
1042 };
1043
1044 static struct version_info *vi_head;
1045
1046 /* api_register_ext_version --- add an extension version string to the list */
1047
1048 static void
1049 api_register_ext_version(awk_ext_id_t id, const char *version)
1050 {
1051         struct version_info *info;
1052
1053         if (version == NULL)
1054                 return;
1055
1056         (void) id;
1057
1058         emalloc(info, struct version_info *, sizeof(struct version_info), "register_ext_version");
1059         info->version = version;
1060         info->next = vi_head;
1061         vi_head = info;
1062 }
1063
1064 /* the struct api */
1065 gawk_api_t api_impl = {
1066         /* data */
1067         GAWK_API_MAJOR_VERSION, /* major and minor versions */
1068         GAWK_API_MINOR_VERSION,
1069         { 0 },                  /* do_flags */
1070
1071         /* registration functions */
1072         api_add_ext_func,
1073         api_register_input_parser,
1074         api_register_output_wrapper,
1075         api_register_two_way_processor,
1076         api_awk_atexit,
1077         api_register_ext_version,
1078
1079         /* message printing functions */
1080         api_fatal,
1081         api_warning,
1082         api_lintwarn,
1083
1084         /* updating ERRNO */
1085         api_update_ERRNO_int,
1086         api_update_ERRNO_string,
1087         api_unset_ERRNO,
1088
1089         /* Function arguments */
1090         api_get_argument,
1091         api_set_argument,
1092
1093         /* Accessing and installing variables and constants */
1094         api_sym_lookup,
1095         api_sym_update,
1096
1097         /* Accessing and modifying variables via scalar cookies */
1098         api_sym_lookup_scalar,
1099         api_sym_update_scalar,
1100
1101         /* Cached values */
1102         api_create_value,
1103         api_release_value,
1104
1105         /* Array management */
1106         api_get_element_count,
1107         api_get_array_element,
1108         api_set_array_element,
1109         api_del_array_element,
1110         api_create_array,
1111         api_clear_array,
1112         api_flatten_array,
1113         api_release_flattened_array,
1114
1115         /* Memory allocation */
1116         malloc,
1117         calloc,
1118         realloc,
1119         free,
1120 };
1121
1122 /* init_ext_api --- init the extension API */
1123
1124 void
1125 init_ext_api()
1126 {
1127         /* force values to 1 / 0 */
1128         api_impl.do_flags[0] = (do_lint ? 1 : 0);
1129         api_impl.do_flags[1] = (do_traditional ? 1 : 0);
1130         api_impl.do_flags[2] = (do_profile ? 1 : 0);
1131         api_impl.do_flags[3] = (do_sandbox ? 1 : 0);
1132         api_impl.do_flags[4] = (do_debug ? 1 : 0);
1133         api_impl.do_flags[5] = (do_mpfr ? 1 : 0);
1134 }
1135
1136 /* update_ext_api --- update the variables in the API that can change */
1137
1138 void
1139 update_ext_api()
1140 {
1141         api_impl.do_flags[0] = (do_lint ? 1 : 0);
1142 }
1143
1144 /* print_ext_versions --- print the list */
1145
1146 extern void
1147 print_ext_versions(void)
1148 {
1149         struct version_info *p;
1150
1151         for (p = vi_head; p != NULL; p = p->next)
1152                 printf("%s\n", p->version);
1153 }