provide /bin/gawk
[platform/upstream/gawk.git] / ext.c
1 /*
2  * ext.c - Builtin function that links external gawk functions and related
3  *         utilities.
4  *
5  * Christos Zoulas, Thu Jun 29 17:40:41 EDT 1995
6  * Arnold Robbins, update for 3.1, Mon Nov 23 12:53:39 EST 1998
7  */
8
9 /*
10  * Copyright (C) 1995 - 2001, 2003-2014 the Free Software Foundation, Inc.
11  * 
12  * This file is part of GAWK, the GNU implementation of the
13  * AWK Programming Language.
14  * 
15  * GAWK is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 3 of the License, or
18  * (at your option) any later version.
19  * 
20  * GAWK is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  * 
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
28  */
29
30 #include "awk.h"
31 extern SRCFILE *srcfiles;
32
33 #ifdef DYNAMIC
34
35 #define OLD_INIT_FUNC   "dlload"
36 #define OLD_FINI_FUNC   "dlunload"
37
38 #include <dlfcn.h>
39
40 /*
41  * is_letter --- function to check letters
42  *      isalpha() isn't good enough since it can look at the locale.
43  * Underscore counts as a letter in awk identifiers
44  */
45
46 static bool
47 is_letter(unsigned char c)
48 {
49         switch (c) {
50         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
51         case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
52         case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
53         case 's': case 't': case 'u': case 'v': case 'w': case 'x':
54         case 'y': case 'z':
55         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
56         case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
57         case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
58         case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
59         case 'Y': case 'Z':
60         case '_':
61                 return true;
62         default:
63                 return false;
64         }
65 }
66
67 /* is_identifier_char --- return true if a character can be used in an identifier */
68
69 static bool
70 is_identifier_char(unsigned char c)
71 {
72         return (is_letter(c) || isdigit(c));
73 }
74
75
76 #define INIT_FUNC       "dl_load"
77
78 /* load_ext --- load an external library */
79
80 void
81 load_ext(const char *lib_name)
82 {
83         int (*install_func)(const gawk_api_t *const, awk_ext_id_t);
84         void *dl;
85         int flags = RTLD_LAZY;
86         int *gpl_compat;
87
88         if (do_sandbox)
89                 fatal(_("extensions are not allowed in sandbox mode"));
90
91         if (do_traditional || do_posix)
92                 fatal(_("-l / @load are gawk extensions"));
93
94         if (lib_name == NULL)
95                 fatal(_("load_ext: received NULL lib_name"));
96
97         if ((dl = dlopen(lib_name, flags)) == NULL)
98                 fatal(_("load_ext: cannot open library `%s' (%s)\n"), lib_name,
99                       dlerror());
100
101         /* Per the GNU Coding standards */
102         gpl_compat = (int *) dlsym(dl, "plugin_is_GPL_compatible");
103         if (gpl_compat == NULL)
104                 fatal(_("load_ext: library `%s': does not define `plugin_is_GPL_compatible' (%s)\n"),
105                                 lib_name, dlerror());
106
107         install_func = (int (*)(const gawk_api_t *const, awk_ext_id_t))
108                                 dlsym(dl, INIT_FUNC);
109         if (install_func == NULL)
110                 fatal(_("load_ext: library `%s': cannot call function `%s' (%s)\n"),
111                                 lib_name, INIT_FUNC, dlerror());
112
113         if (install_func(& api_impl, NULL /* ext_id */) == 0)
114                 warning(_("load_ext: library `%s' initialization routine `%s' failed\n"),
115                                 lib_name, INIT_FUNC);
116 }
117
118 /* do_ext --- load an extension at run-time: interface to load_ext */
119  
120 NODE *
121 do_ext(int nargs)
122 {
123         NODE *obj, *init = NULL, *fini = NULL, *ret = NULL;
124         SRCFILE *s;
125         char *init_func = NULL;
126         char *fini_func = NULL;
127
128         if (nargs == 3) {
129                 fini = POP_STRING();
130                 fini_func = fini->stptr;
131         }
132         if (nargs >= 2) { 
133                 init = POP_STRING();
134                 init_func = init->stptr;
135         }
136         obj = POP_STRING();
137
138         s = add_srcfile(SRC_EXTLIB, obj->stptr, srcfiles, NULL, NULL);
139         if (s != NULL)
140                 ret = load_old_ext(s, init_func, fini_func, obj);
141
142         DEREF(obj);
143         if (fini != NULL)
144                 DEREF(fini);
145         if (init != NULL)
146                 DEREF(init);
147         if (ret == NULL)
148                 ret = dupnode(Nnull_string);
149         return ret;
150 }
151
152 /* load_old_ext --- load an external library */
153
154 NODE *
155 load_old_ext(SRCFILE *s, const char *init_func, const char *fini_func, NODE *obj)
156 {
157         NODE *(*func)(NODE *, void *);
158         NODE *tmp;
159         void *dl;
160         int flags = RTLD_LAZY;
161         int *gpl_compat;
162         const char *lib_name = s->fullpath;
163
164         if (init_func == NULL || init_func[0] == '\0')
165                 init_func = OLD_INIT_FUNC;
166
167         if (fini_func == NULL || fini_func[0] == '\0')
168                 fini_func = OLD_FINI_FUNC;
169
170         if (do_sandbox)
171                 fatal(_("extensions are not allowed in sandbox mode"));
172
173         if (do_traditional || do_posix)
174                 fatal(_("`extension' is a gawk extension"));
175
176         if (lib_name == NULL)
177                 fatal(_("extension: received NULL lib_name"));
178
179         if ((dl = dlopen(lib_name, flags)) == NULL)
180                 fatal(_("extension: cannot open library `%s' (%s)"), lib_name,
181                       dlerror());
182
183         /* Per the GNU Coding standards */
184         gpl_compat = (int *) dlsym(dl, "plugin_is_GPL_compatible");
185         if (gpl_compat == NULL)
186                 fatal(_("extension: library `%s': does not define `plugin_is_GPL_compatible' (%s)"),
187                                 lib_name, dlerror());
188         func = (NODE *(*)(NODE *, void *)) dlsym(dl, init_func);
189         if (func == NULL)
190                 fatal(_("extension: library `%s': cannot call function `%s' (%s)"),
191                                 lib_name, init_func, dlerror());
192
193         if (obj == NULL) {
194                 obj = make_string(lib_name, strlen(lib_name));
195                 tmp = (*func)(obj, dl);
196                 unref(tmp);
197                 unref(obj);
198                 tmp = NULL;
199         } else
200                 tmp = (*func)(obj, dl);
201
202         s->fini_func = (void (*)(void)) dlsym(dl, fini_func);
203         return tmp;
204 }
205
206
207 /* make_builtin --- register name to be called as func with a builtin body */
208
209 awk_bool_t
210 make_builtin(const awk_ext_func_t *funcinfo)
211 {
212         NODE *symbol, *f;
213         INSTRUCTION *b;
214         const char *sp;
215         char c;
216         const char *name = funcinfo->name;
217         int count = funcinfo->num_expected_args;
218
219         sp = name;
220         if (sp == NULL || *sp == '\0')
221                 fatal(_("make_builtin: missing function name"));
222
223         if (! is_letter(*sp))
224                 return awk_false;
225
226         for (sp++; (c = *sp++) != '\0';) {
227                 if (! is_identifier_char(c))
228                         return awk_false;
229         }
230
231         f = lookup(name);
232
233         if (f != NULL) {
234                 if (f->type == Node_func) {
235                         /* user-defined function */
236                         fatal(_("make_builtin: can't redefine function `%s'"), name);
237                 } else if (f->type == Node_ext_func) {
238                         /* multiple extension() calls etc. */ 
239                         if (do_lint)
240                                 lintwarn(_("make_builtin: function `%s' already defined"), name);
241                         return awk_false;
242                 } else
243                         /* variable name etc. */ 
244                         fatal(_("make_builtin: function name `%s' previously defined"), name);
245         } else if (check_special(name) >= 0)
246                 fatal(_("make_builtin: can't use gawk built-in `%s' as function name"), name); 
247
248         if (count < 0)
249                 fatal(_("make_builtin: negative argument count for function `%s'"),
250                                 name);
251
252         b = bcalloc(Op_symbol, 1, 0);
253         b->extfunc = funcinfo->function;
254         b->expr_count = count;
255
256         /* NB: extension sub must return something */
257
258         symbol = install_symbol(estrdup(name, strlen(name)), Node_ext_func);
259         symbol->code_ptr = b;
260         track_ext_func(name);
261         return awk_true;
262 }
263
264 /* make_old_builtin --- register name to be called as func with a builtin body */
265
266 void
267 make_old_builtin(const char *name, NODE *(*func)(int), int count)       /* temporary */
268 {
269         NODE *symbol, *f;
270         INSTRUCTION *b;
271         const char *sp;
272         char c;
273
274         sp = name;
275         if (sp == NULL || *sp == '\0')
276                 fatal(_("extension: missing function name"));
277
278         if (! is_letter(*sp))
279                 fatal(_("extension: illegal character `%c' in function name `%s'"), *sp, name);
280
281         for (sp++; (c = *sp++) != '\0';) {
282                 if (! is_identifier_char(c))
283                         fatal(_("extension: illegal character `%c' in function name `%s'"), c, name);
284         }
285
286         f = lookup(name);
287
288         if (f != NULL) {
289                 if (f->type == Node_func) {
290                         /* user-defined function */
291                         fatal(_("extension: can't redefine function `%s'"), name);
292                 } else if (f->type == Node_ext_func) {
293                         /* multiple extension() calls etc. */ 
294                         if (do_lint)
295                                 lintwarn(_("extension: function `%s' already defined"), name);
296                         return;
297                 } else
298                         /* variable name etc. */ 
299                         fatal(_("extension: function name `%s' previously defined"), name);
300         } else if (check_special(name) >= 0)
301                 fatal(_("extension: can't use gawk built-in `%s' as function name"), name); 
302
303         if (count < 0)
304                 fatal(_("make_builtin: negative argument count for function `%s'"),
305                                 name);
306
307         b = bcalloc(Op_symbol, 1, 0);
308         b->builtin = func;
309         b->expr_count = count;
310
311         /* NB: extension sub must return something */
312
313         symbol = install_symbol(estrdup(name, strlen(name)), Node_old_ext_func);
314         symbol->code_ptr = b;
315         track_ext_func(name);
316 }
317
318
319 /* get_argument --- get the i'th argument of a dynamically linked function */
320
321 NODE *
322 get_argument(int i)
323 {
324         NODE *t;
325         int arg_count, pcount;
326         INSTRUCTION *pc;
327         
328         pc = TOP()->code_ptr;           /* Op_ext_builtin instruction */
329         pcount = (pc + 1)->expr_count;  /* max # of arguments */
330         arg_count = pc->expr_count;     /* # of arguments supplied */
331
332         if (i < 0 || i >= pcount || i >= arg_count)
333                 return NULL;
334
335         t = PEEK(arg_count - i);
336         if (t->type == Node_param_list)
337                 t = GET_PARAM(t->param_cnt);
338
339         if (t->type == Node_array_ref) {
340                 if (t->orig_array->type == Node_var) {
341                         /* already a scalar, can no longer use it as array */ 
342                         t->type = Node_var;
343                         t->var_value = Nnull_string;
344                         return t;
345                 }
346                 return t->orig_array;   /* Node_var_new or Node_var_array */
347         }
348         if (t->type == Node_var)        /* See Case Node_var in setup_frame(), eval.c */
349                 return Nnull_string;
350         /* Node_var_new, Node_var_array or Node_val */
351         return t;
352 }
353
354
355 /*
356  * get_actual_argument --- get the i'th scalar or array argument of a
357  *      dynamically linked function, allowed to be optional.
358  */
359
360 NODE *
361 get_actual_argument(int i, bool optional, bool want_array)
362 {
363         NODE *t;
364         char *fname;
365         int pcount;
366         INSTRUCTION *pc;
367         
368         pc = TOP()->code_ptr;   /* Op_ext_builtin instruction */
369         fname = (pc + 1)->func_name;
370         pcount = (pc + 1)->expr_count;
371  
372         t = get_argument(i);
373         if (t == NULL) {
374                 if (i >= pcount)                /* must be fatal */
375                         fatal(_("function `%s' defined to take no more than %d argument(s)"),
376                                         fname, pcount);
377                 if (! optional)
378                         fatal(_("function `%s': missing argument #%d"),
379                                         fname, i + 1);
380                 return NULL;
381         }
382
383         if (t->type == Node_var_new) {
384                 if (want_array)
385                         return force_array(t, false);
386                 else {
387                         t->type = Node_var;
388                         t->var_value = dupnode(Nnull_string);
389                         return t->var_value;
390                 }
391         }
392
393         if (want_array) {
394                 if (t->type != Node_var_array)
395                         fatal(_("function `%s': argument #%d: attempt to use scalar as an array"),
396                                 fname, i + 1);
397         } else {
398                 if (t->type != Node_val)
399                         fatal(_("function `%s': argument #%d: attempt to use array as a scalar"),
400                                 fname, i + 1);
401         }
402         assert(t->type == Node_var_array || t->type == Node_val);
403         return t;
404 }
405
406 #else
407
408 /* load_ext --- dummy version if extensions not available */
409
410 void
411 load_ext(const char *lib_name)
412 {
413         fatal(_("dynamic loading of library not supported"));
414 }
415 #endif
416
417 /* close_extensions --- execute extension cleanup routines */
418
419 void
420 close_extensions()
421 {
422         SRCFILE *s;
423
424         for (s = srcfiles->next; s != srcfiles; s = s->next) 
425                 if (s->stype == SRC_EXTLIB && s->fini_func)
426                         (*s->fini_func)();
427 }