fungw-1.2.0/0000755000175100017510000000000014047742763011056 5ustar svnsvnfungw-1.2.0/regression/0000755000175100017510000000000014047742763013236 5ustar svnsvnfungw-1.2.0/regression/static_lang.c.in0000644000175100017510000000154713554541752016303 0ustar svnsvnprint [~/* Generated by ./configure - DO NOT EDIT */ /* This file initializes all static linked language bindings */ /* Prototypes */ ~] # temporary: python3 is incompatible with python; prefer python3 for now, for testing put /local/test/langs /local/fungw/bindings switch /local/test/langs case {python[^3]} sub /local/test/langs {python[^3]} {} end end # ... and python3 internal calls are python switch /local/test/langs case {python3} sub /local/test/langs {python3} {python} end end foreach /local/fungw/n in /local/test/langs print [~extern int pplg_init_fungw_~/local/fungw/n~(void);~] {\n} end print [~ /* Initialize all; returns 0 on success */ static int static_lang_init(void) { unsigned err = 0; ~] foreach /local/fungw/n in /local/test/langs print {\t} [~err |= pplg_init_fungw_~/local/fungw/n~();~] {\n} end print [~ return err; } ~] fungw-1.2.0/regression/fgw_string.c0000644000175100017510000000244113561320627015543 0ustar svnsvn#include #include #include static const char *dom_string = "c-string"; fgw_error_t fgwc_malloc(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; FGW_ARGC_REQ_MATCH(1); FGW_ARG_CONV(&argv[1], FGW_SIZE_T); fgw_ptr_reg(ctx, res, dom_string, FGW_VOID, malloc(argv[0].val.nat_size_t)); return FGW_SUCCESS; } fgw_error_t fgwc_free(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; FGW_ARGC_REQ_MATCH(1); FGW_ARG_CONV(&argv[1], FGW_PTR | FGW_VOID); res->type = FGW_VOID; fgw_ptr_unreg(ctx, &argv[1], dom_string); free(argv[1].val.ptr_void); return FGW_SUCCESS; } int *atoi_ucc = NULL; fgw_error_t fgwc_atoi(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; FGW_ARGC_REQ_MATCH(1); FGW_ARG_CONV(&argv[1], FGW_STR); atoi_ucc = argv[0].val.argv0.user_call_ctx; res->val.nat_int = atoi(argv[1].val.str); res->type = FGW_INT; return FGW_SUCCESS; } static int on_load(fgw_obj_t *obj, const char *filename, const char *opts) { fgw_func_reg(obj, "atoi", fgwc_atoi); fgw_func_reg(obj, "malloc", fgwc_malloc); fgw_func_reg(obj, "free", fgwc_free); return 0; } static const fgw_eng_t fgw_string_eng = { "string", fgws_c_call_script, NULL, on_load }; void string_init(void) { fgw_eng_reg(&fgw_string_eng); } fungw-1.2.0/regression/Makefile.in0000644000175100017510000000227314041275427015277 0ustar svnsvnprint [~ all: test test_c test_custom test_script multicall EXAMPLES=../doc/example/00_hello # when fungw is installed, this should be /usr/lib/puplug/ FUNGWBIND=../libfungwbind include $(FUNGWBIND)/libfungwbind.mak CFLAGS = ~/local/fungw/cflags~ ~/target/cc/fpic~ -I.. -I../src_3rd LDFLAGS = ~/local/fungw/ldflags~ ~/target/cc/fpic~ LIBS = ../src_3rd/genht/genht_std.a $(FUNGWBIND_SRCLIBA) LIB_FGW = fgw_string.o ../libfungw/libfungw.a $(FUNGWBIND)/c/fungw_c.o LDLIBS = $(FUNGWBIND_LDLIBS) -lm test: test.o $(LIB_FGW) $(ENGINES) $(LIBS) test_c: test_c.o $(LIB_FGW) $(ENGINES) $(LIBS) test_custom: test_custom.o $(LIB_FGW) $(ENGINES) $(LIBS) test_script: test_script.o hello.o $(LIB_FGW) $(ENGINES) $(LIBS) multicall: multicall.o $(LIB_FGW) $(ENGINES) $(LIBS) ../src_3rd/genht/genht_std.a: cd ../src_3rd/genht && make genht_std.a test.o: test.c test_script.o: test_script.c static_lang.c fgw_string.o: fgw_string.c hello.o: $(EXAMPLES)/hello.c $(CC) $(CFLAGS) -c -o hello.o $(EXAMPLES)/hello.c clean: -rm test test_c test_custom test_script multicall \ hello.o fgw_string.o test.o test_custom.o test_c.o test_script.o \ multicall.o distclean: clean -rm static_lang.c Makefile ~] fungw-1.2.0/regression/multicall.ref0000644000175100017510000000045614032252107015705 0ustar svnsvnscall: test_cb 1: c10 '3.14' test_cb 2: 41 3.140000 test_cb 1: c10 '3.14' test_cb 2: 41 3.140000 vcall: test_cb 1: c10 '3.14' test_cb 2: 41 3.140000 test_cb 1: c10 '3.14' test_cb 2: 41 3.140000 call: test_cb 1: c10 '3.14' test_cb 2: 41 3.140000 test_cb 1: c10 '3.14' test_cb 2: 41 3.140000 fungw-1.2.0/regression/test_c.c0000644000175100017510000000313013315322466014647 0ustar svnsvn#include #include #include #include #include "fgw_string.h" #include int wrap_atoi(fgw_ctx_t *ctx, const char *s) { fgw_arg_t res; fgw_vcall(ctx, &res, "atoi", FGW_STR, s, 0); fgw_arg_conv(ctx, &res, FGW_INT); return res.val.nat_int; } int main() { fgw_ctx_t ctx; fgw_arg_t res, arg[8]; fgw_error_t err; fgw_func_t *func_atoi; char *sres; /* initialize the "string" engine */ string_init(); /* initialize a context */ fgw_init(&ctx, "host"); /* Create an object called "str", using the "string" engine */ fgw_obj_new(&ctx, "str", "string", NULL, NULL); /* get a function handle */ func_atoi = fgw_func_lookup(&ctx, "atoi"); if (func_atoi == NULL) { fprintf(stderr, "Error: failed to find global function atoi\n"); return 1; } /* direct C call to the C function - no fungw overhead, but type conversion overhead still applies; assumes the function is implemented in C */ arg[0].type = FGW_FUNC; arg[0].val.func = func_atoi; arg[1].type = FGW_STR; arg[1].val.str = "42"; err = fgwc_atoi(&res, 2, arg); printf("err=%d rt=%x rv=%d\n", err, res.type, res.val.nat_int); /* string based call */ sres = fgw_scall(&ctx, "atoi", "42", NULL); printf("scall sres='%s'\n", sres); free(sres); /* type/data based call */ err = fgw_vcall(&ctx, &res, "atoi", FGW_STR, "42", NULL); printf("vcall err=%d rt=%x rv=%d\n", err, res.type, res.val.nat_int); /* local wrapper */ printf("wrap: %d\n", wrap_atoi(&ctx, "123")); /* clean up and exit */ fgw_dump_ctx(&ctx, stdout, ""); fgw_uninit(&ctx); fgw_atexit(); return 0; } fungw-1.2.0/regression/test_script.c0000644000175100017510000000614414032255146015736 0ustar svnsvn#include #include #include #include #include "fgw_string.h" #include #include "static_lang.c" char *def_script = NULL; int ref_ucc; extern int *atoi_ucc; void async_error(fgw_obj_t *obj, const char *msg) { printf("Async error: %s: %s\n", obj->name, msg); } /* call a function defined in an object (e.g. from lua) */ int hello(fgw_ctx_t *ctx, int how_many, char *who) { fgw_arg_t res, arg[3]; fgw_func_t *f; f = fgw_func_lookup(ctx, "hello"); if (f != NULL) { arg[0].type = FGW_FUNC; arg[0].val.argv0.func = f; arg[0].val.argv0.user_call_ctx = &ref_ucc; arg[1].type = FGW_INT; arg[1].val.nat_int = how_many; arg[2].type = FGW_STR; arg[2].val.str = who; f->func(&res, 3, arg); if (res.type != FGW_INT) { if ((res.type != FGW_DOUBLE) && (res.type != FGW_LONG) && (res.type != FGW_STR) && (res.type != (FGW_STR | FGW_DYN))) printf("WARNING: returned non-int, non-double, non-str: %x\n", res.type); fgw_arg_conv(ctx, &res, FGW_INT); } } else { printf("ERROR: hello() not found.\n"); return -1; } return res.val.nat_int; } /* Glue for C "scripting" - the code is static linked for this example; could load a .so in real code */ static int c_loader(fgw_obj_t *obj, const char *filename, const char *opts) { if (strcmp(filename, "hello.c") != 0) { fprintf(stderr, "C \"script\" %s is not available\n", filename); exit(1); } hello_load(obj); return 0; } const char *lng; char *script, script_buff[128]; void config() { FILE *f; lng = getenv("LNG"); script = getenv("SCRIPT"); if (script == NULL) { fprintf(stderr, "Error: need SCRIPT set to the test script path\n"); exit(1); } f = fopen(script, "r"); if (f == NULL) { fprintf(stderr, "Error: can not open %s for read\n", script); exit(1); } if ((lng == NULL) || (*lng == '\0')) { lng = fgw_engine_find(script, f); if (lng == NULL) { fprintf(stderr, "Error: can not figure the language of %s; please specify it in LNG\n", script); exit(1); } } fclose(f); } int main() { fgw_ctx_t ctx; int res; /* initialize the "string" engine and language binding engines */ string_init(); static_lang_init(); /* glue for C "scripting" - always needs special treatment */ fgw_c_eng.load = c_loader; fgw_eng_reg(&fgw_c_eng); config(); printf("\n### Set up %s (%s)\n", lng, script); fflush(stdout); /* initialize a context */ fgw_init(&ctx, "host"); ctx.async_error = async_error; /* create objects (load script if needed) */ fgw_obj_new(&ctx, "str", "string", NULL, NULL); fgw_obj_new(&ctx, "hello", lng, script, NULL); /* Call an object function using a local wrapper */ printf("\n### Call hello()\n"); fflush(stdout); res = hello(&ctx, 12, "blobbs"); printf("res=%d\n", res); fflush(stdout); if (getenv("VERBOSE") != NULL) { printf("\n### Public functions:\n"); fgw_dump_ctx(&ctx, stdout, ""); fflush(stdout); } fgw_uninit(&ctx); fgw_atexit(); printf("user call context: "); if (atoi_ucc == &ref_ucc) printf("ok\n"); else if (atoi_ucc == NULL) printf("BROKEN! (NULL)\n"); else printf("BROKEN! (wrong value)\n"); return 0; } fungw-1.2.0/regression/fgw_string.h0000644000175100017510000000055313104775470015555 0ustar svnsvn#include /* Common: register the engine so new objects can be created */ void string_init(void); /* Uncommon: for testing direct calls */ fgw_error_t fgwc_malloc(fgw_arg_t *res, int argc, fgw_arg_t *argv); fgw_error_t fgwc_free(fgw_arg_t *res, int argc, fgw_arg_t *argv); fgw_error_t fgwc_atoi(fgw_arg_t *res, int argc, fgw_arg_t *argv); fungw-1.2.0/regression/multicall.c0000644000175100017510000000211614032753161015354 0ustar svnsvn#include #include #include /* Test that the conversion of an argv[] on the first invocation of a function does not affect the second invocation in an multicall */ fgw_error_t test_cb(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; printf(" test_cb 1: %x '%s'\n", argv[1].type, argv[1].val.str); assert(fgw_arg_conv(ctx, &argv[1], FGW_DOUBLE) == 0); printf(" test_cb 2: %x %f\n", argv[1].type, argv[1].val.nat_double); return FGW_SUCCESS; } int main() { fgw_ctx_t ctx; fgw_obj_t *obj1, *obj2; fgw_arg_t tmp[2]; fgw_init(&ctx, "host"); obj1 = fgw_obj_reg(&ctx, "o1"); obj2 = fgw_obj_reg(&ctx, "o2"); fgw_func_reg(obj1, "test", test_cb); fgw_func_reg(obj2, "test", test_cb); printf("scall:\n"); fgw_scall_all(&ctx, "test", "3.14"); printf("vcall:\n"); fgw_vcall_all(&ctx, "test", FGW_STR, "3.14", 0); printf("call:\n"); tmp[0].type = FGW_INVALID; /* will be filled in by fgw_call_all() */ tmp[1].type = FGW_STR; tmp[1].val.str = "3.14"; fgw_call_all(&ctx, "test", 2, tmp); fgw_uninit(&ctx); fgw_atexit(); return 0; } fungw-1.2.0/regression/test.c0000644000175100017510000000257513315342634014360 0ustar svnsvn#include #include #include #include "fgw_string.h" #include int main() { fgw_ctx_t ctx; char line[1024]; fgw_obj_t *obj; string_init(); fgw_init(&ctx, "host"); obj = fgw_obj_new(&ctx, "str", "string", NULL, NULL); fgw_dump_ctx(&ctx, stdout, ""); while(fgets(line, sizeof(line), stdin) != NULL) { char *fname = line, *args, *next; fgw_func_t *fnc; int fargc; fgw_arg_t fargv[32], res; while(isspace(*fname)) fname++; if ((*fname == '#') || (*fname == '\r') || (*fname == '\n') || (*fname == '\0')) continue; args = strpbrk(fname, "( \t\r\n"); if (args != NULL) { *args = '\0'; args++; } fnc = fgw_func_lookup(&ctx, fname); if (fnc == NULL) { printf("ERROR: no such function\n"); continue; } fargv[0].type = FGW_FUNC; fargv[0].val.func = fnc; for(fargc = 1; args != NULL; fargc++, args = next) { next = strchr(args, ','); if (next != NULL) { *next = '\0'; next++; } while(isspace(*args)) args++; fargv[fargc].type = FGW_STR; fargv[fargc].val.str = args; } printf("call %s %p %d\n", fname, fnc, fnc->func(&res, fargc, fargv)); fgw_arg_conv(&ctx, &res, FGW_STR); printf(" res=%s\n", res.val.str); fgw_arg_free(&ctx, &res); /* the above convert may have allocated a string so it is an FGW_DYN now */ } fgw_uninit(&ctx); fgw_atexit(); return 0; } fungw-1.2.0/regression/test_custom.c0000644000175100017510000000754313315341354015750 0ustar svnsvn#include #include #include #include #include #include "fgw_string.h" #include #include fgw_type_t FGW_MM; /* millimeter type ID; store distance in mm in the long field */ char *mm_dup_str(long val) { char *res = malloc(32); if (val > 1000l*1000l) sprintf(res, "%f km", (double)val/(1000.0*1000.0)); else if (val > 1000) sprintf(res, "%f m", (double)val/1000.0); else sprintf(res, "%ld mm", val); return res; } char *mm_dup_str_nounit(long val) { char *res = malloc(32); sprintf(res, "%ld", val); return res; } long mmtol(const char *s, const char **end) { char *e; long res = strtol(s, &e, 10); if (*e != '\0') { while(isspace(*e)) e++; if (strcmp(e, "mm") == 0) { *end = e+2; } else if (strcmp(e, "km") == 0) { *end = e+2; res *= 1000l*1000l; } else if (strcmp(e, "m") == 0) { *end = e+1; res *= 1000; } else if (strcmp(e, "dm") == 0) { *end = e+2; res *= 100; } else if (strcmp(e, "cm") == 0) { *end = e+2; res *= 10; } else *end = e; } else *end = e; return res; } #define conv_mmtol(dst, src) \ do { \ const char *end; \ dst = mmtol(src, &end); \ if (*end != '\0') \ return -1; \ } while(0) int mm_arg_conv(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target) { if (target == FGW_MM) { /* convert to mm */ long tmp; switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(tmp, conv_assign) ARG_CONV_CASE_LLONG(tmp, conv_assign) ARG_CONV_CASE_DOUBLE(tmp, conv_assign) ARG_CONV_CASE_LDOUBLE(tmp, conv_assign) ARG_CONV_CASE_STR(tmp, conv_mmtol) ARG_CONV_CASE_PTR(tmp, conv_err) ARG_CONV_CASE_CLASS(tmp, conv_err) ARG_CONV_CASE_INVALID(tmp, conv_err) } arg->type = FGW_MM; arg->val.nat_long = tmp; return 0; } if (arg->type == FGW_MM) { /* convert from mm */ long tmp = arg->val.nat_long; switch(target) { ARG_CONV_CASE_LONG(tmp, conv_rev_assign) ARG_CONV_CASE_LLONG(tmp, conv_rev_assign) ARG_CONV_CASE_DOUBLE(tmp, conv_rev_assign) ARG_CONV_CASE_LDOUBLE(tmp, conv_rev_assign) ARG_CONV_CASE_PTR(tmp, conv_err) ARG_CONV_CASE_CLASS(tmp, conv_err) ARG_CONV_CASE_INVALID(tmp, conv_err) case FGW_STR: arg->val.str = mm_dup_str(tmp); arg->type = FGW_STR | FGW_DYN; return 0; } arg->type = target; return 0; } fprintf(stderr, "Neither side of the conversion is mm\n"); abort(); } /* Lazy approach: convert only to/from string */ int mm_arg_conv_lazy(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target) { if (target == FGW_MM) { /* convert to string and then to mm */ long tmp; const char *end; if (fgw_arg_conv(ctx, arg, FGW_STR) != 0) return -1; tmp = mmtol(arg->val.str, &end); if (arg->type & FGW_DYN) free(arg->val.str); if (*end != '\0') return -1; arg->type = FGW_MM; arg->val.nat_long = tmp; return 0; } if (arg->type == FGW_MM) { /* convert from mm */ long tmp = arg->val.nat_long; arg->val.str = mm_dup_str_nounit(tmp); arg->type = FGW_STR | FGW_DYN; return 0; } fprintf(stderr, "Neither side of the conversion is mm\n"); abort(); } void test(fgw_ctx_t *ctx) { fgw_arg_t a, b; int res; a.val.str = "124cm"; a.type = FGW_STR; res = fgw_arg_conv(ctx, &a, FGW_MM); printf("str to mm: %d: 0x%x %ld\n", res, a.type, a.val.nat_long); b = a; res = fgw_arg_conv(ctx, &a, FGW_STR); printf("mm to str: %d: 0x%x %s\n", res, a.type, a.val.str); res = fgw_arg_conv(ctx, &b, FGW_DOUBLE); printf("mm to double: %d: 0x%x %f\n", res, b.type, b.val.nat_double); } int main() { fgw_ctx_t ctx; string_init(); fgw_init(&ctx, "host"); printf("--- proper:\n"); FGW_MM = fgw_reg_custom_type(&ctx, 0, "mm", mm_arg_conv, NULL); assert(FGW_MM != FGW_INVALID); test(&ctx); printf("--- lazy:\n"); FGW_MM = fgw_reg_custom_type(&ctx, 0, "mm", mm_arg_conv_lazy, NULL); assert(FGW_MM != FGW_INVALID); test(&ctx); fgw_uninit(&ctx); fgw_atexit(); return 0; } fungw-1.2.0/AUTHOR0000644000175100017510000000015113561533521011766 0ustar svnsvnFungw is written by Tibor 'Igor2' Palinkas Project page and contact info: http://repo.hu/projects/fungw fungw-1.2.0/libfungw/0000755000175100017510000000000014047742763012673 5ustar svnsvnfungw-1.2.0/libfungw/fungw_debug.c0000644000175100017510000000307313105510756015323 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2017 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ /* Dump the content of a context */ #include #include "fungw.h" void fgw_dump_ctx(fgw_ctx_t *ctx, FILE *f, const char *prefix) { htsp_entry_t *e; if (prefix == NULL) prefix = ""; fprintf(f, "%sfungw ctx:\n", prefix); fprintf(f, "%s objs\n", prefix); for (e = htsp_first(&ctx->obj_tbl); e; e = htsp_next(&ctx->obj_tbl, e)) { fgw_obj_t *obj = e->value; fprintf(f, "%s {%s}\n", prefix, obj->name); } fprintf(f, "%s global functions\n", prefix); for (e = htsp_first(&ctx->func_tbl); e; e = htsp_next(&ctx->func_tbl, e)) { fgw_func_t *fnc = e->value; fprintf(f, "%s %s in {%s}\n", prefix, e->key, fnc->obj->name); } } fungw-1.2.0/libfungw/fungw_call.c0000644000175100017510000002466214041277015015155 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2017, 2018, 2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ /* C->anything call helpers */ #include #include #include "fungw.h" static char *call_func(fgw_ctx_t *ctx, int need_retval, int argc, fgw_arg_t *argv) { fgw_arg_t res = {0}; if (argv[0].val.func->func(&res, argc, argv) != 0) return NULL; if (!need_retval) { if (res.type & FGW_DYN) fgw_arg_free(ctx, &res); return NULL; } fgw_arg_conv(ctx, &res, FGW_STR | FGW_DYN); return res.val.str; } #define ARG_DECL \ va_list ap; \ int argc; \ fgw_arg_t argv_static[16], *argv; \ #ifdef FUNGW_CFG_LONG # define ARG_GET_TYPED_LONG(typ, dst) \ case FGW_LLONG: dst.nat_llong = va_arg(ap, long long); break; \ case FGW_ULLONG: dst.nat_ullong = va_arg(ap, unsigned long long); break; \ case FGW_LDOUBLE: dst.nat_ldouble = va_arg(ap, long double); break; #else # define ARG_GET_TYPED_LONG(typ, dst) #endif #define ARG_GET_TYPED(typ, dst) \ do { \ if (typ & FGW_PTR) \ dst.ptr_void = va_arg(ap, void *); \ else switch(typ) { \ case FGW_CHAR: dst.nat_char = va_arg(ap, int); break; \ case FGW_UCHAR: dst.nat_uchar = va_arg(ap, int); break; \ case FGW_SCHAR: dst.nat_schar = va_arg(ap, int); break; \ case FGW_SHORT: dst.nat_short = va_arg(ap, int); break; \ case FGW_USHORT: dst.nat_ushort = va_arg(ap, int); break; \ case FGW_INT: dst.nat_int = va_arg(ap, int); break; \ case FGW_UINT: dst.nat_uint = va_arg(ap, unsigned int); break; \ case FGW_LONG: dst.nat_long = va_arg(ap, long); break; \ case FGW_ULONG: dst.nat_ulong = va_arg(ap, unsigned long); break; \ case FGW_SIZE_T: dst.nat_size_t = va_arg(ap, size_t); break; \ case FGW_FLOAT: dst.nat_float = va_arg(ap, double); break; \ case FGW_DOUBLE: dst.nat_double = va_arg(ap, double); break; \ ARG_GET_TYPED_LONG(typ, dst) \ case FGW_VOID: \ case FGW_STRUCT: \ case FGW_TERM: \ case FGW_FUNC: \ case FGW_PTR: \ case FGW_STR: \ case FGW_ZTERM: \ case FGW_DYN: \ case FGW_CUSTOM: \ case FGW_AUTO: \ goto badarg; \ } \ } while(0) #define ARG_PRE_COMMON(dbl_get) \ va_start(ap, func_name); \ if (dbl_get) { \ fgw_arg_t tmp; \ for(argc = 1; (tmp.type = va_arg(ap, fgw_type_t)) != 0; argc++) { \ ARG_GET_TYPED(tmp.type, tmp.val); \ } \ } \ else \ for(argc = 1; va_arg(ap, char *) != NULL; argc++) ; \ va_end(ap); \ \ if ((argc) > (sizeof(argv_static) / sizeof(argv_static[0]))) \ argv = malloc((argc) * sizeof(fgw_arg_t)); \ else \ argv = argv_static; \ \ argv[0].type = FGW_FUNC; \ #define ARG_PRE_STR \ do { \ int n; \ \ ARG_PRE_COMMON(0) \ \ va_start(ap, func_name); \ for(n = 1; n < argc; n++) { \ argv[n].type = FGW_STR; \ argv[n].val.str = va_arg(ap, char *); \ } \ va_end(ap); \ } while(0) #define ARG_PRE_TYPE_DATA \ do { \ int n; \ \ ARG_PRE_COMMON(1) \ \ va_start(ap, func_name); \ for(n = 1; n < argc; n++) { \ argv[n].type = va_arg(ap, fgw_type_t); \ ARG_GET_TYPED(argv[n].type, argv[n].val); \ } \ va_end(ap); \ } while(0) #define ARG_POST \ do { \ goto badarg; \ badarg:; \ if (argv != argv_static) \ free(argv); \ } while(0) /* Makes a static copy of all arguments so in_argv[] is preserved (argument conversion will not affect its entries) */ static char *call_func_retain(fgw_ctx_t *ctx, int need_retval, int in_argc, const fgw_arg_t *in_argv) { int n; char *res; fgw_arg_t argv_static[16], *argv; if ((in_argc) > (sizeof(argv_static) / sizeof(argv_static[0]))) argv = malloc((in_argc) * sizeof(fgw_arg_t)); else argv = argv_static; for(n = 0; n < in_argc; n++) { argv[n] = in_argv[n]; argv[n].type &= ~FGW_DYN; } res = call_func(ctx, need_retval, in_argc, argv); ARG_POST; return res; } char *fgw_scall(fgw_ctx_t *ctx, const char *func_name, ...) { char *res = NULL; fgw_func_t *f; ARG_DECL; f = fgw_func_lookup(ctx, func_name); if (f == NULL) return NULL; ARG_PRE_STR; argv[0].val.argv0.func = f; argv[0].val.argv0.user_call_ctx = NULL; res = call_func(ctx, 1, argc, argv); fgw_argv_free(ctx, argc, argv); ARG_POST; return res; } char *fgw_uscall(fgw_ctx_t *ctx, void *user_call_ctx, const char *func_name, ...) { char *res = NULL; fgw_func_t *f; ARG_DECL; f = fgw_func_lookup(ctx, func_name); if (f == NULL) return NULL; ARG_PRE_STR; argv[0].val.argv0.func = f; argv[0].val.argv0.user_call_ctx = user_call_ctx; res = call_func(ctx, 1, argc, argv); fgw_argv_free(ctx, argc, argv); ARG_POST; return res; } #define FGW_MULTI_MAX 512 /* list functions to call for a multi-call; this needs to be done */ static int list_multi(fgw_ctx_t *ctx, const char *func_name, fgw_func_t *list[], int listlen) { htsp_entry_t *e; int len = 0; for (e = htsp_first(&ctx->obj_tbl); e; e = htsp_next(&ctx->obj_tbl, e)) { fgw_obj_t *l = e->value; fgw_func_t *f = htsp_get(&l->func_tbl, func_name); if (f != NULL) { list[len] = f; len++; if (len >= listlen) { /* TODO #3: when breaking API change is possible, do a malloc cache in ctx for this */ return len; } } } return len; } #define MULTI_CALL_LIST \ fgw_func_t *flist[FGW_MULTI_MAX]; \ int n, flistlen = list_multi(ctx, func_name, flist, FGW_MULTI_MAX); void fgw_scall_all(fgw_ctx_t *ctx, const char *func_name, ...) { MULTI_CALL_LIST ARG_DECL; ARG_PRE_STR; /* call all matching names in all objects */ for(n = 0; n < flistlen; n++) { argv[0].val.argv0.func = flist[n]; argv[0].val.argv0.user_call_ctx = NULL; if (flistlen > 1) call_func_retain(ctx, 0, argc, argv); else call_func(ctx, 0, argc, argv); /* no need to fgw_argv_free() - all arguments aer non-dynamic strings */ } fgw_argv_free(ctx, argc, argv); ARG_POST; } void fgw_uscall_all(fgw_ctx_t *ctx, void *user_call_ctx, const char *func_name, ...) { MULTI_CALL_LIST ARG_DECL; ARG_PRE_STR; /* call all matching names in all objects */ for(n = 0; n < flistlen; n++) { argv[0].val.argv0.func = flist[n]; argv[0].val.argv0.user_call_ctx = user_call_ctx; if (flistlen > 1) call_func_retain(ctx, 0, argc, argv); else call_func(ctx, 0, argc, argv); /* no need to fgw_argv_free() - all arguments aer non-dynamic strings */ } fgw_argv_free(ctx, argc, argv); ARG_POST; } fgw_error_t fgw_vcall(fgw_ctx_t *ctx, fgw_arg_t *res, const char *func_name, ...) { fgw_error_t err = FGW_ERR_UNKNOWN; fgw_func_t *f; ARG_DECL; f = fgw_func_lookup(ctx, func_name); if (f == NULL) return FGW_ERR_NOT_FOUND; ARG_PRE_TYPE_DATA; argv[0].val.argv0.func = f; argv[0].val.argv0.user_call_ctx = NULL; err = f->func(res, argc, argv); fgw_argv_free(ctx, argc, argv); ARG_POST; return err; } fgw_error_t fgw_uvcall(fgw_ctx_t *ctx, void *user_call_ctx, fgw_arg_t *res, const char *func_name, ...) { fgw_error_t err = FGW_ERR_UNKNOWN; fgw_func_t *f; ARG_DECL; f = fgw_func_lookup(ctx, func_name); if (f == NULL) return FGW_ERR_NOT_FOUND; ARG_PRE_TYPE_DATA; argv[0].val.argv0.func = f; argv[0].val.argv0.user_call_ctx = user_call_ctx; err = f->func(res, argc, argv); fgw_argv_free(ctx, argc, argv); ARG_POST; return err; } fgw_error_t fgw_vcall_in(fgw_ctx_t *ctx, fgw_arg_t *res, const char *obj_name, const char *func_name, ...) { fgw_error_t err = FGW_ERR_UNKNOWN; fgw_obj_t *obj; fgw_func_t *f; ARG_DECL; obj = fgw_obj_lookup(ctx, obj_name); if (obj == NULL) return FGW_ERR_NOT_FOUND; f = fgw_func_lookup_in(obj, func_name); if (f == NULL) return FGW_ERR_NOT_FOUND; ARG_PRE_TYPE_DATA; argv[0].val.argv0.func = f; argv[0].val.argv0.user_call_ctx = NULL; err = f->func(res, argc, argv); fgw_argv_free(ctx, argc, argv); ARG_POST; return err; } fgw_error_t fgw_uvcall_in(fgw_ctx_t *ctx, void *user_call_ctx, fgw_arg_t *res, const char *obj_name, const char *func_name, ...) { fgw_error_t err = FGW_ERR_UNKNOWN; fgw_obj_t *obj; fgw_func_t *f; ARG_DECL; obj = fgw_obj_lookup(ctx, obj_name); if (obj == NULL) return FGW_ERR_NOT_FOUND; f = fgw_func_lookup_in(obj, func_name); if (f == NULL) return FGW_ERR_NOT_FOUND; ARG_PRE_TYPE_DATA; argv[0].val.argv0.func = f; argv[0].val.argv0.user_call_ctx = user_call_ctx; err = f->func(res, argc, argv); fgw_argv_free(ctx, argc, argv); ARG_POST; return err; } void fgw_vcall_all(fgw_ctx_t *ctx, const char *func_name, ...) { MULTI_CALL_LIST ARG_DECL; ARG_PRE_TYPE_DATA; /* call all matching names in all objects */ for(n = 0; n < flistlen; n++) { argv[0].val.argv0.func = flist[n]; argv[0].val.argv0.user_call_ctx = NULL; if (flistlen > 1) call_func_retain(ctx, 0, argc, argv); else call_func(ctx, 0, argc, argv); } fgw_argv_free(ctx, argc, argv); ARG_POST; } void fgw_uvcall_all(fgw_ctx_t *ctx, void *user_call_ctx, const char *func_name, ...) { MULTI_CALL_LIST ARG_DECL; ARG_PRE_TYPE_DATA; /* call all matching names in all objects */ for(n = 0; n < flistlen; n++) { argv[0].val.argv0.func = flist[n]; argv[0].val.argv0.user_call_ctx = user_call_ctx; if (flistlen > 1) call_func_retain(ctx, 0, argc, argv); else call_func(ctx, 0, argc, argv); } fgw_argv_free(ctx, argc, argv); ARG_POST; } void fgw_ucall_all(fgw_ctx_t *ctx, void *user_call_ctx, const char *func_name, int argc, fgw_arg_t *argv) { MULTI_CALL_LIST argv[0].val.argv0.func = NULL; argv[0].val.argv0.user_call_ctx = user_call_ctx; argv[0].type = FGW_FUNC; /* call all matching names in all objects */ for(n = 0; n < flistlen; n++) { argv[0].val.argv0.func = flist[n]; if (flistlen > 1) call_func_retain(ctx, 0, argc, argv); else call_func(ctx, 0, argc, argv); } fgw_argv_free(ctx, argc, argv); } void fgw_call_all(fgw_ctx_t *ctx, const char *func_name, int argc, fgw_arg_t *argv) { fgw_ucall_all(ctx, NULL, func_name, argc, argv); } fungw-1.2.0/libfungw/Makefile.in0000644000175100017510000000713713770330767014747 0ustar svnsvnput /local/fungw/OFS ?/tmpasm/OFS put /tmpasm/OFS { } put /local/fungw/src { fungw.c fungw_conv.c fungw_ptr.c fungw_debug.c fungw_call.c } uniq /local/fungw/src put /local/fungw/obj /local/fungw/src gsub /local/fungw/obj {.c[ \t\r\n]} {.o } put /local/fungw/cflags ?cc/argstd/Wall if /local/fungw/debug then append /local/fungw/cflags [@-g @/host/cc/argstd/ansi@ @/host/cc/cflags@ -Dinline= @] end put /local/fungw/ldflags [@ @/host/cc/ldflags@ @/target/cc/rdynamic@ @] if ?/local/fungw/disable_dynlib then put /local/fungw/dyntarget {} else put /local/fungw/dyntarget {$(FUNGW_DYN) $(FUNGW_DYN_VER_X) $(FUNGW_DYN_VER_XY) $(FUNGW_DYN_VER_XYZ)} end switch /target/cc/soname case {^$} put /local/fungw/soname {} end default put /local/fungw/soname [@@/target/cc/soname@$(FUNGW_DYN_VER_X)@] end end print [~# Generated by ./configure, do not edit SCCBOX=../scconfig/sccbox PREFIX=~/local/fungw/prefix~ INCDIR=$(install_root)$(DESTDIR)$(PREFIX)/include/libfungw LIBDIR=$(install_root)$(DESTDIR)$(PREFIX)/~/local/fungw/libdirname~ PUPDIR_RAW=$(PREFIX)/~/local/fungw/pupdirname~ CFLAGS = -I.. ~/local/fungw/cflags~ ~/target/cc/fpic~ -I../src_3rd -DFGW_PUPDIRNAME=\"$(PUPDIR_RAW)\" $(FUNGW_CFLAGS) LDFLAGS = ~/local/fungw/ldflags~ ~/target/cc/fpic~ OBJS = ~/local/fungw/obj~ FUNGW_DYN = libfungw~/target/sys/ext_dynlib~ FUNGW_DYN_VER_X = $(FUNGW_DYN).~/local/fungw/ver1~ FUNGW_DYN_VER_XY = $(FUNGW_DYN_VER_X).~/local/fungw/ver2~ FUNGW_DYN_VER_XYZ = $(FUNGW_DYN_VER_XY).~/local/fungw/ver3~ all: libfungw.a $(FUNGW_DYN) libfungw.a: $(OBJS) ~/local/fungw/dyntarget~ @$(SCCBOX) rm -f libfungw.a @~/host/fstools/ar~ r libfungw.a $(OBJS) -@~/host/fstools/ranlib~ libfungw.a $(FUNGW_DYN_VER_XYZ): $(OBJS) $(CC) $(OBJS) -o $(FUNGW_DYN_VER_XYZ) $(LDFLAGS) ~?/target/cc/ldflags_dynlib~ ~?/local/fungw/soname~ ~?/target/cc/so_undefined~ $(FUNGW_DYN_VER_XY): $(FUNGW_DYN_VER_XYZ) $(SCCBOX) ln -f $(FUNGW_DYN_VER_XYZ) $(FUNGW_DYN_VER_XY) $(FUNGW_DYN_VER_X): $(FUNGW_DYN_VER_XYZ) $(SCCBOX) ln -f $(FUNGW_DYN_VER_XYZ) $(FUNGW_DYN_VER_X) $(FUNGW_DYN): $(FUNGW_DYN_VER_XYZ) $(SCCBOX) ln -f $(FUNGW_DYN_VER_XYZ) $(FUNGW_DYN) include Makefile.dep clean: $(SCCBOX) rm -f $(OBJS) libfungw.a $(FUNGW_DYN) $(FUNGW_DYN_VER_X) $(FUNGW_DYN_VER_XY) $(FUNGW_DYN_VER_XYZ) distclean: rm Makefile # Universal install rule (can uninstall as well) install_: $(MKDR) $(LIBDIR) $(INCDIR) $(INST) fungw.h $(INCDIR)/fungw.h $(INST) fungw_conv.h $(INCDIR)/fungw_conv.h $(INST) scconfig_hooks.h $(INCDIR)/scconfig_hooks.h $(INST) $(FUNGW_DYN_VER_XYZ) $(LIBDIR)/$(FUNGW_DYN_VER_XYZ) install_link_: $(SCCBOX) $(HOW) $(FUNGW_DYN_VER_XYZ) $(LIBDIR)/$(FUNGW_DYN) $(SCCBOX) $(HOW) $(FUNGW_DYN_VER_XYZ) $(LIBDIR)/$(FUNGW_DYN_VER_X) $(SCCBOX) $(HOW) $(FUNGW_DYN_VER_XYZ) $(LIBDIR)/$(FUNGW_DYN_VER_XY) install: make install_ "INST=$(SCCBOX) install -i" "MKDR=$(SCCBOX) mkdir -p -i" make install_link_ "HOW=ln -f" linstall: make install_ "INST=$(SCCBOX) install -l -f -a" "MKDR=$(SCCBOX) mkdir -p -i" make install_link_ "HOW=ln -f" uninstall: make install_link_ "HOW=install -u" make install_ "INST=$(SCCBOX) install -u -f" "MKDR=$(SCCBOX) mkdir -u" dep: echo "### Generated file, do not edit, run make dep ###" > Makefile.dep echo "" >> Makefile.dep ~] foreach /local/fungw/c in /local/fungw/src print [~ @gcc -MM ~/local/fungw/c~ -I. >> Makefile.dep ~] end print {\n### Rules for compiling objects ###\n} foreach /local/fungw/c in /local/fungw/src put /local/fungw/o /local/fungw/c sub /local/fungw/o {.c$} {.o} print [~ ~/local/fungw/o~: ~/local/fungw/c~ $(CC) -c $(CFLAGS) -o ~/local/fungw/o~ ~/local/fungw/c~ ~] end put /tmpasm/OFS /local/fungw/OFS fungw-1.2.0/libfungw/Makefile.dep0000644000175100017510000000033013106766545015075 0ustar svnsvn### Generated file, do not edit, run make dep ### fungw.o: fungw.c fungw.h fungw_conv.o: fungw_conv.c fungw.h fungw_ptr.o: fungw_ptr.c fungw.h fungw_debug.o: fungw_debug.c fungw.h fungw_call.o: fungw_call.c fungw.h fungw-1.2.0/libfungw/scconfig_hooks.h0000644000175100017510000000720514047742734016044 0ustar svnsvn/* fungw - function gateway Copyright (C) 2017,2018,2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* These functions can be called from an scconfig hooks.c to get libfungw configured without having to have or compile or run a separate scconfig process for the lib. */ #include #include #include "arg.h" #include "log.h" #include "dep.h" #include "db.h" #include "libs.h" #include "tmpasm_scconfig.h" const char *fungw_ver1 = "1"; const char *fungw_ver2 = "2"; const char *fungw_ver3 = "0"; static void fungw_set_debug(const char *val) { put("/local/fungw/debug", val); } static void fungw_set_prefix(const char *val) { put("/local/fungw/prefix", val); } static void fungw_set_libdirname(const char *val) { put("/local/fungw/libdirname", val); } static void fungw_set_pupdirname(const char *val) { put("/local/fungw/pupdirname", val); } static int fungw_hook_postinit(void) { db_mkdir("/local"); put("/local/fungw/debug", sfalse); put("/local/fungw/ver1", fungw_ver1); put("/local/fungw/ver2", fungw_ver2); put("/local/fungw/ver3", fungw_ver3); put("/local/fungw/prefix", "/usr/local"); put("/local/fungw/libdirname", "lib"); put("/local/fungw/pupdirname", "lib/puplug"); return 0; } static int fungw_hook_detect_host(void) { if (istrue(get("/local/fungw/debug"))) { require("cc/argstd/Wall", 0, 0); require("cc/argstd/ansi", 0, 0); } require("fstools/ar", 0, 0); require("fstools/ranlib", 0, 0); return 0; } static int fungw_hook_detect_target(void) { require("libs/ldl", 0, 0); require("cc/rdynamic", 0, 0); require("cc/soname", 0, 0); require("cc/so_undefined", 0, 0); require("cc/fpic", 0, 0); require("cc/wlrpath", 0, 0); require("cc/ldflags_dynlib", 0, 0); return 0; } /* Root is the path for the fungwlug lib dir (trunk/libfungw) */ static int fungw_hook_generate(const char *root) { int generr = 0; fprintf(stderr, "Generating %s/Makefile (%d)\n", root, generr |= tmpasm(root, "Makefile.in", "Makefile")); return generr; } static void fungw_print_configure_summary(void) { const char *sum_in; fprintf(stderr, "\n\n== fungw configuration summary ==\n"); fprintf(stderr, " version: %s.%s.%s\n", fungw_ver1, fungw_ver2, fungw_ver3); sum_in = get("/local/fungw/summary"); if ((sum_in != NULL) && (*sum_in != '\0')) { char *sep, *curr, *next, *sum, *state; while(*sum_in == ' ') sum_in++; sum = strclone(sum_in); for(curr = sum; (curr != NULL) && (*curr != '\0'); curr = next) { next = strchr(curr, ' '); if (next != NULL) { *next = '\0'; next++; while(*next == ' ') next++; } sep = strchr(curr, ':'); if (sep != NULL) { *sep = '\0'; sep++; if (*sep == ':') sep++; } else sep = "u"; switch(*sep) { case 'e': state = "enabled"; break; case 'd': state = "disabled"; break; default: state = "unknown"; break; } fprintf(stderr, " binding: %-20s %s\n", curr, state); } free(sum); } printf("\n\n"); } fungw-1.2.0/libfungw/fungw_conv.h0000644000175100017510000001000713335542711015203 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2017 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ /* Helper macros to convert types, per class, in a switch */ #define conv_assign(dst, src) dst = (src) #define conv_rev_assign(dst, src) src = (dst) #define conv_round(dst, src) dst = fungw_round(src) #define conv_err(dst, src) return -1 #define conv_break(dst, src) break #define conv_strtol(dst, src) \ do { \ char *end; \ if (src == NULL) \ return -1; \ if ((src[0] == '0') && (src[1] == 'x')) \ dst = strtol(src+2, &end, 16); \ else \ dst = strtol(src, &end, 10); \ if (*end != '\0') \ return -1; \ } while(0) #define conv_strtod(dst, src) \ do { \ char *end; \ if (src == NULL) \ return -1; \ dst = strtod(src, &end); \ if (*end != '\0') \ return -1; \ } while(0) #ifdef FUNGW_CFG_LONG #define conv_strtoll(dst, src) \ do { \ char *end; \ if (src == NULL) \ return -1; \ if ((src[0] == '0') && (src[1] == 'x')) \ dst = strtoll(src+2, &end, 16); \ else \ dst = strtoll(src, &end, 10); \ if (*end != '\0') \ return -1; \ } while(0) #define conv_strtold(dst, src) \ do { \ char *end; \ if (src == NULL) \ return -1; \ dst = strtold(src, &end); \ if (*end != '\0') \ return -1; \ } while(0) #endif #define ARG_CONV_CASE_LONG(dst, func) \ case FGW_CHAR: func(dst, arg->val.nat_char); break; \ case FGW_UCHAR: func(dst, arg->val.nat_uchar); break; \ case FGW_SCHAR: func(dst, arg->val.nat_schar); break; \ case FGW_SHORT: func(dst, arg->val.nat_short); break; \ case FGW_USHORT: func(dst, arg->val.nat_ushort); break; \ case FGW_INT: func(dst, arg->val.nat_int); break; \ case FGW_UINT: func(dst, arg->val.nat_uint); break; \ case FGW_LONG: func(dst, arg->val.nat_long); break; \ case FGW_ULONG: func(dst, arg->val.nat_ulong); break; \ #define ARG_CONV_CASE_DOUBLE(dst, func) \ case FGW_FLOAT: func(dst, arg->val.nat_float); break; \ case FGW_DOUBLE: func(dst, arg->val.nat_double); break; \ #ifdef FUNGW_CFG_LONG # define ARG_CONV_CASE_LDOUBLE(dst, func) \ case FGW_LDOUBLE: func(dst, arg->val.nat_double); break; # define ARG_CONV_CASE_LLONG(dst, func) \ case FGW_LLONG: func(dst, arg->val.nat_llong); break; \ case FGW_ULLONG: func(dst, arg->val.nat_ullong); break; \ case FGW_SIZE_T: func(dst, arg->val.nat_size_t); break; #else # define ARG_CONV_CASE_LDOUBLE(dst, func) # define ARG_CONV_CASE_LLONG(dst, func) \ case FGW_SIZE_T: func(dst, arg->val.nat_size_t); break; #endif #define ARG_CONV_CASE_PTR(dst, func) \ case FGW_STRUCT: func(dst, arg->val.ptr_void); break; \ case FGW_VOID: func(dst, arg->val.ptr_void); break; \ #define ARG_CONV_CASE_STR(dst, func) \ case FGW_STR: { \ char *__str__ = arg->val.ptr_char; \ int __need2free__ = arg->type & FGW_DYN; \ func(dst, __str__); \ if (__need2free__) \ free(__str__); \ break; \ } #define ARG_CONV_CASE_CLASS(dst, func) \ case FGW_PTR: func(dst, 0); break; \ case FGW_ZTERM: func(dst, 0); break; \ #define ARG_CONV_CASE_INVALID(dst, func) \ case FGW_INVALID: func(dst, 0); break; \ case FGW_FUNC: func(dst, 0); break; \ case FGW_DYN: func(dst, 0); break; \ case FGW_CUSTOM: func(dst, 0); break; double fungw_round(double x); fungw-1.2.0/libfungw/fungw.h0000644000175100017510000003153713757631554014205 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2017,2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #ifndef FUNGW_H #define FUNGW_H #include #include #include #include #include #ifdef FUNGW_CFG_LONG # define FGW_API_VER (0x1010 | 0x1) #else # define FGW_API_VER (0x1010) #endif extern unsigned int fgw_api_ver; /* maximum length of an ID (lib name or function name) */ #define FGW_ID_LEN 255 typedef enum fgw_error_e { FGW_SUCCESS = 0, FGW_ERR_ARGC, /* wrong number of arguments */ FGW_ERR_ARG_CONV, /* failed to convert an argument */ FGW_ERR_ARGV_TYPE, /* wrong argument type */ FGW_ERR_NOT_FOUND, FGW_ERR_PTR_DOMAIN, /* pointer passed from the wrong domain */ FGW_ERR_UNKNOWN } fgw_error_t; typedef struct fgw_ctx_s fgw_ctx_t; typedef struct fgw_obj_s fgw_obj_t; typedef struct fgw_arg_s fgw_arg_t; typedef struct fgw_eng_s fgw_eng_t; typedef struct fgw_func_s fgw_func_t; typedef struct fgw_custype_s fgw_custype_t; struct fgw_ctx_s { htsp_t func_tbl; /* strdup'd name to (fgw_func_t *) */ htsp_t obj_tbl; /* to (fgw_obj_t *) */ htpp_t ptr_tbl; /* void* to const-domain-string (not strdup'd) */ void (*async_error)(fgw_obj_t *obj, const char *msg); /* called on error while executing a fungw call typicially in a binding */ fgw_custype_t *custype; /* array of custom types, indexed as (type-FGW_CUSTOM); NULL if there's no custom type */ char *name; }; struct fgw_obj_s { char *name; int name_len; unsigned long int script_type; htsp_t func_tbl; /* func->name to (fgw_func_t *) (func name is not strup'd) */ void *script_data, *script_user_call_ctx; fgw_ctx_t *parent; const fgw_eng_t *engine; }; extern htsp_t fgw_engines; struct fgw_eng_s { char *name; fgw_error_t (*call_script)(fgw_arg_t *res, int argc, fgw_arg_t *argv); /* call a function defined in the script */ int (*init)(fgw_obj_t *obj, const char *filename, const char *opts); /* initialize the interpreter */ int (*load)(fgw_obj_t *obj, const char *filename, const char *opts); /* load a script (external functions will be registered already) */ int (*unload)(fgw_obj_t *obj); /* unload a script, lib is free'd by the caller */ void (*reg_func)(fgw_obj_t *obj, const char *name, fgw_func_t *f); void (*unreg_func)(fgw_obj_t *obj, const char *name); int (*test_parse)(const char *filename, FILE *f); /* returns 1 if the file looks like something the engine can handle; if f is not NULL, it's a read-only file seeked to the beginning */ const char *def_ext; /* default script extension */ }; typedef enum fgw_type_e { FGW_INVALID = 0, FGW_TERM = FGW_INVALID, FGW_CHAR = 0x0010, FGW_UCHAR, FGW_SCHAR, FGW_SHORT, FGW_USHORT, FGW_INT, FGW_UINT, FGW_LONG, FGW_ULONG, FGW_AUTO, /* for "custom to any" conversion -> should convert to any base (non-custom) type */ FGW_SIZE_T = 0x0030, #ifdef FUNGW_CFG_LONG FGW_LLONG, FGW_ULLONG, #endif FGW_FLOAT = 0x0040, FGW_DOUBLE, #ifdef FUNGW_CFG_LONG FGW_LDOUBLE, #endif FGW_STRUCT = 0x0050, /* valid only wth FGW_PTR */ FGW_VOID, /* valid only wth FGW_PTR */ FGW_FUNC, FGW_CUSTOM = 0x0060, /* the first custom type; anything between this and FGW_IN is considered a custom type */ FGW_PTR = 0x0400, /* pointer to the given type (or array) */ FGW_ZTERM = 0x0800, /* zero-terminated array */ FGW_DYN = 0x1000, /* dynamic allocated by wrappers, should be free'd */ /* shorthands */ FGW_STR = FGW_CHAR | FGW_PTR | FGW_ZTERM } fgw_type_t; #define FGW_NUM_CUSTOM_TYPES (FGW_PTR - FGW_CUSTOM) #define FGW_BASE_TYPE(t) ((t) & 0xFFF) #define FGW_IS_TYPE_CUSTOM(t) ((((t) & 0x3FF) >= FGW_CUSTOM) && (((t) & 0x3FF) <= FGW_CUSTOM + FGW_NUM_CUSTOM_TYPES)) /* fungw functions have the following prototype, bundled with a binding-specific ID: */ struct fgw_func_s { fgw_error_t (*func)(fgw_arg_t *res, int argc, fgw_arg_t *argv); char *name; fgw_obj_t *obj; void *obj_data; void *reg_data; /* optional field to be loaded by the registrar, after function registration; e.g. for holding description or other metadata on why the function is registered */ }; typedef union fgw_value_e { /* nativ types */ char nat_char; unsigned char nat_uchar; signed char nat_schar; short nat_short; unsigned short nat_ushort; int nat_int; unsigned int nat_uint; long nat_long; unsigned long nat_ulong; #ifdef FUNGW_CFG_LONG long long nat_llong; unsigned long long nat_ullong; #endif size_t nat_size_t; float nat_float; double nat_double; #ifdef FUNGW_CFG_LONG long double nat_ldouble; #endif /* pointer types */ char *ptr_char; unsigned char *ptr_uchar; signed char *ptr_schar; short *ptr_short; unsigned short *ptr_ushort; int *ptr_int; unsigned int *ptr_uint; long *ptr_long; unsigned long *ptr_ulong; #ifdef FUNGW_CFG_LONG long long *ptr_llong; unsigned long long *ptr_ullong; #endif size_t *ptr_size_t; float *ptr_float; double *ptr_double; #ifdef FUNGW_CFG_LONG long double *ptr_ldouble; #endif void *ptr_struct; void *ptr_void; char *str; const char *cstr; fgw_func_t *func; struct { fgw_func_t *func; void *user_call_ctx; } argv0; /* function call with user context also encoded in argv[0] */ union { char c[sizeof(void *)*2]; void *p[2]; } custom; } fgw_value_t; struct fgw_arg_s { fgw_type_t type; fgw_value_t val; }; /* Custom type: should use the ->custom field */ struct fgw_custype_s { char *name; /* slot is in use if not NULL */ int (*arg_conv)(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target); /* converts to target or if that is not possible, to any non-custom type for a 2-stage conversion */ int (*arg_free)(fgw_ctx_t *ctx, fgw_arg_t *arg); /* free extra memory allocated for the type for storing the value (does not free arg itself) */ }; /* call this before the applicaton exits, to clean up persistent engine data */ void fgw_atexit(void); /*** Context handling ***/ void fgw_init(fgw_ctx_t *ctx, const char *name); void fgw_uninit(fgw_ctx_t *ctx); /*** object table: registration and lookup ***/ #define fgw_obj_lookup(ctx, name) ((fgw_obj_t *)htsp_get(&(ctx)->obj_tbl, (name))) fgw_obj_t *fgw_obj_reg(fgw_ctx_t *ctx, const char *name); void fgw_obj_unreg(fgw_ctx_t *ctx, fgw_obj_t *obj); /* Create a new instance of an engine */ fgw_obj_t *fgw_obj_new(fgw_ctx_t *ctx, const char *obj_name, const char *eng_name, const char *filename, const char *opts); fgw_obj_t *fgw_obj_new2(fgw_ctx_t *ctx, const char *obj_name, const char *eng_name, const char *filename, const char *opts, void *user_call_ctx); /*** function table: registration and lookup ***/ /* Return the function for a name on NULL if not found*/ #define fgw_func_lookup_in(obj, name) ((fgw_func_t *)htsp_get(&(obj)->func_tbl, (name))) #define fgw_func_lookup(ctx, name) ((fgw_func_t *)htsp_get(&(ctx)->func_tbl, (name))) /* Register a named function, retruns 0 on success; re-registrtion is error */ fgw_func_t *fgw_func_reg(fgw_obj_t *obj, const char *name, fgw_error_t (*func)(fgw_arg_t *res, int argc, fgw_arg_t *argv)); /* Unregister a named function, retruns 0 if it could remove the name */ int fgw_func_unreg(fgw_obj_t *obj, const char *name); /*** converter ***/ int fgw_arg_conv(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target_type); void fgw_arg_free(fgw_ctx_t *ctx, fgw_arg_t *arg); void fgw_argv_free(fgw_ctx_t *ctx, int argc, fgw_arg_t *argv); /*** wrapper imnplementation helpers */ #define FGW_DECL_CTX \ fgw_ctx_t *ctx = argv[0].val.func->obj->parent #define FGW_ARGC_REQ_MATCH(expected) \ do { if ((argc-1) != expected) return FGW_ERR_ARGC; } while(0) #define FGW_ARGC_REQ_RANGE(minarg, maxarg) \ do { if (((argc-1) < minarg) || ((argc-1) > maxarg)) return FGW_ERR_ARGC; } while(0) #define FGW_ARG_CONV(arg, target_type) \ do { if (fgw_arg_conv(ctx, arg, target_type) != 0) return FGW_ERR_ARG_CONV; } while(0) /*** pointers ***/ void fgw_ptr_reg(fgw_ctx_t *ctx, fgw_arg_t *res, const char *ptr_domain, fgw_type_t ptr_type, void *ptr); void fgw_ptr_unreg(fgw_ctx_t *ctx, fgw_arg_t *res, const char *ptr_domain); /* returns non-zero if ptr is a pointer and is in the ptr_domain */ int fgw_ptr_in_domain(fgw_ctx_t *ctx, fgw_arg_t *ptr, const char *ptr_domain); /*** debug ***/ void fgw_dump_ctx(fgw_ctx_t *ctx, FILE *f, const char *prefix); /*** engines ***/ void fgw_eng_reg(const fgw_eng_t *eng); void fgw_eng_unreg(const char *name); #define fgw_eng_lookup(name) \ ((fgw_engines.table == NULL) ? ((fgw_eng_t *)NULL) : ((fgw_eng_t *)htsp_get(&fgw_engines, name))) /*** function calls ***/ /* The ones prefixed with 'u' also pass user_call_ctx in argv0 (packed after the func field in the argv[0].val.argv0) */ /* Call a function by name, with const char string arguments terminated with NULL; return a newly allocated string with the return value. Func name is either a full name (obj.func) or a short name (func) */ char *fgw_scall(fgw_ctx_t *ctx, const char *func_name, ...); char *fgw_uscall(fgw_ctx_t *ctx, void *user_call_ctx, const char *func_name, ...); /* Call func_name (always short name) in each object that has it; objects are visited in random order. */ void fgw_scall_all(fgw_ctx_t *ctx, const char *func_name, ...); void fgw_uscall_all(fgw_ctx_t *ctx, void *user_call_ctx, const char *func_name, ...); /* Same as fgw_scall, but takes fgw_type_t, data pairs in vararg and the result is copied in res. Arg list terminated with 0. */ fgw_error_t fgw_vcall(fgw_ctx_t *ctx, fgw_arg_t *res, const char *func_name, ...); fgw_error_t fgw_vcall_in(fgw_ctx_t *ctx, fgw_arg_t *res, const char *obj_name, const char *func_name, ...); fgw_error_t fgw_uvcall(fgw_ctx_t *ctx, void *user_call_ctx, fgw_arg_t *res, const char *func_name, ...); fgw_error_t fgw_uvcall_in(fgw_ctx_t *ctx, void *user_call_ctx, fgw_arg_t *res, const char *obj_name, const char *func_name, ...); /* Same as fgw_scall_all, but takes fgw_type_t, data pairs in vararg */ void fgw_vcall_all(fgw_ctx_t *ctx, const char *func_name, ...); void fgw_uvcall_all(fgw_ctx_t *ctx, void *user_call_ctx, const char *func_name, ...); /* Same as fgw_scall_all, but takes argc/argv; caller should allocate but leave argv[0] empty - it is going to be overwritten */ void fgw_call_all(fgw_ctx_t *ctx, const char *func_name, int argc, fgw_arg_t *argv); void fgw_ucall_all(fgw_ctx_t *ctx, void *user_call_ctx, const char *func_name, int argc, fgw_arg_t *argv); /*** misc ***/ #define FGW_CFG_PUPDIR fgw_cfg_pupdir extern const char *fgw_cfg_pupdir; char *fgw_strdup(const char *s); /* Throw an async error from a binding */ void fgw_async_error(fgw_obj_t *obj, const char *msg); /* Register a new type; if id is non-zero, attempt to use that specific ID for the type (may fail if it's already in use), else allocate one dynamically. */ fgw_type_t fgw_reg_custom_type(fgw_ctx_t *ctx, fgw_type_t id, const char *name, int (*arg_conv)(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target), int (*arg_free)(fgw_ctx_t *ctx, fgw_arg_t *arg)); /* Remove a custom type; the id will never be reused so outstading arg values with the given type will not be given to the wrong new function. The conv function is set to NULL to generate an error. Returns 0 on success. */ int fgw_unreg_custom_type(fgw_ctx_t *ctx, fgw_type_t id); /* Compare each ending to the end of the filename, return 1 on match (0 otherwise) */ int fgw_test_parse_fn(const char *filename, const char **endings); /* Ask each engine whether it can load fn/f. If f is not NULL, it must be open for read and will be rewound. Returns NULL or the name of the first engine that is willing to load the file. */ const char *fgw_engine_find(const char *fn, FILE *f); /*** script helpers ***/ #define fgws_ucc_save(obj) \ do { \ void *__ucc_save__ = (obj)->script_user_call_ctx; \ (obj)->script_user_call_ctx = argv[0].val.argv0.user_call_ctx; #define fgws_ucc_restore(obj) \ (obj)->script_user_call_ctx = __ucc_save__; \ } while(0) #endif fungw-1.2.0/libfungw/fungw_conv.c0000644000175100017510000002371613631362467015220 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2017, 2018 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ /* In-place converter of fgw_arg_t from one type to another */ #include #include #include #include #include #include "fungw.h" #include "fungw_conv.h" /* round() is not c89 and glibc requires feature macros for it. To avoid all the risk, just implement a local inlinable function that depends only on ceil. */ double fungw_round(double x) { double t; if (x >= 0.0) { t = ceil(x); if (t - x > 0.5) t -= 1.0; return t; } t = ceil(-x); if ((t + x) > 0.5) t -= 1.0; return -t; } void fgw_arg_free_str(fgw_ctx_t *ctx, fgw_arg_t *arg) { free(arg->val.str); arg->type = FGW_INVALID; } void fgw_arg_free(fgw_ctx_t *ctx, fgw_arg_t *arg) { int baset; if (!(arg->type & FGW_DYN)) { arg->type = FGW_INVALID; return; } if ((arg->type & FGW_CHAR) && (arg->type & FGW_PTR)) { arg->type = FGW_INVALID; free(arg->val.str); return; } if (ctx->custype == NULL) { arg->type = FGW_INVALID; return; } baset = FGW_BASE_TYPE(arg->type); if ((baset >= FGW_CUSTOM) && (baset < FGW_CUSTOM + FGW_NUM_CUSTOM_TYPES)) { if ((ctx->custype[baset - FGW_CUSTOM].name == NULL) || (ctx->custype[baset - FGW_CUSTOM].arg_free == NULL)) { arg->type = FGW_INVALID; return; } ctx->custype[baset - FGW_CUSTOM].arg_free(ctx, arg); } arg->type = FGW_INVALID; } void fgw_argv_free(fgw_ctx_t *ctx, int argc, fgw_arg_t *argv) { for(; argc > 0; argc--,argv++) fgw_arg_free(ctx, argv); } int fgw_arg_conv_to_long(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target) { unsigned long tmp; switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(tmp, conv_assign) ARG_CONV_CASE_LLONG(tmp, conv_assign) ARG_CONV_CASE_DOUBLE(tmp, conv_round) ARG_CONV_CASE_LDOUBLE(tmp, conv_round) ARG_CONV_CASE_STR(tmp, conv_strtol) ARG_CONV_CASE_PTR(tmp, conv_err) ARG_CONV_CASE_CLASS(tmp, conv_err) case FGW_AUTO: ARG_CONV_CASE_INVALID(tmp, conv_err) } switch(target) { ARG_CONV_CASE_LONG(tmp, conv_rev_assign) ARG_CONV_CASE_LLONG(tmp, conv_err) ARG_CONV_CASE_DOUBLE(tmp, conv_err) ARG_CONV_CASE_LDOUBLE(tmp, conv_err) ARG_CONV_CASE_PTR(tmp, conv_err) ARG_CONV_CASE_STR(tmp, conv_err) ARG_CONV_CASE_CLASS(tmp, conv_err) case FGW_AUTO: ARG_CONV_CASE_INVALID(tmp, conv_err) } arg->type = target; return 0; } int fgw_arg_conv_to_llong(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target) { #ifdef FUNGW_CFG_LONG unsigned long long tmp; #else unsigned long tmp; #endif switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(tmp, conv_assign) ARG_CONV_CASE_LLONG(tmp, conv_assign) ARG_CONV_CASE_DOUBLE(tmp, conv_round) ARG_CONV_CASE_LDOUBLE(tmp, conv_round) #ifdef FUNGW_CFG_LONG ARG_CONV_CASE_STR(tmp, conv_strtoll) #else ARG_CONV_CASE_STR(tmp, conv_strtol) #endif ARG_CONV_CASE_PTR(tmp, conv_err) ARG_CONV_CASE_CLASS(tmp, conv_err) case FGW_AUTO: ARG_CONV_CASE_INVALID(tmp, conv_err) } switch(target) { ARG_CONV_CASE_LONG(tmp, conv_err) ARG_CONV_CASE_LLONG(tmp, conv_rev_assign) ARG_CONV_CASE_DOUBLE(tmp, conv_err) ARG_CONV_CASE_LDOUBLE(tmp, conv_err) ARG_CONV_CASE_PTR(tmp, conv_err) ARG_CONV_CASE_STR(tmp, conv_err) ARG_CONV_CASE_CLASS(tmp, conv_err) case FGW_AUTO: ARG_CONV_CASE_INVALID(tmp, conv_err) } arg->type = target; return 0; } int fgw_arg_conv_to_double(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target) { double tmp; switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(tmp, conv_assign) ARG_CONV_CASE_LLONG(tmp, conv_assign) ARG_CONV_CASE_DOUBLE(tmp, conv_assign) ARG_CONV_CASE_LDOUBLE(tmp, conv_assign) ARG_CONV_CASE_STR(tmp, conv_strtod) ARG_CONV_CASE_PTR(tmp, conv_err) ARG_CONV_CASE_CLASS(tmp, conv_err) case FGW_AUTO: ARG_CONV_CASE_INVALID(tmp, conv_err) } switch(target) { ARG_CONV_CASE_LONG(tmp, conv_err) ARG_CONV_CASE_LLONG(tmp, conv_err) ARG_CONV_CASE_DOUBLE(tmp, conv_rev_assign) ARG_CONV_CASE_LDOUBLE(tmp, conv_err) ARG_CONV_CASE_PTR(tmp, conv_err) ARG_CONV_CASE_STR(tmp, conv_err) ARG_CONV_CASE_CLASS(tmp, conv_err) case FGW_AUTO: ARG_CONV_CASE_INVALID(tmp, conv_err) } arg->type = target; return 0; } int fgw_arg_conv_to_ldouble(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target) { #ifdef FUNGW_CFG_LONG long double tmp; #else double tmp; #endif switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(tmp, conv_assign) ARG_CONV_CASE_LLONG(tmp, conv_assign) ARG_CONV_CASE_DOUBLE(tmp, conv_assign) ARG_CONV_CASE_LDOUBLE(tmp, conv_assign) #ifdef FUNGW_CFG_LONG ARG_CONV_CASE_STR(tmp, conv_strtold) #else ARG_CONV_CASE_STR(tmp, conv_strtod) #endif ARG_CONV_CASE_PTR(tmp, conv_err) ARG_CONV_CASE_CLASS(tmp, conv_err) case FGW_AUTO: ARG_CONV_CASE_INVALID(tmp, conv_err) } switch(target) { ARG_CONV_CASE_LONG(tmp, conv_err) ARG_CONV_CASE_LLONG(tmp, conv_err) ARG_CONV_CASE_DOUBLE(tmp, conv_err) ARG_CONV_CASE_LDOUBLE(tmp, conv_rev_assign) ARG_CONV_CASE_PTR(tmp, conv_err) ARG_CONV_CASE_STR(tmp, conv_err) ARG_CONV_CASE_CLASS(tmp, conv_err) case FGW_AUTO: ARG_CONV_CASE_INVALID(tmp, conv_err) } (void)tmp; arg->type = target; return 0; } int fgw_arg_conv_to_str(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target) { char *s; if (FGW_BASE_TYPE(arg->type) == FGW_STR) { if (!(target & FGW_DYN)) /* want static string, have static or dynamic string */ return 0; s = fgw_strdup(arg->val.str); goto ok; /* have static string, want dynamic */ } s = malloc(128); if (arg->type & FGW_PTR) { if (arg->val.ptr_void == NULL) *s = '\0'; else sprintf(s, "%p", arg->val.ptr_void); goto ok; } switch(FGW_BASE_TYPE(arg->type)) { case FGW_STR: break; case FGW_CHAR: sprintf(s, "%c", arg->val.nat_char); goto ok; case FGW_UCHAR: sprintf(s, "%u", arg->val.nat_uchar); goto ok; case FGW_SCHAR: sprintf(s, "%d", arg->val.nat_schar); goto ok; case FGW_SHORT: sprintf(s, "%d", arg->val.nat_short); goto ok; case FGW_USHORT: sprintf(s, "%u", arg->val.nat_ushort); goto ok; case FGW_INT: sprintf(s, "%d", arg->val.nat_int); goto ok; case FGW_UINT: sprintf(s, "%u", arg->val.nat_uint); goto ok; case FGW_LONG: sprintf(s, "%ld", arg->val.nat_long); goto ok; case FGW_ULONG: sprintf(s, "%lu", arg->val.nat_ulong); goto ok; #ifdef FUNGW_CFG_LONG case FGW_SIZE_T: sprintf(s, "%zd", arg->val.nat_size_t); goto ok; #else case FGW_SIZE_T: sprintf(s, "%ld", (long)arg->val.nat_size_t); goto ok; #endif case FGW_FLOAT: sprintf(s, "%f", arg->val.nat_float); goto ok; case FGW_DOUBLE: sprintf(s, "%f", arg->val.nat_double); goto ok; #ifdef FUNGW_CFG_LONG case FGW_LDOUBLE: sprintf(s, "%d", arg->val.nat_short); goto ok; case FGW_LLONG: sprintf(s, "%lld", arg->val.nat_llong); goto ok; case FGW_ULLONG: sprintf(s, "%llu", arg->val.nat_ullong); goto ok; #endif ARG_CONV_CASE_PTR(arg, conv_break) case FGW_AUTO: ARG_CONV_CASE_INVALID(arg, conv_break) } free(s); return -1; ok:; arg->type = FGW_STR | FGW_DYN; arg->val.str = s; return 0; } /* convert hex pointer string to pointer - can't use strtol(), sizeof(long) may be less than sizeof(void *) */ static ptrdiff_t strtoptr(char *s, char **end) { ptrdiff_t d, v = 0; if ((s[0] == '0') && (s[1] == 'x')) s += 2; for(; *s != '\0'; s++) { if ((*s >= '0') && (*s <= '9')) d = *s - '0'; else if ((*s >= 'a') && (*s <= 'f')) d = *s - 'a' + 10; else if ((*s >= 'A') && (*s <= 'F')) d = *s - 'A' + 10; else break; v = v << 4; v |= d; } if (end != NULL) *end = s; return v; } int fgw_arg_conv_to_ptr(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target) { if (FGW_BASE_TYPE(arg->type) == FGW_STR) { char *end; ptrdiff_t l; l = strtoptr(arg->val.str, &end); if (*end != '\0') return -1; if (arg->type & FGW_DYN) fgw_arg_free_str(ctx, arg); arg->type = target; arg->val.ptr_void = (void *)l; return 0; } if (arg->type & FGW_PTR) { arg->type = target; return 0; } return -1; } #define custom_type_conv \ if ((baset >= FGW_CUSTOM) && (baset < FGW_CUSTOM + FGW_NUM_CUSTOM_TYPES)) { \ if ((ctx->custype == NULL) || (ctx->custype[baset - FGW_CUSTOM].name == NULL) || (ctx->custype[baset - FGW_CUSTOM].arg_conv == NULL)) \ return -1; \ if (ctx->custype[baset - FGW_CUSTOM].arg_conv(ctx, arg, target) != 0) \ return -1; \ if ((arg->type & 0xFF) == target) \ return 0; \ } /* ^^^ fall through: the custom converter converted to some base type - proceed */ int fgw_arg_conv(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target) { int baset = (arg->type & 0xFF); if (arg->type == target) return 0; custom_type_conv; /* convert source custom type to something knonw, if needed */ if (target == FGW_AUTO) return 0; baset = (target & 0xFF); custom_type_conv; /* convert to dest custom type if needed */ if ((target & FGW_STR) == FGW_STR) return fgw_arg_conv_to_str(ctx, arg, target); if (target & FGW_PTR) return fgw_arg_conv_to_ptr(ctx, arg, target); if ((target & 0xFF) < 0x30) return fgw_arg_conv_to_long(ctx, arg, target); if ((target & 0xFF) < 0x40) return fgw_arg_conv_to_llong(ctx, arg, target); if ((target & 0xFF) < 0x50) return fgw_arg_conv_to_double(ctx, arg, target); if ((target & 0xFF) < 0x60) return fgw_arg_conv_to_ldouble(ctx, arg, target); return -1; } fungw-1.2.0/libfungw/fungw.c0000644000175100017510000002244314032261517014155 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2017 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include "fungw.h" #include unsigned int fgw_api_ver = FGW_API_VER; htsp_t fgw_engines; #ifdef FGW_PUPDIRNAME const char *fgw_cfg_pupdir = FGW_PUPDIRNAME; #else const char *fgw_cfg_pupdir = NULL; #endif char *fgw_strdup(const char *s) { int len = strlen(s); char *res; res = malloc(len + 1); memcpy(res, s, len + 1); return res; } void fgw_init(fgw_ctx_t *ctx, const char *name) { htsp_init(&ctx->func_tbl, strhash, strkeyeq); htsp_init(&ctx->obj_tbl, strhash, strkeyeq); htpp_init(&ctx->ptr_tbl, ptrhash, ptrkeyeq); ctx->custype = NULL; if (name != NULL) ctx->name = fgw_strdup(name); else ctx->name = NULL; } void fgw_uninit(fgw_ctx_t *ctx) { htsp_entry_t *e; int n; for (e = htsp_first(&ctx->obj_tbl); e; e = htsp_next(&ctx->obj_tbl, e)) fgw_obj_unreg(ctx, e->value); htsp_uninit(&ctx->func_tbl); htsp_uninit(&ctx->obj_tbl); htpp_uninit(&ctx->ptr_tbl); if (ctx->custype != NULL) { for(n = 0; n < FGW_NUM_CUSTOM_TYPES; n++) free(ctx->custype[n].name); free(ctx->custype); } free(ctx->name); } static int build_path(char *out, fgw_obj_t *obj, const char *name) { int ll, nl = strlen(name); if (nl > FGW_ID_LEN) return -1; if (obj != NULL) { ll = obj->name_len; memcpy(out, obj->name, ll); } else { out[0] = '*'; ll = 1; } out[ll] = '.'; memcpy(out+ll+1, name, nl+1); return 0; } /* Register a new global function in all engines */ static void fgw_func_reg_eng(fgw_ctx_t *ctx, const char *func_name, fgw_func_t *f, int is_short) { htsp_entry_t *e; for (e = htsp_first(&ctx->obj_tbl); e; e = htsp_next(&ctx->obj_tbl, e)) { fgw_obj_t *obj = e->value; if (is_short && (obj == f->obj)) continue; /* do not register the short name functions in its own object - the object should do direct call */ if ((obj->engine != NULL) && (obj->engine->reg_func != NULL)) obj->engine->reg_func(obj, func_name, f); } } fgw_func_t *fgw_func_reg(fgw_obj_t *obj, const char *name, fgw_error_t (*func)(fgw_arg_t *res, int argc, fgw_arg_t *argv)) { char path[2*FGW_ID_LEN+2]; fgw_func_t *f; if (build_path(path, obj, name) != 0) return NULL; if (htsp_get(&obj->func_tbl, name) != NULL) return NULL; f = calloc(sizeof(fgw_func_t), 1); f->name = fgw_strdup(name); f->func = func; f->obj = obj; f->obj_data = NULL; /* local name */ htsp_set(&obj->func_tbl, f->name, f); /* global path */ if (htsp_get(&obj->func_tbl, path) == NULL) { htsp_set(&obj->parent->func_tbl, fgw_strdup(path), f); fgw_func_reg_eng(obj->parent, path, f, 0); } /* global short name */ if (htsp_get(&obj->parent->func_tbl, f->name) == NULL) { htsp_set(&obj->parent->func_tbl, fgw_strdup(f->name), f); fgw_func_reg_eng(obj->parent, f->name, f, 1); } return f; } int fgw_func_unreg(fgw_obj_t *obj, const char *name) { char path[2*FGW_ID_LEN+2]; fgw_func_t *fnc = htsp_get(&obj->func_tbl, name); htsp_entry_t *ep; if (fnc == NULL) return -1; if (build_path(path, obj, name) != 0) return -1; ep = htsp_popentry(&obj->parent->func_tbl, path); if (ep != NULL) { if ((obj->engine != NULL) && (obj->engine->unreg_func != NULL)) obj->engine->unreg_func(obj, path); free(ep->key); } ep = htsp_popentry(&obj->parent->func_tbl, name); if (ep != NULL) { if ((obj->engine != NULL) && (obj->engine->unreg_func != NULL) && (fnc->obj != obj)) /* unreg short names registered by other objects - self-registered ones shouldn't exist because of direct calls */ obj->engine->unreg_func(obj, name); } /* if short global name is us, need to remove there and find a substitute */ if (htsp_get(&obj->parent->func_tbl, name) == fnc) { htsp_entry_t *e; /* look for an alternative and register that */ for (e = htsp_first(&obj->parent->obj_tbl); e; e = htsp_next(&obj->parent->obj_tbl, e)) { fgw_obj_t *l = e->value; fgw_func_t *f = htsp_get(&l->func_tbl, name); if ((f != NULL) && (f->obj != obj)) { htsp_set(&obj->parent->func_tbl, fgw_strdup(name), f); fgw_func_reg_eng(obj->parent, name, f, 1); break; } } } if (ep != NULL) free(ep->key); return 0; } fgw_obj_t *fgw_obj_reg(fgw_ctx_t *ctx, const char *obj_name) { fgw_obj_t *obj; int nl = strlen(obj_name); if (nl > FGW_ID_LEN) return NULL; if (htsp_get(&ctx->obj_tbl, obj_name) != NULL) return NULL; obj = calloc(sizeof(fgw_obj_t), 1); obj->name = fgw_strdup(obj_name); obj->name_len = nl; obj->parent = ctx; htsp_init(&obj->func_tbl, strhash, strkeyeq); htsp_set(&ctx->obj_tbl, obj->name, obj); return obj; } void fgw_obj_unreg(fgw_ctx_t *ctx, fgw_obj_t *obj) { htsp_entry_t *e; for (e = htsp_first(&obj->func_tbl); e; e = htsp_next(&obj->func_tbl, e)) { fgw_func_unreg(obj, e->key); free(e->key); free(e->value); } if ((obj->engine != NULL) && (obj->engine->unreg_func != NULL)) for (e = htsp_first(&ctx->func_tbl); e; e = htsp_next(&ctx->func_tbl, e)) obj->engine->unreg_func(obj, e->key); if ((obj->engine != NULL) && (obj->engine->unload != NULL)) obj->engine->unload(obj); htsp_uninit(&obj->func_tbl); htsp_pop(&ctx->obj_tbl, obj->name); free(obj->name); free(obj); } void fgw_eng_reg(const fgw_eng_t *eng) { if (fgw_engines.table == NULL) htsp_init(&fgw_engines, strhash, strkeyeq); htsp_set(&fgw_engines, eng->name, (void *)eng); } void fgw_eng_unreg(const char *name) { htsp_pop(&fgw_engines, name); } fgw_obj_t *fgw_obj_new2(fgw_ctx_t *ctx, const char *obj_name, const char *eng_name, const char *filename, const char *opts, void *user_call_ctx) { fgw_obj_t *obj; const fgw_eng_t *eng = fgw_engines.table != NULL ? htsp_get(&fgw_engines, eng_name) : NULL; htsp_entry_t *e; if (eng == NULL) return NULL; obj = fgw_obj_reg(ctx, obj_name); if (obj == NULL) return NULL; obj->engine = eng; if ((eng->init != NULL) && (eng->init(obj, filename, opts) != 0)) { free(obj->name); free(obj); return NULL; } if (obj->engine->reg_func != NULL) { for (e = htsp_first(&ctx->func_tbl); e; e = htsp_next(&ctx->func_tbl, e)) { fgw_func_t *func = e->value; if (func->obj != obj) obj->engine->reg_func(obj, e->key, func); } } obj->script_user_call_ctx = user_call_ctx; if ((eng->load != NULL) && (eng->load(obj, filename, opts) != 0)) { fgw_obj_unreg(ctx, obj); return NULL; } obj->script_user_call_ctx = NULL; return obj; } fgw_obj_t *fgw_obj_new(fgw_ctx_t *ctx, const char *obj_name, const char *eng_name, const char *filename, const char *opts) { return fgw_obj_new2(ctx, obj_name, eng_name, filename, opts, NULL); } void fgw_atexit(void) { htsp_uninit(&fgw_engines); } void fgw_async_error(fgw_obj_t *obj, const char *msg) { if ((obj != NULL) && (obj->parent != NULL) && (obj->parent->async_error != NULL)) obj->parent->async_error(obj, msg); } fgw_type_t fgw_reg_custom_type(fgw_ctx_t *ctx, fgw_type_t id, const char *name, int (*arg_conv)(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target), int (*arg_free)(fgw_ctx_t *ctx, fgw_arg_t *arg)) { if (ctx->custype == NULL) ctx->custype = calloc(sizeof(fgw_custype_t), FGW_NUM_CUSTOM_TYPES); /* find a suitable id */ if (id == FGW_INVALID) { for(id = 0; id < FGW_NUM_CUSTOM_TYPES; id++) if (ctx->custype[id].name == NULL) break; if (id == FGW_NUM_CUSTOM_TYPES) return FGW_INVALID; } else { if ((id < FGW_CUSTOM) || (id >= FGW_CUSTOM + FGW_NUM_CUSTOM_TYPES) || (ctx->custype[id].name != NULL)) return FGW_INVALID; id -= FGW_CUSTOM; } ctx->custype[id].name = fgw_strdup(name); ctx->custype[id].arg_conv = arg_conv; ctx->custype[id].arg_free = arg_free; return id + FGW_CUSTOM; } int fgw_unreg_custom_type(fgw_ctx_t *ctx, fgw_type_t id) { if ((id < FGW_CUSTOM) || (id >= FGW_CUSTOM + FGW_NUM_CUSTOM_TYPES) || (ctx->custype[id].name == NULL)) return -1; free(ctx->custype[id].name); ctx->custype[id].name = NULL; ctx->custype[id].arg_conv = NULL; return 0; } int fgw_test_parse_fn(const char *filename, const char **endings) { size_t len, maxlen = strlen(filename); const char *end = filename + maxlen; for(; *endings != NULL; endings++) { len = strlen(*endings); if ((len < maxlen-1) && (strcmp(*endings, end - len) == 0)) return 1; } return 0; } const char *fgw_engine_find(const char *fn, FILE *f) { const fgw_eng_t *eng; htsp_entry_t *e; if (fgw_engines.table == NULL) return NULL; for(e = htsp_first(&fgw_engines); e != NULL; e = htsp_next(&fgw_engines, e)) { eng = e->value; if (eng->test_parse != NULL) { if (f != NULL) rewind(f); if (eng->test_parse(fn, f) == 1) return e->key; } } return NULL; } fungw-1.2.0/libfungw/fungw_ptr.c0000644000175100017510000000311313135304624015033 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2017 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ /* track pointers */ #include #include "fungw.h" void fgw_ptr_reg(fgw_ctx_t *ctx, fgw_arg_t *res, const char *ptr_domain, fgw_type_t ptr_type, void *ptr) { res->type = ptr_type | FGW_PTR; res->val.ptr_void = ptr; if (ptr != NULL) htpp_set(&ctx->ptr_tbl, ptr, (void *)ptr_domain); } void fgw_ptr_unreg(fgw_ctx_t *ctx, fgw_arg_t *res, const char *ptr_domain) { htpp_pop(&ctx->ptr_tbl, res->val.ptr_void); res->type = FGW_VOID; res->val.ptr_void = NULL; } int fgw_ptr_in_domain(fgw_ctx_t *ctx, fgw_arg_t *ptr, const char *ptr_domain) { if (!(ptr->type & FGW_PTR)) return 0; return htpp_get(&ctx->ptr_tbl, ptr->val.ptr_void) == ptr_domain; } fungw-1.2.0/Changelog0000644000175100017510000000525614047742734012676 0ustar svnsvnfungw 1.2.0 (r751) ~~~~~~~~~~~~~~~~~~ [core] -Fix: multi-call needs to build a temporary list of functions to call before doing the calls: the hash may be changed by the call (e.g. fgwirc load action) [doc] -Add: more scripting languages considered -Fix: C engine example: functions should be all static to avoid namespace pollution -Fix: link releases with full url so packed up doc won't break -Fix: use full hostname for svn and web URLs [fawk] -Fix: import upstream fix: return statement in vararg function messed up the stack in language fawk [mujs] -Add: mujs binding [picol] -Add: picol binding (minimal tcl) fungw 1.1.1 (r685) ~~~~~~~~~~~~~~~~~~ [bindings] -Add: upgrade the C binding to a full engine of its own right [build] -Fix: print the full path of Makefile being generated -Fix: run ranlib on the .a -Rename: example/ to regression/ - it's too complex to serve as an example -Add: make recuses to regression [core] -Fix: do not crash on engine lookup if there is no engine registered - just return NULL [doc] -Add: better/shorter explanation of objects -Add: model drawing shows the per context function hash -Add: 7 more languages that can not be used -Add: unsupported language: matlab with liboctave: C++-only -Add: new dir doc/example for hosting a new set of more user digestable examples [regression] -Fix: multicall test: missing sentinel/terminator 0 in vcall_all fungw 1.1.0 (r612) ~~~~~~~~~~~~~~~~~~ [core] -Fix: plain old function call: init local res to INVALID before the call to avoid freeing uninitialized arg after the call if the call did not set it -Fix: fgw_obj_new() shouldn't crash but return NULL if allocating the new object fails (e.g. id is already in use) -Add: FGW_AUTO type: script bindings need to convert custom type to any base type to be able to handle them -Add: default 'script file name extension' field in the engine struct (useful for saving new scripts) -Add: publish fungw pup installation dir so the host app can add it to puplug search paths -Add: fgw_obj_new2(): a version of fgw_obj_new() that passes on "script user call ctx", so that script initialization section may make calls with context [fawk] -Fix: don't crash when converting NULL string to fawk string literal, use empty string -Update: new libfawk_error() API for delivering line number for runtime errors [bindings] -Fix: make sure all script bindings convert custom type to FGW_AUTO (instead of FGW_STR or not converting at all) before passing it to the script -Add: $script-ext in pups of all bindings (so an app can decide whether to load an engine or not) -Add: $lang-alias in pups: language aliases for duktape, estutter and mruby fungw-1.2.0/libfungwbind/0000755000175100017510000000000014047742763013530 5ustar svnsvnfungw-1.2.0/libfungwbind/cli/0000755000175100017510000000000014047742763014277 5ustar svnsvnfungw-1.2.0/libfungwbind/cli/Plug.tmpasm0000644000175100017510000000155213323015002016403 0ustar svnsvnput /local/fungw/mod {fungw_cli} put /local/fungw/mod_dir {cli} put /local/fungw/cquote {sh_head} put /local/fungw/mod_distclean_files {os_dep.h} # need both fork and pipe put /local/fungw/cli_ok {true} if ?libs/proc/fork/presents then else put /local/fungw/cli_ok {false} end if ?libs/io/pipe/presents then else put /local/fungw/cli_ok {false} end switch ?libs/proc/fork/presents case {true} put /local/fungw/mod_src {fungw_cli.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end put /local/fungw/tmp libs/proc/fork/includes append /local/fungw/tmp libs/io/pipe/includes gsub /local/fungw/tmp {\\\\n *} {\n} #uniq /local/fungw/tmp /local/fungw/tmp {#define} {#include} redir [@@/local/fungw/mod_dir@/os_dep.h@] print [~/*** Automatically generated by scconfig, do not edit. Source: cli/Plug.tmpasm ***/ ~/local/fungw/tmp~ ~] redir fungw-1.2.0/libfungwbind/cli/fungw_cli.pup0000644000175100017510000000015213757662517017005 0ustar svnsvn$desc cli binding engine $state works $script-ext cli .sh $lang-alias cli bash default buildin autoload 0 fungw-1.2.0/libfungwbind/cli/sh_head0000644000175100017510000000154413561336454015615 0ustar svnsvn### header pasted by fungw fgw_ok() { echo "ok" >&4 } fgw_func_reg() { local res echo "fgw_func_reg $1" >&4 read res <&3 if test "$res" = "fr_ok" then return 0 else return 1 fi } fgw() { local fn cmd arg echo "call_begin $#" >&4 fn="$1" shift 1 while test $# -gt 0 do #TODO: quote echo "call_arg $1" >&4 shift 1 done echo "call_end $fn" >&4 # read the result read cmd arg <&3 if test "$cmd" = "retok" then echo "$arg" return 0 else echo "" return 1 fi } fgw_main_loop() { local cmd arg call fgw_ok while read cmd arg <&3 do case "$cmd" in call_begin) call="" ;; call_arg) call="$call \"$arg\"" ;; call_end) call="$arg$call" fgw_retval="" echo "call=$call" eval "$call" if test $? = 0 then echo "retok $fgw_retval" >&4 else echo "retfail" >&4 fi ;; esac done } fungw-1.2.0/libfungwbind/cli/sh_head.h0000644000175100017510000000771113561336454016045 0ustar svnsvn/* Autogenerated by cquote.c - DO NOT EDIT */ /* (Using character array instead of string literal for long strings) */ static const char sh_head_arr[] = { '#','#','#',' ','h','e','a','d','e','r',' ','p','a','s','t','e', 'd',' ','b','y',' ','f','u','n','g','w','\n', 'f','g','w','_','o','k','(',')','\n', '{','\n', '\t','e','c','h','o',' ','"','o','k','"',' ','>','&','4','\n', '}','\n', '\n', 'f','g','w','_','f','u','n','c','_','r','e','g','(',')','\n', '{','\n', '\t','l','o','c','a','l',' ','r','e','s','\n', '\t','e','c','h','o',' ','"','f','g','w','_','f','u','n','c', '_','r','e','g',' ','$','1','"',' ','>','&','4','\n', '\t','r','e','a','d',' ','r','e','s',' ','<','&','3','\n', '\t','i','f',' ','t','e','s','t',' ','"','$','r','e','s','"', ' ','=',' ','"','f','r','_','o','k','"','\n', '\t','t','h','e','n','\n', '\t','\t','r','e','t','u','r','n',' ','0','\n', '\t','e','l','s','e','\n', '\t','\t','r','e','t','u','r','n',' ','1','\n', '\t','f','i','\n', '}','\n', '\n', 'f','g','w','(',')','\n', '{','\n', '\t','l','o','c','a','l',' ','f','n',' ','c','m','d',' ','a', 'r','g','\n', '\n', '\t','e','c','h','o',' ','"','c','a','l','l','_','b','e','g', 'i','n',' ','$','#','"',' ','>','&','4','\n', '\t','f','n','=','"','$','1','"','\n', '\t','s','h','i','f','t',' ','1','\n', '\t','w','h','i','l','e',' ','t','e','s','t',' ','$','#',' ', '-','g','t',' ','0','\n', '\t','d','o','\n', '#','T','O','D','O',':',' ','q','u','o','t','e','\n', '\t','\t','e','c','h','o',' ','"','c','a','l','l','_','a','r', 'g',' ','$','1','"',' ','>','&','4','\n', '\t','\t','s','h','i','f','t',' ','1','\n', '\t','d','o','n','e','\n', '\t','e','c','h','o',' ','"','c','a','l','l','_','e','n','d', ' ','$','f','n','"',' ','>','&','4','\n', '\n', '\t','#',' ','r','e','a','d',' ','t','h','e',' ','r','e','s', 'u','l','t','\n', '\t','r','e','a','d',' ','c','m','d',' ','a','r','g',' ','<', '&','3','\n', '\t','i','f',' ','t','e','s','t',' ','"','$','c','m','d','"', ' ','=',' ','"','r','e','t','o','k','"','\n', '\t','t','h','e','n','\n', '\t','\t','e','c','h','o',' ','"','$','a','r','g','"','\n', '\t','\t','r','e','t','u','r','n',' ','0','\n', '\t','e','l','s','e','\n', '\t','\t','e','c','h','o',' ','"','"','\n', '\t','\t','r','e','t','u','r','n',' ','1','\n', '\t','f','i','\n', '}','\n', '\n', '\n', 'f','g','w','_','m','a','i','n','_','l','o','o','p','(',')','\n', '{','\n', '\t','l','o','c','a','l',' ','c','m','d',' ','a','r','g',' ', 'c','a','l','l','\n', '\t','f','g','w','_','o','k','\n', '\t','w','h','i','l','e',' ','r','e','a','d',' ','c','m','d', ' ','a','r','g',' ','<','&','3','\n', '\t','d','o','\n', '\t','\t','c','a','s','e',' ','"','$','c','m','d','"',' ','i', 'n','\n', '\t','\t','\t','c','a','l','l','_','b','e','g','i','n',')',' ', 'c','a','l','l','=','"','"',' ',';',';','\n', '\t','\t','\t','c','a','l','l','_','a','r','g',')',' ','c','a', 'l','l','=','"','$','c','a','l','l',' ','\\','"','$','a','r', 'g','\\','"','"',' ',';',';','\n', '\t','\t','\t','c','a','l','l','_','e','n','d',')','\n', '\t','\t','\t','\t','c','a','l','l','=','"','$','a','r','g','$', 'c','a','l','l','"','\n', '\t','\t','\t','\t','f','g','w','_','r','e','t','v','a','l','=', '"','"','\n', '\t','\t','\t','\t','e','c','h','o',' ','"','c','a','l','l','=', '$','c','a','l','l','"','\n', '\t','\t','\t','\t','e','v','a','l',' ','"','$','c','a','l','l', '"','\n', '\t','\t','\t','\t','i','f',' ','t','e','s','t',' ','$','?',' ', '=',' ','0','\n', '\t','\t','\t','\t','t','h','e','n','\n', '\t','\t','\t','\t','\t','e','c','h','o',' ','"','r','e','t', 'o','k',' ','$','f','g','w','_','r','e','t','v','a','l','"',' ', '>','&','4','\n', '\t','\t','\t','\t','e','l','s','e','\n', '\t','\t','\t','\t','\t','e','c','h','o',' ','"','r','e','t', 'f','a','i','l','"',' ','>','&','4','\n', '\t','\t','\t','\t','f','i','\n', '\t','\t','\t','\t',';',';','\n', '\t','\t','e','s','a','c','\n', '\t','d','o','n','e','\n', '}','\n', 0}; static const char *sh_head = sh_head_arr; fungw-1.2.0/libfungwbind/cli/fungw_cli.c0000644000175100017510000003504013757344474016426 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2018, 2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include #include #include "os_dep.h" #include #include #include #include #include #include typedef struct { const char *name; const char *shebang, *execflag; const char *head, *foot; } fgws_cli_lang_t; #include "sh_head.h" static const char sh_foot[] = "\nfgw_main_loop\n"; static const fgws_cli_lang_t langs[] = { {"shell", "/bin/sh", "-c", sh_head_arr, sh_foot}, {"bash", "/bin/bash", "-c", sh_head_arr, sh_foot}, {"dash", "/bin/dash", "-c", sh_head_arr, sh_foot}, {"shell", "/usr/bin/sh", "-c", sh_head_arr, sh_foot}, {"bash", "/usr/bin/bash", "-c", sh_head_arr, sh_foot}, {"dash", "/usr/bin/dash", "-c", sh_head_arr, sh_foot}, {NULL, NULL, NULL, NULL, NULL} }; typedef struct { fgw_obj_t *obj; int fd_c2s[2]; /* c->script pipe; c writes [1], script reads [0] */ int fd_s2c[2]; /* c->script pipe; c reads [0], script writes [1] */ pid_t pid; int running; /* whether the background process is running */ /* read buffer */ char buf[256]; int buf_fill, bufp; /* name of the script being executed */ char scr_name[L_tmpnam]; } cli_ctx_t; static fgw_error_t fgws_cli_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv); /* buffered read on the control pipe */ static int cli_getc(cli_ctx_t *ctx) { if (ctx->bufp >= ctx->buf_fill) { ctx->buf_fill = read(ctx->fd_s2c[0], ctx->buf, sizeof(ctx->buf)); if (ctx->buf_fill <= 0) return ctx->buf_fill; ctx->bufp = 0; } return ctx->buf[ctx->bufp++]; } /* blocking write all buf */ static ssize_t cli_write_(int fd, const char *buf, ssize_t len) { ssize_t orig = len; while(len > 0) { ssize_t l; l = write(fd, buf, len); if (l <= 0) return -1; buf += l; len -= l; } return orig; } static ssize_t cli_write(cli_ctx_t *ctx, const char *buf, ssize_t len) { return cli_write_(ctx->fd_c2s[1], buf, len); } /* print small (<1k) formatted strings */ static void cli_printf(cli_ctx_t *ctx, const char *fmt, ...) { char tmp[1024]; size_t len; va_list ap; va_start(ap, fmt); len = vsprintf(tmp, fmt, ap); va_end(ap); cli_write(ctx, tmp, len); } static int cli_got_eof(cli_ctx_t *ctx, ssize_t len, int err_no) { /* later, for nonblocking: if ((len == 0) && (err_no == EAGAIN)) { usleep? return 0; } */ if (len <= 0) { ctx->running = 0; return 1; } return 0; } static int cli_read_fgw_func_reg(cli_ctx_t *ctx, char *buf, int maxlen) { int bl = 0; fgw_func_t *func; for(;;) { int c; /* read the function name */ c = cli_getc(ctx); if (cli_got_eof(ctx, c, errno)) return -1; if ((c == '\n') || (c == '\r')) break; buf[bl] = c; bl++; if (bl >= maxlen) return -1; } buf[bl] = '\0'; func = fgw_func_reg(ctx->obj, buf, fgws_cli_call_script); if (func == NULL) { fgw_async_error(ctx->obj, "fgw_func_reg: failed to register function\n"); fgw_async_error(ctx->obj, buf); fgw_async_error(ctx->obj, "\n"); cli_write(ctx, "fr_err\n", 7); } cli_write(ctx, "fr_ok\n", 6); return 0; } static int cli_read_fgw_arg(cli_ctx_t *ctx, fgw_arg_t *arg) { int used = 0, alloced = 256, grow = 2048, limit=1024*1024; arg->type = FGW_STR | FGW_DYN; arg->val.str = malloc(alloced); for(;;) { int c; /* read the function name */ c = cli_getc(ctx); if (cli_got_eof(ctx, c, errno)) { free(arg->val.str); arg->type = 0; return -1; } if ((c == '\n') || (c == '\r')) break; if (used >= alloced) { alloced += grow; if (alloced > limit) { free(arg->val.str); arg->type = 0; return -1; } arg->val.str = realloc(arg->val.str, alloced+1); } arg->val.str[used] = c; used++; } arg->val.str[used] = '\0'; return 0; } static int cli_read_fgw_int(cli_ctx_t *ctx, int *dst) { long tmp = 0; int valid = 0; for(;;) { int c; /* read the function name */ c = cli_getc(ctx); if (cli_got_eof(ctx, c, errno)) return -1; if ((c == '\n') || (c == '\r')) break; if (isdigit(c)) { tmp *= 10; tmp += c - '0'; valid = 1; } else { valid = 0; break; } } *dst = tmp; return !valid; } #define cli_wait_ok_free_arg() \ do { \ int n; \ if (argv != NULL) { \ for (n = 1; n < argc; n++) {\ if (argv[n].type == (FGW_STR | FGW_DYN)) \ free(argv[n].val.str); \ argv[n].type = 0; \ } \ free(argv); \ argv = NULL; \ } \ argp = argc = -1; \ argv_alloced = 0; \ } while(0) static int cli_wait_ok(cli_ctx_t *ctx, fgw_arg_t *ret) { char buf[1024]; int bl = 0; int argp = -1, argc = -1; int argv_alloced = 0; fgw_arg_t *argv = NULL; for(;;) { int c; /* read the command */ c = cli_getc(ctx); if (cli_got_eof(ctx, c, errno)) return -1; if (isspace(c)) { buf[bl] = '\0'; if (buf[0] == '\0') continue; if ((buf[0] == 'o') && (buf[1] == 'k')) { if (ret != NULL) { fprintf(stderr, "cli_wait_ok(): got 'ok' while waiting for a function return\n"); goto err; } goto done; } else if (strcmp(buf, "call_begin") == 0) { if (cli_read_fgw_int(ctx, &argc) != 0) { fprintf(stderr, "cli_wait_ok(): invalid call_beign argc: %s\n", buf+11); goto err; } if (argc > 1024) { fprintf(stderr, "cli_wait_ok(): argc too high: %s\n", buf+11); goto err; } if (argc > argv_alloced) { argv_alloced = argc; argv = realloc(argv, argv_alloced * sizeof(fgw_arg_t)); } argp = 1; bl = 0; } else if (strcmp(buf, "call_arg") == 0) { if (argc < 0) { fprintf(stderr, "cli_wait_ok(): invalid call_arg without call_begin\n"); goto err; } if (argp >= argc) { fprintf(stderr, "cli_wait_ok(): invalid call_arg: too many arguments\n"); goto err; } cli_read_fgw_arg(ctx, &argv[argp]); argp++; bl = 0; } else if (strcmp(buf, "call_end") == 0) { fgw_arg_t fn, res; fgw_func_t *func; fgw_error_t ferr; if (argp != argc) { fprintf(stderr, "cli_wait_ok(): invalid call_end: number of args mismatch: %d != %d\n", argp, argc); goto err; } cli_read_fgw_arg(ctx, &fn); func = fgw_func_lookup(ctx->obj->parent, fn.val.str); if (func == NULL) { fprintf(stderr, "cli_wait_ok(): invalid call_end: unknown function '%s'\n", fn.val.str); free(fn.val.str); goto err; } free(fn.val.str); argv[0].type = FGW_FUNC; argv[0].val.argv0.func = func; argv[0].val.argv0.user_call_ctx = ctx->obj->script_user_call_ctx; res.type = FGW_PTR; res.val.ptr_void = NULL; ferr = func->func(&res, argc, argv); fgw_argv_free(ctx->obj->parent, argc, argv); cli_wait_ok_free_arg(); if ((ferr == 0) && (fgw_arg_conv(ctx->obj->parent, &res, FGW_STR | FGW_DYN) == 0)) { cli_write(ctx, "retok ", 6); cli_write(ctx, res.val.str, strlen(res.val.str)); cli_write(ctx, "\n", 1); } else cli_write(ctx, "retfail\n", 8); if (res.type == (FGW_STR | FGW_DYN)) free(res.val.str); bl = 0; } else if (strcmp(buf, "retok") == 0) { if (ret == NULL) { fprintf(stderr, "cli_wait_ok(): got 'retok' while NOT waiting for a function return\n"); goto err; } if (cli_read_fgw_arg(ctx, ret) < 0) goto err; goto done; } else if (strcmp(buf, "retfail") == 0) { if (ret == NULL) { fprintf(stderr, "cli_wait_ok(): got 'retfail' while NOT waiting for a function return\n"); goto err; } goto ret1; } else if (strcmp(buf, "fgw_func_reg") == 0) { if (cli_read_fgw_func_reg(ctx, buf, sizeof(buf)) < 0) goto err; bl = 0; } else { fprintf(stderr, "cli_wait_ok(): Invalid request from the script: '%s'\n", buf); goto err; } } else { buf[bl] = c; bl++; if (bl >= sizeof(buf)) goto err; } } done:; cli_wait_ok_free_arg(); return 0; ret1:; cli_wait_ok_free_arg(); return 1; err:; cli_wait_ok_free_arg(); return -1; } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_cli_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { /* Do not register functions in the script, in script->c direction fgw calls are wrapped due to syntax limitations. */ } /* API: fgw calls a cli function */ static fgw_error_t fgws_cli_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; cli_ctx_t *ctx = obj->script_data; int n, rv; res->type = FGW_PTR; res->val.ptr_void = NULL; fgws_ucc_save(obj); cli_printf(ctx, "call_begin %d\n", argc-1); for(n = 1; n < argc; n++) { cli_write(ctx, "call_arg ", 9); fgw_arg_conv(obj->parent, &argv[n], FGW_STR | FGW_DYN); #warning TODO: protect against \n cli_write(ctx, argv[n].val.str, strlen(argv[n].val.str)); cli_write(ctx, "\n", 1); } cli_printf(ctx, "call_end %s\n", argv[0].val.func->name); rv = cli_wait_ok(ctx, res); fgws_ucc_restore(obj); for (n = 1; n < argc; n++) { if (argv[n].type == (FGW_STR | FGW_DYN)) { argv[n].type = 0; free(argv[n].val.str); } } if (rv == 0) return FGW_SUCCESS; if (res->type == (FGW_STR | FGW_DYN)) { res->type = 0; free(res->val.str); } return FGW_ERR_UNKNOWN; } /* API: unload the script */ static int fgws_cli_unload(fgw_obj_t *obj) { cli_ctx_t *ctx = obj->script_data; if ((ctx->running) && (ctx->pid > 1)) kill(ctx->pid, SIGTERM); if (*ctx->scr_name != '\0') remove(ctx->scr_name); close(ctx->fd_c2s[0]); close(ctx->fd_c2s[1]); close(ctx->fd_s2c[0]); close(ctx->fd_s2c[1]); free(ctx); return 0; } /* API: init the interpreter so that functions can be registered */ static int fgws_cli_init(fgw_obj_t *obj, const char *filename, const char *opts) { cli_ctx_t *ctx = malloc(sizeof(cli_ctx_t)); obj->script_data = ctx; ctx->obj = obj; ctx->pid = -1; ctx->buf_fill = ctx->bufp = 0; *ctx->scr_name = '\0'; if (pipe(ctx->fd_c2s) != 0) { free(ctx); return -1; } if (pipe(ctx->fd_s2c) != 0) { close(ctx->fd_c2s[0]); close(ctx->fd_c2s[1]); free(ctx); return -1; } return 0; } /* API: load a script into an object */ static int fgws_cli_load(fgw_obj_t *obj, const char *filename, const char *opts) { cli_ctx_t *ctx = obj->script_data; char shebang[2048], *shb, buf[1024]; FILE *f; const fgws_cli_lang_t *l; int fd; /* figure language from shebang */ f = fopen(filename, "r"); if (f == NULL) { fprintf(stderr, "fgws_cli_load: can't open '%s' for read\n", filename); fclose(f); return -1; } shb = fgets(shebang, sizeof(shebang)-1, f); if (shb == NULL) { fprintf(stderr, "fgws_cli_load: unable to load shebang from %s\n", filename); fclose(f); return -1; } if ((shb[0] != '#') && (shb[0] != '!')) { fprintf(stderr, "fgws_cli_load: invalid shebang prefix in %s\n", filename); fclose(f); return -1; } shb+=2; while(isspace(*shb)) shb++; for(l = langs; l->name != NULL; l++) if (strncmp(l->shebang, shb, strlen(l->shebang)) == 0) break; if (l->name == NULL) { fprintf(stderr, "fgws_cli_load: unrecognized shebang in %s: '%s'\n", filename, shb); fclose(f); return -1; } /* write the temp file with header, script and footer */ /* NOTE: tmpnam is the only portable way to do this; O_EXCL below should guarantee it's safe. There's a worse, unavoidable race when we later exec the interpreter on the file, by name. */ if (tmpnam(ctx->scr_name) == NULL) { fprintf(stderr, "fgws_cli_load: failed to create temp file\n"); fclose(f); return -1; } fd = open(ctx->scr_name, O_WRONLY | O_EXCL | O_CREAT, 0600); if (fd < 0) { fprintf(stderr, "fgws_cli_load: failed to create temp file '%s'\n", ctx->scr_name); fclose(f); return -1; } cli_write_(fd, l->head, strlen(l->head)); while(!feof(f)) { int len = fread(buf, 1, sizeof(buf), f); if (len > 0) cli_write_(fd, buf, len); } cli_write_(fd, l->foot, strlen(l->foot)); close(fd); fclose(f); /* fork and execute the interpreter */ ctx->pid = fork(); if (ctx->pid == 0) { int fd; /* child */ close(ctx->fd_c2s[1]); close(ctx->fd_s2c[0]); if (ctx->fd_c2s[0] != 3) { close(3); fd = dup2(ctx->fd_c2s[0], 3); if (fd != 3) { fprintf(stderr, "Can't dup2 script input on fd 3: %d %s\n", fd, strerror(errno)); exit(1); } } if (ctx->fd_s2c[1] != 4) { close(4); fd = dup2(ctx->fd_s2c[1], 4); if (fd != 4) { fprintf(stderr, "Can't dup2 script input on fd 4: %d\n", fd); exit(1); } } for(fd = 5; fd < 1024; fd++) close(fd); execl(l->shebang, l->execflag, ctx->scr_name, NULL); exit(1); } ctx->running = 1; close(ctx->fd_c2s[0]); close(ctx->fd_s2c[1]); cli_wait_ok(ctx, NULL); return 0; } static int fgws_cli_test_parse(const char *filename, FILE *f) { const char *exts[] = {".sh", ".bash", NULL }; const char *shebang[] = {"sh", "bash", "dash", NULL }; if (f != NULL) { char line[128], *s; const char **sh; s = fgets(line, sizeof(line), f); if ((s != NULL) && (s[0] == '#') && (s[1] == '!')) { s += 2; while(isspace(*s)) s++; if (strncmp(s, "/usr", 4) == 0) s += 4; if (strncmp(s, "/opt", 4) == 0) s += 4; if (strncmp(s, "/local", 6) == 0) s += 6; if (strncmp(s, "/bin", 4) == 0) s += 4; for(sh = shebang; *sh != NULL; sh++) if (strncmp(*sh, s, strlen(*sh)) == 0) return 1; } } return fgw_test_parse_fn(filename, exts); } /* API: engine registration */ static const fgw_eng_t fgw_cli_eng = { "cli", fgws_cli_call_script, fgws_cli_init, fgws_cli_load, fgws_cli_unload, fgws_cli_reg_func, NULL, fgws_cli_test_parse, ".sh" }; int pplg_check_ver_fungw_cli(int version_we_need) { return 0; } int pplg_init_fungw_cli(void) { (void)sh_head; /* suppress warning on unised var */ fgw_eng_reg(&fgw_cli_eng); return 0; } void pplg_uninit_fungw_cli(void) { fgw_eng_unreg(fgw_cli_eng.name); } fungw-1.2.0/libfungwbind/libfungwbind.mak.in0000644000175100017510000000240613430212373017263 0ustar svnsvnprint {# Generated by ./configure - DO NOT EDIT\n} print {# Makefile include for direct linking fungw bindings (not using puplug)\n\n} #put /local/fungw/binding_cflags {} put /local/fungw/binding_ldlibs {} put /local/fungw/binding_srcliba {} foreach /local/fungw/n in /local/fungw/bindings_all put /local/sdisable [@/local/fungw/binding/static-disable/@/local/fungw/n@@] resolve /local/sdisablev /local/sdisable if ?/local/sdisablev then print [@# @/local/fungw/n@: disabled by fungw scconfig_hooks.h @] else print [@include $(FUNGWBIND)/@/local/fungw/n@/fungw_@/local/fungw/n@.mak @] # append /local/fungw/binding_cflags [@ $(CFLAGS_fungw_@/local/fungw/n@)@] append /local/fungw/binding_ldlibs [@ $(LDFLAGS_fungw_@/local/fungw/n@)@] append /local/fungw/binding_srcliba [@ $(SRCLIBA_fungw_@/local/fungw/n@)@] end end # CFLAGS for compiling the binding libs - do not use in the host app # FUNGWBIND_CFLAGS = @/local/fungw/binding_cflags@ print [@ # LDLIBS to be used for linking a host app when bindings are directly # dynamic linked (not loaded using puplug) FUNGWBIND_LDLIBS = @/local/fungw/binding_ldlibs@ # List of .a files to be linked when all bindings are directly static # linked into the application FUNGWBIND_SRCLIBA = @/local/fungw/binding_srcliba@ @] fungw-1.2.0/libfungwbind/Makefile.in0000644000175100017510000000073113512102055015552 0ustar svnsvnprint {# Generated by ./configure - DO NOT EDIT\n\n} foreach /local/fungw/task in {all clean install linstall uninstall} print [~~/local/fungw/task~:~] {\n} foreach /local/fungw/n in /local/fungw/bindings_all print [~ cd ~/local/fungw/n~ && make ~/local/fungw/task~~] {\n} end print {\n} end print {distclean:\n} foreach /local/fungw/n in /local/fungw/bindings_all print [~ cd ~/local/fungw/n~ && make distclean~] {\n} end print { rm libfungwbind.mak Makefile\n} fungw-1.2.0/libfungwbind/estutter/0000755000175100017510000000000014047742763015407 5ustar svnsvnfungw-1.2.0/libfungwbind/estutter/Plug.tmpasm0000644000175100017510000000055713302030063017520 0ustar svnsvnput /local/fungw/mod {fungw_estutter} put /local/fungw/mod_dir {estutter} switch ?libs/script/estutter/presents case {true} put /local/fungw/mod_cflags libs/script/estutter/cflags put /local/fungw/mod_ldflags libs/script/estutter/ldflags put /local/fungw/mod_src {fungw_estutter.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/estutter/fungw_estutter.pup0000644000175100017510000000022413757657675021236 0ustar svnsvn$desc estutter binding engine $state works $script-ext estutter .stt $script-ext estutter .estt $lang-alias estutter stt default buildin autoload 0 fungw-1.2.0/libfungwbind/estutter/fungw_estutter.c0000644000175100017510000002123413757344474020646 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2018, 2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include #include typedef struct fctx_s fctx_t; struct fctx_s { fctx_t *next; /* linked list of all names so they can be freed */ char name[1]; /* dynamic allocation */ }; typedef struct { fgw_obj_t *obj; stt_ctx_t stt; stt_varctx_t *vctx; fctx_t *fl; /* list of functions */ } estutter_t; static void fgws_stt_obj2arg(fgw_arg_t *dst, const stt_obj_t *src) { switch (src->type) { case STT_ST_NIL: dst->type = FGW_PTR; dst->val.ptr_void = NULL; break; case STT_ST_INT: dst->type = FGW_LONG; dst->val.nat_long = src->d.integer.value; break; case STT_ST_NUM: dst->type = FGW_DOUBLE; dst->val.nat_double = src->d.num.value; break; case STT_ST_CHAR: dst->type = FGW_INT; dst->val.nat_int = src->d.ch.ch; break; case STT_ST_T: dst->type = FGW_INT; dst->val.nat_int = 1; break; case STT_ST_STRING: dst->type = FGW_STR; dst->val.str = src->d.string.value; break; default: dst->type = FGW_PTR; dst->val.ptr_void = NULL; } } static stt_obj_t *fgws_stt_arg2obj(fgw_ctx_t *fctx, stt_ctx_t *stt, fgw_arg_t *arg) { # define FGW_STT_RET_LONG(lst, val) return stt_create_int(stt, val); # define FGW_STT_RET_DOUBLE(lst, val) return stt_create_num(stt, val); # define FGW_STT_RET_PTR(lst, val) return stt_create_int(stt, (stt_intptr_t)val); # define FGW_STT_RET_STR(lst, val) return stt_create_string(stt, val); # define FGW_STT_RET_NIL(lst, val) return stt_create_nil(stt); if (FGW_IS_TYPE_CUSTOM(arg->type)) fgw_arg_conv(fctx, arg, FGW_AUTO); /* if fails, it remains custom and will be unhandled */ switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(NULL, FGW_STT_RET_LONG); ARG_CONV_CASE_LLONG(NULL, FGW_STT_RET_DOUBLE); ARG_CONV_CASE_DOUBLE(NULL, FGW_STT_RET_DOUBLE); ARG_CONV_CASE_LDOUBLE(NULL, FGW_STT_RET_DOUBLE); ARG_CONV_CASE_PTR(NULL, FGW_STT_RET_PTR); ARG_CONV_CASE_STR(NULL, FGW_STT_RET_STR); ARG_CONV_CASE_CLASS(NULL, FGW_STT_RET_NIL); ARG_CONV_CASE_INVALID(NULL, FGW_STT_RET_NIL); } if (arg->type & FGW_PTR) { FGW_STT_RET_PTR(NULL, arg->val.ptr_void); } else FGW_STT_RET_NIL(NULL, 0); } /* API: the script is calling an fgw function */ static STT_BUILTIN(fgws_stt_call_fgw) { estutter_t *est = ctx->stt->user_data; fctx_t *fctx = data; /* fctx->name */ fgw_func_t *func; int argc, n; fgw_arg_t res, *argv, argv_static[16]; fgw_error_t err; stt_obj_t *sres, *sobj; func = fgw_func_lookup(est->obj->parent, fctx->name); if (func == NULL) { fgw_async_error(est->obj, "fgws_stt_call_fgw: function to be called is not found:"); fgw_async_error(est->obj, fctx->name); fgw_async_error(est->obj, "\n"); return STT_NIL(ctx->stt); } for(argc = 0, sobj = parm; sobj->type == STT_ST_CONS; sobj = sobj->d.cons.cdr) argc++; if ((argc + 1) > (sizeof(argv_static) / sizeof(argv_static[0]))) argv = malloc((argc + 1) * sizeof(fgw_arg_t)); else argv = argv_static; /* Set the first param */ argv[0].type = FGW_FUNC; argv[0].val.argv0.func = func; argv[0].val.argv0.user_call_ctx = est->obj->script_user_call_ctx; /* Convert all params to fgw args */ for (n = 0; n < argc; n++) { stt_nextarg(ctx, &sobj, &parm); fgws_stt_obj2arg(&argv[n+1], sobj); } /* Call the target function */ res.type = FGW_PTR; res.val.ptr_void = NULL; err = func->func(&res, argc+1, argv); /* Free the array */ fgw_argv_free(est->obj->parent, argc, argv); if (argv != argv_static) free(argv); if (err != 0) return 0; sres = fgws_stt_arg2obj(func->obj->parent, &est->stt, &res); if (res.type & FGW_DYN) free(res.val.ptr_void); return sres; } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_stt_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { estutter_t *est = obj->script_data; size_t len = strlen(name); fctx_t *fctx; char *s; stt_var_t *old; fctx = malloc(sizeof(fctx_t) + len); /* +1 for the \0 is granted by name[1] in the struct */ memcpy(fctx->name, name, len+1); for(s = fctx->name; *s != '\0'; s++) if (*s == '.') *s = '-'; old = stt_varctx_retr(est->vctx, fctx->name); if (old != NULL) { fprintf(stderr, "fungw_estutter fgws_stt_reg_func(): Can't register function over existing symbol: %s\n", fctx->name); free(fctx); return; } stt_varctx_set(est->vctx, fctx->name, stt_create_builtin_with_data(&est->stt, fgws_stt_call_fgw, fctx)); fctx->next = est->fl; est->fl = fctx; } /* API: unload the script */ static int fgws_stt_unload(fgw_obj_t *obj) { estutter_t *est = obj->script_data; fctx_t *f, *next; /* free all stored function names */ for(f = est->fl; f != NULL; f = next) { next = f->next; free(f); } stt_varctx_free(est->vctx); stt_uninit(&est->stt); free(est); return 0; } /* API: fgw calls a stt function */ static fgw_error_t fgws_stt_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; estutter_t *est = obj->script_data; int n; stt_s_mark mark; stt_obj_t *call, *tail, *sres, *a; mark = stt_gc_prot_mark(&est->stt); call = stt_create_cons(&est->stt, stt_create_symbol(&est->stt, argv[0].val.func->name), NULL); tail = call; for(n = 1; n < argc; n++) { a = fgws_stt_arg2obj(obj->parent, &est->stt, &argv[n]); tail->d.cons.cdr = stt_create_cons(&est->stt, a, NULL); tail = tail->d.cons.cdr; } tail->d.cons.cdr = stt_create_nil(&est->stt); fgws_ucc_save(obj); sres = stt_eval(est->vctx, call); fgws_ucc_restore(obj); if (sres->type == STT_ST_ERROR) { #warning TODO stt_fp_printobj(stderr, est->vctx, sres); return FGW_ERR_UNKNOWN; } stt_gc_prot_free(&est->stt, mark, NULL); fgws_stt_obj2arg(res, sres); return FGW_SUCCESS; } /* Helper function for the script to register its functions */ static STT_BUILTIN(fgws_stt_freg) { estutter_t *est = ctx->stt->user_data; fgw_func_t *func; stt_obj_t *fna; STT_ARG_STRING(&est->stt, fna); func = fgw_func_reg(est->obj, fna->d.string.value, fgws_stt_call_script); if (func == NULL) { fgw_async_error(est->obj, "fgw_stt_func_reg: failed to register function\n"); fgw_async_error(est->obj, fna->d.string.value); fgw_async_error(est->obj, "\n"); return stt_create_nil(&est->stt); } return stt_create_t(&est->stt); } /* API: init the interpreter so that functions can be registered */ static int fgws_stt_init(fgw_obj_t *obj, const char *filename, const char *opts) { estutter_t *est; est = calloc(sizeof(estutter_t), 1); est->stt.user_data = est; est->obj = obj; stt_init(&est->stt); est->vctx = stt_varctx_create(&est->stt, NULL, 1024); obj->script_data = est; stt_register_builtins(est->vctx); stt_register_stdio(est->vctx); /* add the stt->fgw glue */ stt_varctx_set(est->vctx, "fgw_func_reg", stt_create_builtin(&est->stt, fgws_stt_freg)); return 0; } /* API: load a script into an object */ static int fgws_stt_load(fgw_obj_t *obj, const char *filename, const char *opts) { estutter_t *est = obj->script_data; stt_obj_t *sys; sys = stt_load_system(est->vctx); if(sys->type == STT_ST_ERROR) { stt_fp_printobj(stderr, est->vctx, sys); fputc('\n', stderr); } stt_evalfile(est->vctx, filename); return 0; } static int fgws_stt_test_parse(const char *filename, FILE *f) { const char *exts[] = {".estt", ".stt", NULL }; return fgw_test_parse_fn(filename, exts); } /* API: engine registration */ static const fgw_eng_t fgw_estutter_eng = { "estutter", fgws_stt_call_script, fgws_stt_init, fgws_stt_load, fgws_stt_unload, fgws_stt_reg_func, NULL, fgws_stt_test_parse, ".stt" }; int pplg_check_ver_fungw_estutter(int version_we_need) { return 0; } int pplg_init_fungw_estutter(void) { fgw_eng_reg(&fgw_estutter_eng); return 0; } void pplg_uninit_fungw_estutter(void) { fgw_eng_unreg(fgw_estutter_eng.name); } fungw-1.2.0/libfungwbind/fawk/0000755000175100017510000000000014047742763014460 5ustar svnsvnfungw-1.2.0/libfungwbind/fawk/Plug.tmpasm0000644000175100017510000000060213512644574016605 0ustar svnsvnput /local/fungw/mod {fungw_fawk} put /local/fungw/mod_dir {fawk} switch ?libs/script/fawk/presents case {true} put /local/fungw/mod_cflags {} put /local/fungw/mod_ldflags {} put /local/fungw/mod_src {fungw_fawk.c} put /local/fingw/mod_extra_rules [@ fungw_fawk.o: libfawk_sc/libfawk_sc_all.c @] include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/fawk/fungw_fawk.pup0000644000175100017510000000031113757460755017344 0ustar svnsvn$desc fawk binding engine $state works $script-ext fawk .awk $script-ext fawk .fawk $script-ext fpas .pas $script-ext fpas .fpas $script-ext fbas .bas $script-ext fbas .fbas default buildin autoload 0 fungw-1.2.0/libfungwbind/fawk/fungw_fawk.c0000644000175100017510000002415013757344474016770 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include typedef double fawk_num_t; typedef long fawk_refco_t; #define FAWK_API static #include "libfawk_sc/libfawk_sc_all.c" /* loader */ void libfawk_error(fawk_ctx_t *ctx, const char *str, const char *loc_fn, long loc_line, long loc_col) { fgw_obj_t *obj = ctx->user_data; char tmp[256]; fgw_async_error(obj, "fawk error: "); fgw_async_error(obj, str); fgw_async_error(obj, " at "); fgw_async_error(obj, loc_fn); sprintf(tmp, " %ld:%ld\n", loc_line, loc_col); fgw_async_error(obj, tmp); } static int getch1(fawk_ctx_t *ctx, fawk_src_t *src) { FILE *f = ctx->parser.isp->user_data; return fgetc(f); } static int include1(fawk_ctx_t *ctx, fawk_src_t *src, int opening, fawk_src_t *from) { if (opening) { FILE *f; if ((*src->fn != '/') && (from != NULL)) { /* calculate relative address from 'from' */ int l1 = strlen(src->fn), l2 = strlen(from->fn); char *end, *fn = malloc(l1+l2+4); memcpy(fn, from->fn, l2+1); end = strrchr(fn, '/'); if (end != NULL) { end++; memcpy(end, src->fn, l1+1); f = fopen(fn, "r"); } else f = fopen(src->fn, "r"); free(fn); } else f = fopen(src->fn, "r"); src->user_data = f; if (f == NULL) { fprintf(stderr, "Can't find %s for include\n", src->fn); return -1; } } else fclose(src->user_data); return 0; } static void fgw_fawk_tocell(fgw_ctx_t *fctx, fawk_ctx_t *ctx, fawk_cell_t *dst, fgw_arg_t *arg) { # define FGW_FAWK_TOCELL_NUM(lst, val) dst->type = FAWK_NUM; dst->data.num = val; return; # define FGW_FAWK_TOCELL_STR(lst, val) dst->type = FAWK_STR; dst->data.str = fawk_str_new_from_literal(ctx, (val == NULL ? "" : val), -1); return; # define FGW_FAWK_TOCELL_PTR(lst, val) fgw_arg_conv(fctx, arg, FGW_STR); FGW_FAWK_TOCELL_STR(lst, val); # define FGW_FAWK_TOCELL_NIL(lst, val) dst->type = FAWK_NIL; return; if (FGW_IS_TYPE_CUSTOM(arg->type)) fgw_arg_conv(fctx, arg, FGW_AUTO); switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(lst, FGW_FAWK_TOCELL_NUM); ARG_CONV_CASE_LLONG(lst, FGW_FAWK_TOCELL_NUM); ARG_CONV_CASE_DOUBLE(lst, FGW_FAWK_TOCELL_NUM); ARG_CONV_CASE_LDOUBLE(lst, FGW_FAWK_TOCELL_NUM); ARG_CONV_CASE_PTR(lst, FGW_FAWK_TOCELL_PTR); ARG_CONV_CASE_STR(lst, FGW_FAWK_TOCELL_STR); ARG_CONV_CASE_CLASS(lst, FGW_FAWK_TOCELL_NIL); ARG_CONV_CASE_INVALID(lst, FGW_FAWK_TOCELL_NIL); } if (arg->type & FGW_PTR) { FGW_FAWK_TOCELL_PTR(lst, arg->val.ptr_void); } else { FGW_FAWK_TOCELL_NIL(lst, 0); } } /* Read the fawk stack and convert the result to an fgw arg */ static void fgw_fawk_toarg(fawk_ctx_t *ctx, fgw_arg_t *dst, fawk_cell_t *src) { switch(src->type) { case FAWK_NUM: case FAWK_STRNUM: dst->type = FGW_DOUBLE; dst->val.nat_double = src->data.num; break; case FAWK_STR: dst->type = FGW_STR | FGW_DYN; dst->val.str = fgw_strdup(src->data.str->str); break; case FAWK_NIL: default: dst->type = FGW_PTR; dst->val.ptr_void = NULL; break; } } /* API: the script is calling an fgw function */ static void fgws_fawk_call_fgw(fawk_ctx_t *ctx, const char *fname, int argc, fawk_cell_t *retval) { fgw_obj_t *obj = ctx->user_data; int n; fgw_arg_t res, *argv, argv_static[16]; fgw_func_t *func; func = fgw_func_lookup(obj->parent, fname); if (func == NULL) return; if ((argc + 1) > (sizeof(argv_static) / sizeof(argv_static[0]))) argv = malloc((argc + 1) * sizeof(fgw_arg_t)); else argv = argv_static; argv[0].type = FGW_FUNC; argv[0].val.argv0.func = func; argv[0].val.argv0.user_call_ctx = obj->script_user_call_ctx; for (n = 0; n < argc; n++) fgw_fawk_toarg(ctx, &argv[n+1], FAWK_CFUNC_ARG(n)); /* Run command */ res.type = FGW_PTR; res.val.ptr_void = NULL; if (func->func(&res, argc+1, argv) != 0) return; /* Free the array */ fgw_argv_free(obj->parent, argc+1, argv); if (argv != argv_static) free(argv); fgw_fawk_tocell(obj->parent, ctx, retval, &res); } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_fawk_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { fawk_ctx_t *ctx = obj->script_data; fawk_symtab_regcfunc(ctx, name, fgws_fawk_call_fgw); } /* API: fgw calls a fawk function */ static fgw_error_t fgws_fawk_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; fawk_ctx_t *ctx = obj->script_data; fawk_cell_t r; int i; if (fawk_call1(ctx, argv[0].val.func->name) != 0) return FGW_ERR_NOT_FOUND; for (i = 1; i < argc; i++) fgw_fawk_tocell(obj->parent, ctx, fawk_push_alloc(ctx), &argv[i]); if (fawk_call2(ctx, argc-1) != 0) return FGW_ERR_ARGC; fgws_ucc_save(obj); i = fawk_execute(ctx, -1); fgws_ucc_restore(obj); if (i != 0) return FGW_ERR_UNKNOWN; fawk_pop(ctx, &r); fgw_fawk_toarg(ctx, res, &r); return FGW_SUCCESS; } /* API: unload the script */ static int fgws_fawk_unload(fgw_obj_t *obj) { if (obj->script_data != NULL) { fawk_uninit(obj->script_data); free(obj->script_data); } obj->script_data = NULL; return 0; } /* Helper function for the script to register its functions */ static void fgws_fawk_freg(fawk_ctx_t *ctx, const char *fname, int argc, fawk_cell_t *retval) { fgw_obj_t *obj = ctx->user_data; fawk_cell_t *fr = FAWK_CFUNC_ARG(0); fgw_func_t *func; if (argc != 1) { fgw_async_error(obj, "fgw_func_reg: wrong number of arguments: need 1\n"); return; } if (fr->type != FAWK_FUNC) { fgw_async_error(obj, "fgw_func_reg: need a function name (without quotes)\n"); return; } func = fgw_func_reg(obj, fr->data.func.name, fgws_fawk_call_script); if (func == NULL) { fgw_async_error(obj, "fgw_func_reg: failed to register function "); fgw_async_error(obj, fr->data.func.name); fgw_async_error(obj, "\n"); return; } retval->type = FAWK_NUM; retval->data.num = 0; } /* API: init the interpreter so that functions can be registered */ static int fgws_fawk_init(fgw_obj_t *obj, const char *filename, const char *opts) { fawk_ctx_t *ctx; /* initialize the interpreter */ obj->script_data = ctx = malloc(sizeof(fawk_ctx_t)); if (ctx == NULL) { fgw_async_error(obj, "fgws_fawk_init: failed to allocate the script context\n"); return -1; } fawk_init(ctx); /* add the fawk->fgw glue */ fawk_symtab_regcfunc(ctx, "fgw_func_reg", fgws_fawk_freg); ctx->user_data = obj; return 0; } /* API: load a script into an object */ static int fgws_fawk_load_any(fgw_obj_t *obj, const char *filename, const char *opts, int (*parser)(fawk_ctx_t *ctx)) { fawk_ctx_t *ctx = obj->script_data; ctx->parser.get_char = getch1; ctx->parser.include = include1; /* Load the file */ ctx->parser.isp->user_data = fopen(filename, "r"); if (ctx->parser.isp->user_data == NULL) { fgw_async_error(obj, "fgws_fawk_load: failed to load the script\n"); error:; fawk_uninit(ctx); obj->script_data = NULL; return -1; } ctx->parser.isp->fn = fawk_strdup(ctx, filename); if (parser(ctx) != 0) { fgw_async_error(obj, "fgws_fawk_load: failed to parse the script()\n"); goto error; } if ((fawk_call1(ctx, "main") != 0) || (fawk_call2(ctx, 0) != 0) || fawk_execute(ctx, -1)) { fgw_async_error(obj, "fgws_fawk_load: failed to call main()\n"); goto error; } return 0; } static int fgws_fawk_load_fawk(fgw_obj_t *obj, const char *filename, const char *opts) { return fgws_fawk_load_any(obj, filename, opts, fawk_parse_fawk); } static int fgws_fawk_load_fbas(fgw_obj_t *obj, const char *filename, const char *opts) { return fgws_fawk_load_any(obj, filename, opts, fawk_parse_fbas); } static int fgws_fawk_load_fpas(fgw_obj_t *obj, const char *filename, const char *opts) { return fgws_fawk_load_any(obj, filename, opts, fawk_parse_fpas); } static int fgws_fawk_test_parse_fawk(const char *filename, FILE *f) { const char *exts[] = {".fawk", NULL }; return fgw_test_parse_fn(filename, exts); } static int fgws_fawk_test_parse_fbas(const char *filename, FILE *f) { const char *exts[] = {".fbas", ".bas", NULL }; return fgw_test_parse_fn(filename, exts); } static int fgws_fawk_test_parse_fpas(const char *filename, FILE *f) { const char *exts[] = {".fpas", ".pas", NULL }; return fgw_test_parse_fn(filename, exts); } /* API: engine registration: fawk */ static const fgw_eng_t fgw_fawk_eng = { "fawk", fgws_fawk_call_script, fgws_fawk_init, fgws_fawk_load_fawk, fgws_fawk_unload, fgws_fawk_reg_func, NULL, fgws_fawk_test_parse_fawk, ".fawk" }; /* API: engine registration: fbas */ static const fgw_eng_t fgw_fbas_eng = { "fbas", fgws_fawk_call_script, fgws_fawk_init, fgws_fawk_load_fbas, fgws_fawk_unload, fgws_fawk_reg_func, NULL, fgws_fawk_test_parse_fbas, ".bas" }; /* API: engine registration: fpas */ static const fgw_eng_t fgw_fpas_eng = { "fpas", fgws_fawk_call_script, fgws_fawk_init, fgws_fawk_load_fpas, fgws_fawk_unload, fgws_fawk_reg_func, NULL, fgws_fawk_test_parse_fpas, ".pas" }; int pplg_check_ver_fungw_fawk(int version_we_need) { return 0; } int pplg_init_fungw_fawk(void) { (void)fawk_sym_lookup; (void)fawk_array_resolve_c; /* suppress warning for unused vars */ fgw_eng_reg(&fgw_fawk_eng); fgw_eng_reg(&fgw_fbas_eng); fgw_eng_reg(&fgw_fpas_eng); return 0; } void pplg_uninit_fungw_fawk(void) { fgw_eng_unreg(fgw_fawk_eng.name); fgw_eng_unreg(fgw_fbas_eng.name); fgw_eng_unreg(fgw_fpas_eng.name); } fungw-1.2.0/libfungwbind/fawk/libfawk_sc/0000755000175100017510000000000014047742763016564 5ustar svnsvnfungw-1.2.0/libfungwbind/fawk/libfawk_sc/libfawk_sc_all.c0000644000175100017510000045537314045677304021701 0ustar svnsvn /* libfawk - A function-only AWK dialect - compacted, single-source-file version (for a human readable source code please visit the project page) Copyright (c) 2017..2020 Tibor 'Igor2' Palinkas. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Project page: http://repo.hu/projects/libfawk Source code: svn://repo.hu/libfawk/trunk Contact the author: http://igor2.repo.hu/contact.html */ #include #include #include #include #include #ifndef fawk_malloc #define fawk_malloc(ctx,size) malloc(size) #define fawk_calloc(ctx,n,m) calloc(n, m) #define fawk_realloc(ctx,ptr,size) realloc(ptr, size) #define fawk_free(ctx,ptr) free(ptr) #endif #ifndef fawk_assert #include #define fawk_assert assert #endif #ifndef fawk_no_math #include #define fawk_strtod(str) strtod(str, NULL) #define fawk_fmod(x,y) fmod(x, y) #define FAWK_NUM_PRINTF_FMT "%g" #endif #define GENHT_STATIC static #define GENHT_INLINE #define HT_HAS_CONST_KEY typedef void *fawk_htpp_key_t; typedef const void *fawk_htpp_const_key_t; typedef void *fawk_htpp_value_t; #define HT(x) fawk_htpp_ ## x typedef struct { int flag; unsigned int hash; HT(key_t) key; HT(value_t) value; } HT(entry_t); typedef struct { unsigned int mask; unsigned int fill; unsigned int used; HT(entry_t) *table; unsigned int (*keyhash)(HT(const_key_t)); int (*keyeq)(HT(const_key_t), HT(const_key_t)); } HT(t); FAWK_API int HT(init)(HT(t) *ht, unsigned int (*keyhash)(HT(const_key_t)), int (*keyeq)(HT(const_key_t), HT(const_key_t))); FAWK_API void HT(uninit)(HT(t) *ht); FAWK_API int HT(resize)(HT(t) *ht, unsigned int hint); FAWK_API int HT(has)(HT(t) *ht, HT(const_key_t) key); FAWK_API HT(value_t) HT(get)(HT(t) *ht, HT(const_key_t) key); FAWK_API void HT(set)(HT(t) *ht, HT(key_t) key, HT(value_t) value); FAWK_API HT(entry_t) *HT(insert)(HT(t) *ht, HT(key_t) key, HT(value_t) value); FAWK_API HT(value_t) HT(pop)(HT(t) *ht, HT(const_key_t) key); GENHT_STATIC GENHT_INLINE int HT(isused)(const HT(entry_t) *entry) {return entry->flag > 0;} GENHT_STATIC GENHT_INLINE int HT(isempty)(const HT(entry_t) *entry) {return entry->flag == 0;} GENHT_STATIC GENHT_INLINE int HT(isdeleted)(const HT(entry_t) *entry) {return entry->flag < 0;} GENHT_STATIC GENHT_INLINE HT(entry_t) *HT(first)(const HT(t) *ht) { HT(entry_t) *entry = 0; if (ht->used) for (entry = ht->table; !HT(isused)(entry); entry++); return entry; } GENHT_STATIC GENHT_INLINE HT(entry_t) *HT(next)(const HT(t) *ht, HT(entry_t) *entry) { while (++entry != ht->table + ht->mask + 1) if (HT(isused)(entry)) return entry; return 0; } #ifndef HT_INVALID_VALUE #define HT_INVALID_VALUE 0 #endif #define HT_MINSIZE 8 #define HT_MAXSIZE (1U << 31) #define JUMP(i,j) i += j++ #define JUMP_FIRST(i,j) j = 1, i += j++ static GENHT_INLINE void setused(HT(entry_t) *entry) { entry->flag = 1; } static GENHT_INLINE void setdeleted(HT(entry_t) *entry) { entry->flag = -1; } static GENHT_INLINE unsigned int entryhash(const HT(entry_t) *entry) { return entry->hash; } FAWK_API int HT(init)(HT(t) *ht, unsigned int (*keyhash)(HT(const_key_t)), int (*keyeq)(HT(const_key_t), HT(const_key_t))) { ht->mask = HT_MINSIZE - 1; ht->fill = 0; ht->used = 0; ht->table = calloc( ht->mask + 1, sizeof(HT(entry_t))); if (!ht->table) return -1; ht->keyhash = keyhash; ht->keyeq = keyeq; return 0; } FAWK_API void HT(uninit)(HT(t) *ht) { free( ht->table); ht->table = NULL; } static HT(entry_t) *lookup(HT(t) *ht, HT(const_key_t) key, unsigned int hash) { unsigned int mask = ht->mask; unsigned int i = hash; unsigned int j; HT(entry_t) *table = ht->table; HT(entry_t) *entry = table + (i & mask); HT(entry_t) *free_entry; if (HT(isempty)(entry)) return entry; else if (HT(isdeleted)(entry)) free_entry = entry; else if (entryhash(entry) == hash && ht->keyeq(entry->key, key)) return entry; else free_entry = NULL; for (JUMP_FIRST(i, j); ; JUMP(i, j)) { entry = table + (i & mask); if (HT(isempty)(entry)) return (free_entry == NULL) ? entry : free_entry; else if (HT(isdeleted)(entry)) { if (free_entry == NULL) free_entry = entry; } else if (entryhash(entry) == hash && ht->keyeq(entry->key, key)) return entry; } } static HT(entry_t) *cleanlookup(HT(t) *ht, unsigned int hash) { unsigned int mask = ht->mask; unsigned int i = hash; unsigned int j; HT(entry_t) *table = ht->table; HT(entry_t) *entry = table + (i & mask); if (HT(isempty)(entry)) return entry; for (JUMP_FIRST(i, j); ; JUMP(i, j)) { entry = table + (i & mask); if (HT(isempty)(entry)) return entry; } } FAWK_API int HT(resize)(HT(t) *ht, unsigned int hint) { unsigned int newsize; unsigned int used = ht->used; HT(entry_t) *oldtable = ht->table; HT(entry_t) *entry; if (hint < used << 1) hint = used << 1; if (hint > HT_MAXSIZE) hint = HT_MAXSIZE; for (newsize = HT_MINSIZE; newsize < hint; newsize <<= 1); ht->table = calloc( newsize, sizeof(HT(entry_t))); if (!ht->table) { ht->table = oldtable; return -1; } ht->mask = newsize - 1; ht->fill = ht->used; for (entry = oldtable; used > 0; entry++) if (HT(isused)(entry)) { used--; *cleanlookup(ht, entryhash(entry)) = *entry; } free( oldtable); return 0; } FAWK_API int HT(has)(HT(t) *ht, HT(const_key_t) key) { HT(entry_t) *entry = lookup(ht, key, ht->keyhash(key)); return HT(isused)(entry); } FAWK_API HT(value_t) HT(get)(HT(t) *ht, HT(const_key_t) key) { HT(entry_t) *entry = lookup(ht, key, ht->keyhash(key)); return HT(isused)(entry) ? entry->value : HT_INVALID_VALUE; } static GENHT_INLINE void checkfill(HT(t) *ht) { if (ht->fill > ht->mask - (ht->mask >> 2) || ht->fill > ht->used << 2) HT(resize)(ht, ht->used << (ht->used > 1 << 16 ? 1 : 2)); } FAWK_API HT(entry_t) *HT(insert)(HT(t) *ht, HT(key_t) key, HT(value_t) value) { unsigned int hash = ht->keyhash(key); HT(entry_t) *entry = lookup(ht, key, hash); if (HT(isused)(entry)) return entry; if (HT(isempty)(entry)) ht->fill++; ht->used++; entry->hash = hash; entry->key = key; entry->value = value; setused(entry); checkfill(ht); return NULL; } FAWK_API void HT(set)(HT(t) *ht, HT(key_t) key, HT(value_t) value) { HT(entry_t) *entry = HT(insert)(ht, key, value); if (entry) entry->value = value; } FAWK_API HT(value_t) HT(pop)(HT(t) *ht, HT(const_key_t) key) { HT(entry_t) *entry = lookup(ht, key, ht->keyhash(key)); HT(value_t) v; if (!HT(isused)(entry)) return HT_INVALID_VALUE; ht->used--; v = entry->value; setdeleted(entry); return v; } #undef HT_INVALID_VALUE #undef HT unsigned libfawk_hash_seed = 0x9e3779b9; static int genht_strcasecmp(const char *s1, const char *s2) { for(; (*s1 != 0) && (*s2 != 0) && ((*s1 == *s2) || (tolower(*s1) == tolower(*s2))); s1++, s2++); return tolower(*s1) - tolower(*s2);} static unsigned strhash(const void *key) { const unsigned char *p = key; unsigned h = libfawk_hash_seed; while (*p) h += (h << 2) + *p++; return h; } static int strkeyeq(const void *a, const void *b) { return !strcmp(a, b); } static unsigned strhash_case(const char *key) { const unsigned char *p = (const unsigned char *)key; unsigned h = libfawk_hash_seed; while (*p) h += (h << 2) + tolower(*p++); return h; } static int strkeyeq_case(const char *a, const char *b) { return !genht_strcasecmp(a, b); } static unsigned ptrhash(const void *k) { const unsigned long n = (const unsigned long)k; return (n >> 2) ^ (n >> 12); } static int ptrkeyeq(const void *a, const void *b) { return a == b; } #define FAWK_API_VER 1 typedef struct fawk_cell_s fawk_cell_t; typedef struct fawk_ctx_s fawk_ctx_t; typedef enum { FAWK_NIL, FAWK_NUM, FAWK_STR, FAWK_STRNUM, FAWK_ARRAY, FAWK_FUNC, FAWK_SYMREF, FAWK_CCALL_RET, FAWK_SCALAR = FAWK_NIL } fawk_celltype_t; typedef struct { fawk_num_t num; fawk_refco_t refco; size_t used, alloced; char str[1]; } fawk_str_t; typedef struct { fawk_celltype_t type; union { fawk_num_t num; fawk_str_t *str; } data; } fawk_arridx_t; typedef struct { fawk_refco_t refco; long uid; fawk_htpp_t hash; unsigned destroying:1; } fawk_arr_t; typedef void (*fawk_cfunc_t)(fawk_ctx_t *ctx, const char *fname, int argc, fawk_cell_t *retval); typedef struct { const char *name; fawk_cfunc_t cfunc; size_t ip; int numargs, numfixedargs; } fawk_func_t; typedef struct { union { fawk_cell_t *global; int local; } ref; char is_local; size_t idx_len; fawk_arridx_t *idx; } fawk_symref_t; struct fawk_cell_s { char *name; fawk_celltype_t type; union { fawk_num_t num; fawk_str_t *str; fawk_arr_t *arr; fawk_symref_t symref; fawk_func_t func; } data; }; typedef enum { FAWKI_MAKE_SYMREF, FAWKI_PUSH_NUM, FAWKI_PUSH_STR, FAWKI_PUSH_SYMVAL, FAWKI_PUSH_TOPVAR, FAWKI_PUSH_REL, FAWKI_PUSH_NIL, FAWKI_POP, FAWKI_POPJZ, FAWKI_POPJNZ, FAWKI_NEG, FAWKI_NOT, FAWKI_EQ, FAWKI_NEQ, FAWKI_LTEQ, FAWKI_GTEQ, FAWKI_LT, FAWKI_GT, FAWKI_IN, FAWKI_ADD, FAWKI_SUB, FAWKI_MUL, FAWKI_DIV, FAWKI_MOD, FAWKI_CONCAT, FAWKI_FORIN_FIRST, FAWKI_FORIN_NEXT, FAWKI_INCDEC, FAWKI_SET, FAWKI_CALL, FAWKI_RET, FAWKI_JMP, FAWKI_ABORT } fawk_ins_t; typedef struct { enum { FAWKC_INS, FAWKC_SYMREF, FAWKC_NUM, FAWKC_STR, FAWKC_CSTR } type; union { fawk_ins_t ins; fawk_symref_t *symref; fawk_num_t num; fawk_str_t *str; const char *cstr; } data; unsigned int line; } fawk_code_t; #define FAWK_STACK_PAGE_SIZE 256 #define FAWK_MAX_INCLUDE_STACK 16 typedef struct fawk_src_s { char *fn; long line, col, last_col; void *user_data; } fawk_src_t; typedef struct fawk_pkg_s { union { long l; fawk_num_t num; void *ptr; } data[8]; void (*str_free_cb)(struct fawk_pkg_s *pkg, fawk_ctx_t *ctx, fawk_str_t *str); void (*uninit_cb)(struct fawk_pkg_s *pkg, fawk_ctx_t *ctx); struct fawk_pkg_s *next; } fawk_pkg_t; #define FAWK_PKG_CALL(ctx,func,args) { fawk_pkg_t *p, *next; for(p = ctx->pkg_head; p != NULL; p = next) { next = p->next; if (func != NULL) func args; } } struct fawk_ctx_s { fawk_htpp_t symtab; struct { int (*get_char)(fawk_ctx_t *ctx, fawk_src_t *src); int (*include)(fawk_ctx_t *ctx, fawk_src_t *src, int opening, fawk_src_t *from); fawk_src_t *isp; fawk_src_t include_stack[FAWK_MAX_INCLUDE_STACK]; int in_textblk, pushback; char *buff, *curr_func; size_t used, alloced; unsigned textblk_state, in_eof:1; } parser; struct { int alloced, used; int avail; fawk_cell_t **page; } stack; struct { size_t used, alloced; fawk_code_t *code; } code; struct { int numargs, numfixedargs, numidx, funcdef_offs; fawk_htpp_t *labels, *lablink; } compiler; size_t errbuff_alloced; char *errbuff; size_t ip; size_t sp, fp; long arr_uid; struct { unsigned trace:1; unsigned error:1; } exec; fawk_pkg_t *pkg_head; void *user_data; }; typedef enum { FAWK_ER_FIN, FAWK_ER_STEPS, FAWK_ER_ERROR } fawk_execret_t; #define FAWK_STACK_INVALID (-1) #define FAWK_CODE_INVALID (-1) FAWK_API void fawk_init(fawk_ctx_t *ctx); FAWK_API void fawk_uninit(fawk_ctx_t *ctx); FAWK_API char *fawk_strdup(fawk_ctx_t *ctx, const char *s); FAWK_API void fawk_dump_cell(fawk_cell_t *cell, int verbose); FAWK_API fawk_str_t *fawk_str_new_from_literal(fawk_ctx_t *ctx, const char *s, size_t len_limit); FAWK_API fawk_str_t *fawk_str_clone(fawk_ctx_t *ctx, fawk_str_t *src, size_t enlarge); FAWK_API fawk_str_t *fawk_str_dup(fawk_ctx_t *ctx, fawk_str_t *src); FAWK_API void fawk_str_free(fawk_ctx_t *ctx, fawk_str_t *src); FAWK_API fawk_str_t *fawk_str_concat(fawk_ctx_t *ctx, fawk_str_t *s1, fawk_str_t *s2); FAWK_API int fawk_cast_to_num(fawk_ctx_t *ctx, fawk_cell_t *cell); FAWK_API int fawk_cast_to_str(fawk_ctx_t *ctx, fawk_cell_t *cell); FAWK_API void fawk_array_init(fawk_ctx_t *ctx, fawk_cell_t *dst); FAWK_API void fawk_array_free(fawk_ctx_t *ctx, fawk_cell_t *dst); FAWK_API fawk_arridx_t *fawk_array_dump_list(fawk_ctx_t *ctx, fawk_cell_t *arrcell, size_t *out_len); FAWK_API fawk_cell_t *fawk_array_resolve_c(fawk_ctx_t *ctx, int create, fawk_cell_t *arrcell, ...); FAWK_API void libfawk_error(fawk_ctx_t *ctx, const char *str, const char *loc_fn, long loc_line, long loc_col); #define LIBFAWK_ERROR(ctx,str,loc_fn,loc_line,loc_col,retval) \ do { libfawk_error(ctx, str, loc_fn, loc_line, loc_col); return retval; } while(0) FAWK_API void fawk_errbuff(fawk_ctx_t *ctx, size_t len); FAWK_API void fawk_close_include(fawk_ctx_t *ctx, fawk_src_t *src); #define FAWK_INCDEC_INC 1 #define FAWK_INCDEC_POST 2 #define FAWK_ERR ctx->errbuff #define FAWK_ERROR(ctx,len,fmt) \ do { \ fawk_errbuff(ctx, len); \ if (ctx->errbuff != NULL) { sprintf fmt; libfawk_error(ctx, ctx->errbuff, "", ctx->code.code[ctx->ip].line, 0); } \ ctx->exec.error = 1; \ } while(0) #define loop_pretest_jumpback() \ size_t skip, back; \ skip = fawk_pop_num(ctx, 1); \ back = fawk_pop_num(ctx, 1); \ fawkc_addi(ctx, FAWKI_JMP); \ fawkc_addnum(ctx, back); \ ctx->code.code[skip].data.num = FAWK_CURR_IP(); #define parse_aix_expr() \ fawkc_addi(ctx, FAWKI_MAKE_SYMREF); \ fawkc_addsymref(ctx, "SUBSEP", ctx->compiler.numidx, 0); \ fawkc_addnum(ctx, 0); \ fawkc_addi(ctx, FAWKI_PUSH_SYMVAL); \ fawkc_addi(ctx, FAWKI_CONCAT); #define fawk_parser_loop(yyctxtype,STYPE,lex,parse,ctx,next,done) \ yyctxtype yyctx; \ int res; \ parse ## _init(&yyctx); \ for(;;) { \ STYPE lval; \ res = parse(&yyctx, ctx, lex(&lval, ctx), &lval); \ if (res != next) break; \ } \ return res != done; FAWK_API fawk_cell_t *fawk_push_alloc(fawk_ctx_t *ctx); FAWK_API size_t fawk_push_num(fawk_ctx_t *ctx, fawk_num_t num); FAWK_API size_t fawk_push_str(fawk_ctx_t *ctx, const char *str); FAWK_API int fawk_pop(fawk_ctx_t *ctx, fawk_cell_t *dst); FAWK_API fawk_cell_t *fawk_peek(fawk_ctx_t *ctx, int addr); FAWK_API fawk_num_t fawk_pop_num(fawk_ctx_t *ctx, int expect_num); FAWK_API void fawk_cell_free(fawk_ctx_t *ctx, fawk_cell_t *cell); FAWK_API void fawk_cell_cpy(fawk_ctx_t *ctx, fawk_cell_t *dst, const fawk_cell_t *src); FAWK_API int fawk_call1(fawk_ctx_t *ctx, const char *funcname); FAWK_API int fawk_call2(fawk_ctx_t *ctx, int argc); FAWK_API fawk_execret_t fawk_execute(fawk_ctx_t *ctx, size_t steps); FAWK_API void fawk_reset(fawk_ctx_t *ctx); #define FAWK_CFUNC_ARG(argn) fawk_peek(ctx, -(argc-(argn))) FAWK_API int fawk_builtin_init(fawk_ctx_t *ctx); FAWK_API int fawk_symtab_regcfunc(fawk_ctx_t *ctx, const char *name, fawk_cfunc_t cfunc); FAWK_API fawk_cell_t *fawk_symtab_regvar(fawk_ctx_t *ctx, const char *name, fawk_celltype_t tclass); FAWK_API fawk_cell_t *fawk_sym_lookup(fawk_ctx_t *ctx, const char *name); FAWK_API fawk_cell_t *fawk_symtab_deref(fawk_ctx_t *ctx, const fawk_symref_t *sr, int arr_create, fawk_cell_t **parent); FAWK_API int fawk_symtab_regfunc(fawk_ctx_t *ctx, const char *name, size_t addr, int numargs, int numfixedargs); #define keyconv(i,n,s,isnum) \ if ((i)->type == FAWK_NUM) { isnum = 1; n = (i)->data.num; } \ else if ((i)->type == FAWK_STRNUM) { isnum = 1; n = (i)->data.str->num; } \ else if ((i)->type == FAWK_NIL) { isnum = 0; s = "\001NIL\001"; } \ else if ((i)->type == FAWK_STR) { isnum = 0; s = (i)->data.str->str; } \ else abort(); static unsigned int arrhash(const void *k) { int isnum; fawk_num_t n; const char *s; keyconv((const fawk_arridx_t *)k, n, s, isnum); return isnum ? n : strhash(s); } static int arrkeyeq(const void *k1, const void *k2) { fawk_num_t n1, n2; int isnum1 = 0, isnum2 = 0; const char *s1 = NULL, *s2 = NULL; keyconv((const fawk_arridx_t *)k1, n1, s1, isnum1); keyconv((const fawk_arridx_t *)k2, n2, s2, isnum2); if (isnum1 && isnum2) return n1 == n2; if (!isnum1 && !isnum2) return !strcmp(s1, s2); return 0; } FAWK_API void fawk_array_init(fawk_ctx_t *ctx, fawk_cell_t *dst) { dst->data.arr = fawk_calloc(ctx, sizeof(fawk_arr_t), 1); if (dst->data.arr == NULL) { dst->type = FAWK_NIL; return; } dst->type = FAWK_ARRAY; dst->data.arr->uid = ctx->arr_uid++; dst->data.arr->refco = 1; fawk_htpp_init(&dst->data.arr->hash, arrhash, arrkeyeq); } FAWK_API void fawk_array_free(fawk_ctx_t *ctx, fawk_cell_t *dst) { if (dst->data.arr->destroying) return; dst->data.arr->destroying = 1; dst->data.arr->refco--; if (dst->data.arr->refco == 0) { fawk_htpp_entry_t *e; for (e = fawk_htpp_first(&dst->data.arr->hash); e; e = fawk_htpp_next(&dst->data.arr->hash, e)) { fawk_arridx_t *idx = e->key; if ((idx->type == FAWK_STR) || (idx->type == FAWK_STRNUM)) fawk_str_free(ctx, idx->data.str); fawk_cell_free(ctx, e->value); fawk_free(ctx, e->value); fawk_free(ctx, e->key); } fawk_htpp_uninit(&dst->data.arr->hash); fawk_free(ctx, dst->data.arr); dst->data.arr = NULL; dst->type = FAWK_NIL; } else dst->data.arr->destroying = 0; } FAWK_API fawk_arridx_t *fawk_array_dump_list(fawk_ctx_t *ctx, fawk_cell_t *arrcell, size_t *out_len) { size_t len, n; fawk_arridx_t *list; fawk_htpp_entry_t *e; if ((arrcell == NULL) || (arrcell->type != FAWK_ARRAY)) return NULL; len = arrcell->data.arr->hash.used; list = fawk_malloc(ctx, sizeof(fawk_arridx_t) * len); if (list == NULL) return NULL; for (e = fawk_htpp_first(&arrcell->data.arr->hash), n = 0; e; e = fawk_htpp_next(&arrcell->data.arr->hash, e), n++) { fawk_arridx_t *idx = e->key; list[n].type = idx->type; if ((idx->type == FAWK_STR) || (idx->type == FAWK_STRNUM)) { list[n].data.str = fawk_str_dup(ctx, idx->data.str); if (list[n].data.str == NULL) { list[n].type = FAWK_NIL; ctx->exec.error = 1; } } else if (idx->type != FAWK_NIL) list[n].data.num = idx->data.num; } *out_len = len; return list; } FAWK_API fawk_cell_t *fawk_array_resolve_c(fawk_ctx_t *ctx, int create, fawk_cell_t *arrcell, ...) { fawk_symref_t sr; fawk_arridx_t idx[64]; va_list ap; int n; sr.ref.global = arrcell; sr.is_local = 0; sr.idx_len = 1; sr.idx = idx; va_start(ap, arrcell); for(n = 0; n < sizeof(idx)/sizeof(idx[0]); n++) { idx[n].type = (fawk_celltype_t)va_arg(ap, int); switch(idx[n].type) { case FAWK_NIL: va_end(ap); return fawk_symtab_deref(ctx, &sr, create, NULL); case FAWK_NUM: idx[n].data.num = va_arg(ap, fawk_num_t); break; case FAWK_STR: idx[n].data.str = fawk_str_new_from_literal(ctx, va_arg(ap, char *), -1); if (idx[n].data.str == NULL) goto err; break; default: goto err; } } err:; va_end(ap); return NULL; } static void fawk_bi_int(fawk_ctx_t *ctx, const char *fname, int argc, fawk_cell_t *retval) { if (argc == 1) { fawk_cell_cpy(ctx, retval, FAWK_CFUNC_ARG(0)); fawk_cast_to_num(ctx, retval); retval->data.num = (long)retval->data.num; } } static void fawk_bi_length(fawk_ctx_t *ctx, const char *fname, int argc, fawk_cell_t *retval) { if (argc == 1) { fawk_cell_t *v = FAWK_CFUNC_ARG(0); switch(v->type) { case FAWK_STR: case FAWK_STRNUM: retval->data.num = v->data.str->used; break; case FAWK_ARRAY: retval->data.num = v->data.arr->hash.used; break; case FAWK_NIL: case FAWK_NUM: case FAWK_FUNC: case FAWK_SYMREF: case FAWK_CCALL_RET: return; } retval->type = FAWK_NUM; } } static void fawk_bi_delete(fawk_ctx_t *ctx, const char *fname, int argc, fawk_cell_t *retval) { int n; for(n = 0; n < argc; n++) { fawk_cell_t *arr, *item, *v = FAWK_CFUNC_ARG(n); if (v->type == FAWK_SYMREF) { if ((item = fawk_symtab_deref(ctx, &v->data.symref, 0, &arr)) == NULL) continue; if (arr != NULL) fawk_htpp_pop(&arr->data.arr->hash, &v->data.symref.idx[v->data.symref.idx_len-1]); fawk_cell_free(ctx, item); } } } static void fawk_bi_isarray(fawk_ctx_t *ctx, const char *fname, int argc, fawk_cell_t *retval) { fawk_cell_t *arr, *v = FAWK_CFUNC_ARG(0); retval->type = FAWK_NUM; retval->data.num = 0; if (v->type == FAWK_SYMREF) { if (fawk_symtab_deref(ctx, &v->data.symref, 0, &arr) == NULL) return; if (arr != NULL) retval->data.num = 1; } else if (v->type == FAWK_ARRAY) retval->data.num = 1; } static void fawk_bi_print_cell(fawk_ctx_t *ctx, const char *fname, int argc, fawk_cell_t *retval) { #ifndef FAWK_DISABLE_FAWK_PRINT int n; for(n = 0; n < argc; n++) { fawk_dump_cell(FAWK_CFUNC_ARG(n), fname[10] == '_'); printf(n == argc-1 ? "\n" : " "); } #endif } static void fawk_bi_substr(fawk_ctx_t *ctx, const char *fname, int argc, fawk_cell_t *retval) { fawk_cell_t *str = FAWK_CFUNC_ARG(0), *from = FAWK_CFUNC_ARG(1), *len, dummy; if ((argc != 2) && (argc != 3)) return; fawk_cast_to_str(ctx, str); fawk_cast_to_num(ctx, from); if (argc > 2) { len = FAWK_CFUNC_ARG(2); fawk_cast_to_num(ctx, len); } else { len = &dummy; len->data.num = str->data.str->used; } if (--from->data.num < 0) from->data.num = 0; if (from->data.num > str->data.str->used) from->data.num = str->data.str->used; retval->type = FAWK_STR; retval->data.str = fawk_str_new_from_literal(ctx, str->data.str->str + (long)from->data.num, len->data.num); } FAWK_API int fawk_builtin_init(fawk_ctx_t *ctx) { fawk_cell_t *vs = fawk_symtab_regvar(ctx, "SUBSEP", FAWK_SCALAR), *vv = fawk_symtab_regvar(ctx, "FAWK_API_VER", FAWK_SCALAR); if ((vs == NULL) || (vv == NULL)) return -1; vs->type = FAWK_STR; vs->data.str = fawk_str_new_from_literal(ctx, "\034", -1); if (vs->data.str == NULL) return -1; vv->type = FAWK_NUM; vv->data.num = FAWK_API_VER; fawk_symtab_regcfunc(ctx, "int", fawk_bi_int); fawk_symtab_regcfunc(ctx, "length", fawk_bi_length); fawk_symtab_regcfunc(ctx, "delete", fawk_bi_delete); fawk_symtab_regcfunc(ctx, "isarray", fawk_bi_isarray); fawk_symtab_regcfunc(ctx, "fawk_print_cell", fawk_bi_print_cell); fawk_symtab_regcfunc(ctx, "fawk_print", fawk_bi_print_cell); fawk_symtab_regcfunc(ctx, "substr", fawk_bi_substr); return 0; } FAWK_API int fawk_cast_to_num(fawk_ctx_t *ctx, fawk_cell_t *cell) { fawk_num_t res; switch(cell->type) { case FAWK_NUM: return 0; case FAWK_STRNUM: res = cell->data.str->num; break; case FAWK_STR: res = fawk_strtod(cell->data.str->str); break; case FAWK_FUNC: res = cell->data.func.ip; break; case FAWK_ARRAY: res = cell->data.arr->uid; break; case FAWK_SYMREF: case FAWK_CCALL_RET: FAWK_ERROR(ctx, 32, (FAWK_ERR, "cast-to-num: invalid type\n")); return -1; case FAWK_NIL: res = 0; break; } fawk_cell_free(ctx, cell); cell->type = FAWK_NUM; cell->data.num = res; return 0; } FAWK_API int fawk_cast_to_str(fawk_ctx_t *ctx, fawk_cell_t *cell) { char buff[128]; const char *res; fawk_num_t n; switch(cell->type) { case FAWK_NUM: sprintf(buff, FAWK_NUM_PRINTF_FMT, cell->data.num); n = cell->data.num; cell->data.str = fawk_str_new_from_literal(ctx, buff, -1); cell->data.str->num = n; cell->type = (cell->data.str == NULL) ? FAWK_NIL : FAWK_STRNUM; return 0; case FAWK_STRNUM: case FAWK_STR: return 0; case FAWK_FUNC: res = cell->data.func.name; break; case FAWK_SYMREF: case FAWK_ARRAY: case FAWK_CCALL_RET: FAWK_ERROR(ctx, 32, (FAWK_ERR, "cast-to-str: invalid type\n")); return -1; case FAWK_NIL: res = ""; break; } cell->data.str = fawk_str_new_from_literal(ctx, res, -1); cell->type = (cell->data.str == NULL) ? FAWK_NIL : FAWK_STR; return 0; } static fawk_code_t *grow(fawk_ctx_t *ctx) { if (ctx->code.used >= ctx->code.alloced) { fawk_code_t *c; ctx->code.alloced += 1024; if ((c = fawk_realloc(ctx, ctx->code.code, ctx->code.alloced * sizeof(fawk_code_t))) == NULL) { ctx->code.alloced = 0; return NULL; } ctx->code.code = c; } ctx->code.code[ctx->code.used].line = ctx->parser.isp->line+1; return &ctx->code.code[ctx->code.used++]; } FAWK_API void fawkc_addi(fawk_ctx_t *ctx, fawk_ins_t ins) { fawk_code_t *i = grow(ctx); if (i == NULL) { return; } i->type = FAWKC_INS; i->data.ins = ins; } FAWK_API void fawkc_addcs(fawk_ctx_t *ctx, const char *s) { fawk_code_t *i = grow(ctx); if (i == NULL) { return; } i->type = FAWKC_CSTR; i->data.cstr = s; } FAWK_API void fawkc_adds(fawk_ctx_t *ctx, const char *s) { fawk_code_t *i = grow(ctx); if (i == NULL) { return; } i->data.str = fawk_str_new_from_literal(ctx, s, -1); i->type = (i->data.str == NULL) ? FAWKC_NUM : FAWKC_STR; } FAWK_API void fawkc_addnum(fawk_ctx_t *ctx, fawk_num_t num) { fawk_code_t *i = grow(ctx); if (i == NULL) { return; } i->type = FAWKC_NUM; i->data.num = num; } FAWK_API int fawkc_addsymref(fawk_ctx_t *ctx, const char *name, int isarr, int stack_from) { fawk_code_t *i; fawk_cell_t *c; int n, offs; for(n = stack_from, offs = -ctx->fp-1+stack_from; n < ctx->fp; n++,offs++) { c = fawk_peek(ctx, n); fawk_assert(c->type == FAWK_STR); if (strcmp(name, c->data.str->str) == 0) { i = grow(ctx); if (i == NULL) { return -1; } i->type = FAWKC_SYMREF; i->data.symref = fawk_calloc(ctx, sizeof(fawk_symref_t), 1); if (i->data.symref == NULL) return -1; i->data.symref->is_local = 1; i->data.symref->ref.local = offs-1; return 0; } } if ((c = fawk_symtab_regvar(ctx, name, isarr ? FAWK_ARRAY : FAWK_SCALAR)) == NULL) return -1; i = grow(ctx); if (i == NULL) { return -1; } i->type = FAWKC_SYMREF; i->data.symref = fawk_calloc(ctx, sizeof(fawk_symref_t), 1); if (i->data.symref == NULL) return -1; i->data.symref->ref.global = c; return 0; } FAWK_API void fawkc_addi(fawk_ctx_t *ctx, fawk_ins_t ins); FAWK_API void fawkc_addnum(fawk_ctx_t *ctx, fawk_num_t num); FAWK_API void fawkc_addcs(fawk_ctx_t *ctx, const char *s); FAWK_API void fawkc_adds(fawk_ctx_t *ctx, const char *s); FAWK_API int fawkc_addsymref(fawk_ctx_t *ctx, const char *name, int isarr, int stack_from); #define FAWK_PUSH_IP() fawk_push_num(ctx, ctx->code.used) #define FAWK_CURR_IP() ctx->code.used static void lazy_binop1(fawk_ctx_t *ctx, int is_or) { fawkc_addi(ctx, is_or ? FAWKI_POPJNZ : FAWKI_POPJZ); FAWK_PUSH_IP(); fawkc_addnum(ctx, 777); } static void lazy_binop2(fawk_ctx_t *ctx, int is_or) { size_t jmp1 = fawk_pop_num(ctx, 1); fawkc_addi(ctx, is_or ? FAWKI_POPJNZ : FAWKI_POPJZ); fawkc_addnum(ctx, FAWK_CURR_IP()+5); fawkc_addi(ctx, FAWKI_PUSH_NUM); fawkc_addnum(ctx, is_or ? 0 : 1); fawkc_addi(ctx, FAWKI_JMP); fawkc_addnum(ctx, FAWK_CURR_IP()+3); ctx->code.code[jmp1].data.num = FAWK_CURR_IP(); fawkc_addi(ctx, FAWKI_PUSH_NUM); fawkc_addnum(ctx, is_or ? 1 : 0); } #define STACKA(addr) ((ctx->stack.page[(addr) / FAWK_STACK_PAGE_SIZE])[((addr) % FAWK_STACK_PAGE_SIZE)]) #define STACKR(offs) STACKA(ctx->sp + (offs)) #define NEED_STACK(entries) fawk_assert((ctx->sp-(entries)) >= ctx->fp); #define FAWK_CAST_TO_NUM(cell) \ do { \ if (cell->type != FAWK_NUM) \ fawk_cast_to_num(ctx, cell); \ } while(0) #define FAWK_CAST_TO_STR(cell) \ do { \ if (cell->type != FAWK_STR) \ fawk_cast_to_str(ctx, cell); \ } while(0) static void cell_free(fawk_ctx_t *ctx, fawk_cell_t *cell) { int n; switch(cell->type) { case FAWK_STR: case FAWK_STRNUM: if (cell->data.str != NULL) fawk_str_free(ctx, cell->data.str); break; case FAWK_ARRAY: fawk_array_free(ctx, cell); return; case FAWK_SYMREF: for(n = 0; (n < cell->data.symref.idx_len) && (cell->data.symref.idx_len != -1); n++) if ((cell->data.symref.idx[n].type == FAWK_STR) || (cell->data.symref.idx[n].type == FAWK_STRNUM)) fawk_str_free(ctx, cell->data.symref.idx[n].data.str); fawk_free(ctx, cell->data.symref.idx); break; default: break; } cell->type = FAWK_NIL; } FAWK_API void fawk_cell_free(fawk_ctx_t *ctx, fawk_cell_t *cell) { cell_free(ctx, cell); } static void cellcpy(fawk_ctx_t *ctx, fawk_cell_t *dst, const fawk_cell_t *src) { cell_free(ctx, dst); *dst = *src; switch(src->type) { case FAWK_STR: case FAWK_STRNUM: dst->data.str = fawk_str_dup(ctx, src->data.str); if (dst->data.str == NULL) dst->type = FAWK_NIL; break; case FAWK_ARRAY: dst->type = FAWK_ARRAY; dst->data.arr = src->data.arr; dst->data.arr->refco++; default: break; } } FAWK_API void fawk_cell_cpy(fawk_ctx_t *ctx, fawk_cell_t *dst, const fawk_cell_t *src) { cellcpy(ctx, dst, src); } static fawk_cell_t *push_alloc(fawk_ctx_t *ctx) { fawk_cell_t *res; if (ctx->stack.avail == 0) { if (ctx->stack.used >= ctx->stack.alloced) { fawk_cell_t **pg; ctx->stack.alloced += 128; if ((pg = fawk_realloc(ctx, ctx->stack.page, sizeof(fawk_cell_t *) * ctx->stack.alloced)) == NULL) { ctx->stack.alloced = 0; ctx->exec.error = 1; return NULL; } ctx->stack.page = pg; } ctx->stack.page[ctx->stack.used] = fawk_malloc(ctx, sizeof(fawk_cell_t) * FAWK_STACK_PAGE_SIZE); if (ctx->stack.page[ctx->stack.used] == NULL) { ctx->exec.error = 1; return NULL;} ctx->stack.avail = FAWK_STACK_PAGE_SIZE; ctx->stack.used++; } ctx->stack.avail--; res = &STACKA(ctx->sp); res->name = NULL; res->type = FAWK_NIL; ctx->sp++; return res; } FAWK_API void fawk_reset(fawk_ctx_t *ctx) { size_t n; for(n = 0; n < ctx->sp; n++) cell_free(ctx, &STACKA(n)); ctx->ip = ctx->fp = ctx->sp = 0; fawk_free(ctx, ctx->errbuff); ctx->errbuff = NULL; ctx->errbuff_alloced = 0; } FAWK_API fawk_cell_t *fawk_push_alloc(fawk_ctx_t *ctx) { return push_alloc(ctx); } FAWK_API size_t fawk_push_num(fawk_ctx_t *ctx, fawk_num_t num) { fawk_cell_t *cell = push_alloc(ctx); if (cell == NULL) return FAWK_STACK_INVALID; cell->type = FAWK_NUM; cell->data.num = num; return ctx->sp-1; } FAWK_API size_t fawk_push_str(fawk_ctx_t *ctx, const char *str) { fawk_cell_t *cell = push_alloc(ctx); if (cell == NULL) return FAWK_STACK_INVALID; cell->data.str = fawk_str_new_from_literal(ctx, str, -1); cell->type = (cell->data.str == NULL) ? FAWK_NIL : FAWK_STR; return ctx->sp-1; } #define PUSH() push_alloc(ctx) #define POP() \ do { \ cell_free(ctx, &STACKR(-1)); \ ctx->sp--; \ ctx->stack.avail++; \ } while(0) FAWK_API fawk_num_t fawk_pop_num(fawk_ctx_t *ctx, int expect_num) { fawk_cell_t *cell; NEED_STACK(1); cell = &STACKR(-1); if (cell->type != FAWK_NUM) { if (expect_num) { fawk_assert(cell->type == FAWK_NUM); POP(); return 0; } fawk_cast_to_num(ctx, cell); } POP(); return cell->data.num; } FAWK_API int fawk_pop(fawk_ctx_t *ctx, fawk_cell_t *dst) { if (ctx->sp < 0) return -1; *dst = STACKR(-1); STACKR(-1).type = FAWK_NIL; POP(); return 0; } FAWK_API fawk_cell_t *fawk_peek(fawk_ctx_t *ctx, int addr) { return (addr >= 0) ? &STACKA(addr) : &STACKR(addr); } static fawk_cell_t *symtab_deref(fawk_ctx_t *ctx, const fawk_symref_t *sr, int arr_create, fawk_cell_t **parent) { fawk_cell_t *base, *child; int n; base = (sr->is_local) ? &STACKA(ctx->fp + sr->ref.local) : sr->ref.global; fawk_assert(base != NULL); if (parent != NULL) *parent = NULL; if (sr->idx_len == 0) return base; for(n = 0; (n < sr->idx_len) && (sr->idx_len != -1); n++) { if (base->type == FAWK_NIL) fawk_array_init(ctx, base); else if (base->type != FAWK_ARRAY) { FAWK_ERROR(ctx, 64, (FAWK_ERR, "deref: symbol is not an array but is indexed like if it was\n")); return NULL; } if ((child = fawk_htpp_get(&base->data.arr->hash, &sr->idx[n])) == NULL) { fawk_arridx_t *idx; if (!arr_create) return NULL; child = fawk_malloc(ctx, sizeof(fawk_cell_t)); if (child == NULL) { return NULL; } child->type = FAWK_NIL; idx = fawk_malloc(ctx, sizeof(fawk_arridx_t)); if (idx == NULL) { free(child); return NULL; } idx->type = sr->idx[n].type; if ((sr->idx[n].type == FAWK_STR) || (sr->idx[n].type == FAWK_STRNUM)) { idx->data.str = fawk_str_dup(ctx, sr->idx[n].data.str); if (idx->data.str == NULL) { idx->type = FAWK_NIL; FAWK_ERROR(ctx, 64, (FAWK_ERR, "memory exhausted\n")); } } else idx->data.num = sr->idx[n].data.num; fawk_htpp_set(&base->data.arr->hash, idx, child); } if ((n < sr->idx_len-1) && (child->type == FAWK_NIL)) fawk_array_init(ctx, child); if (parent != NULL) *parent = base; base = child; } return base; } FAWK_API fawk_cell_t *fawk_symtab_deref(fawk_ctx_t *ctx, const fawk_symref_t *sr, int arr_create, fawk_cell_t **parent) { return symtab_deref(ctx, sr, arr_create, parent); } static fawk_cell_t *topvar(fawk_ctx_t *ctx, int and_pop) { fawk_cell_t *cdst, *csrc, *cell; NEED_STACK(1); cell = &STACKR(-1); fawk_assert(cell->type == FAWK_SYMREF); if ((csrc = symtab_deref(ctx, &cell->data.symref, 1, NULL)) == NULL) return NULL; if (and_pop) POP(); cdst = PUSH(); if (cdst == NULL) return NULL; cellcpy(ctx, cdst, csrc); return csrc; } static void exec_call(fawk_ctx_t *ctx, int argc) { fawk_cell_t *fc, *nil, *vararg, *child, vtmp; fc = &STACKR(-(argc + 1)); fawk_assert(fc->type == FAWK_SYMREF); if ((fc = symtab_deref(ctx, &fc->data.symref, 1, NULL)) == NULL) return; if (fc->type != FAWK_FUNC) { FAWK_ERROR(ctx, 64, (FAWK_ERR, "can't call: symbol is not a function\n")); return; } if (fc->data.func.cfunc == NULL) { if (fc->data.func.numfixedargs >= 0) { int vac = argc - fc->data.func.numfixedargs - 1; fawk_array_init(ctx, &vtmp); while(argc > fc->data.func.numfixedargs) { fawk_arridx_t *idx = malloc(sizeof(fawk_arridx_t)); if (idx == NULL) goto enomem; idx->type = FAWK_NUM; idx->data.num = vac--; child = fawk_malloc(ctx, sizeof(fawk_cell_t)); if (child == NULL) { enomem:; fawk_cell_free(ctx, &vtmp); return; } *child = STACKR(-1); ctx->sp--; ctx->stack.avail++; argc--; fawk_htpp_set(&vtmp.data.arr->hash, idx, child); } vararg = PUSH(); *vararg = vtmp; } else if (argc > fc->data.func.numargs) {FAWK_ERROR(ctx, 64 + strlen(fc->data.func.name), (FAWK_ERR, "Function '%s' called with more arguments than it takes\n", fc->data.func.name)); return; } while(argc < fc->data.func.numargs) { nil = PUSH(); if (nil == NULL) {FAWK_ERROR(ctx, 64, (FAWK_ERR, "memory exhausted\n")); return; } nil->type = FAWK_NIL; argc++; } fawk_push_num(ctx, ctx->fp); fawk_push_num(ctx, ctx->ip+1); ctx->fp = ctx->sp; ctx->ip = fc->data.func.ip-1; } else { fawk_cell_free(ctx, &STACKR(-(argc + 1))); fc->data.func.cfunc(ctx, fc->data.func.name, argc, &STACKR(-(argc + 1))); for(;argc > 0; argc--) POP(); ctx->ip++; } } static int idx_steal_cell(fawk_arridx_t *res, fawk_cell_t *csrc, int allow_nil) { res->type = csrc->type; switch(csrc->type) { case FAWK_NUM: res->data.num = csrc->data.num; return 0; case FAWK_STRNUM: case FAWK_STR: res->data.str = csrc->data.str; csrc->data.str = NULL; return 0; case FAWK_NIL: if (allow_nil) return 0; default: break; } return -1; } #define BREAK(num_param) ctx->ip += num_param; break #define UNOP_ON_TOP(action) \ NEED_STACK(1); \ cell = &STACKR(-1); \ FAWK_CAST_TO_NUM(cell); \ action #define BINOP(op1,op2) \ do { \ NEED_STACK(2); \ op1 = &STACKR(-1); \ op2 = &STACKR(-2); \ } while(0) #define BINOP_NUM(op1,op2) \ do { \ BINOP(op1, op2); \ FAWK_CAST_TO_NUM(op1); \ FAWK_CAST_TO_NUM(op2); \ POP(); \ } while(0) #define FAWK_NILSTR(op) (((op)->type == FAWK_NIL) || (((op)->type == FAWK_STR) && (*(op)->data.str->str == '\0'))) #define BINOP_STRNUM(sres,op1,op2) \ do { \ BINOP(op1, op2); \ if ((i->data.ins == FAWKI_EQ) && FAWK_NILSTR(op1) && FAWK_NILSTR(op2)) { STACKR(-2).type = FAWK_NUM; sres = 0; } \ else if ((i->data.ins == FAWKI_NEQ) && ((FAWK_NILSTR(op1) && !FAWK_NILSTR(op2)) || (!FAWK_NILSTR(op1) && FAWK_NILSTR(op2)))) { STACKR(-2).type = FAWK_NUM; sres = 1; } \ else if ((op1->type == FAWK_NIL) || (op2->type == FAWK_NIL)) { STACKR(-2).type = FAWK_NUM; sres = -2; }\ else if ((op1->type == FAWK_STR) || (op2->type == FAWK_STR)) { \ FAWK_CAST_TO_STR(op1); \ FAWK_CAST_TO_STR(op2); \ sres = strcmp(op2->data.str->str, op1->data.str->str); \ STACKR(-2).type = FAWK_NUM; \ } \ else { \ FAWK_CAST_TO_NUM(op1); \ FAWK_CAST_TO_NUM(op2); \ if (op2->data.num > op1->data.num) sres = 1; \ else if (op2->data.num == op1->data.num) sres = 0; \ else sres = -1; \ } \ POP(); \ } while(0) FAWK_API fawk_execret_t fawk_execute(fawk_ctx_t *ctx, size_t steps) { fawk_cell_t *cell, *csrc, *cdst; int n; for(;steps > 0; steps--) { fawk_code_t *i = &ctx->code.code[ctx->ip]; if (ctx->exec.error) return FAWK_ER_ERROR; if (ctx->exec.trace) printf("[%ld] %x\n", (long)ctx->ip, i->data.ins); fawk_assert(i[0].type == FAWKC_INS); switch(i->data.ins) { case FAWKI_MAKE_SYMREF: { fawk_cell_t res; fawk_assert(i[1].type == FAWKC_SYMREF); fawk_assert(i[2].type == FAWKC_NUM); res.type = FAWK_SYMREF; res.data.symref.is_local = i[1].data.symref->is_local; res.data.symref.idx_len = i[2].data.num; if (i[1].data.symref->is_local) { res.name = ""; res.data.symref.ref.local = i[1].data.symref->ref.local; if ((res.data.symref.idx_len > 0) || (res.data.symref.idx_len == -1)) { fawk_cell_t *base = &STACKA(ctx->fp + res.data.symref.ref.local); if (base->type == FAWK_NIL) fawk_array_init(ctx, base); } } else { res.name = i[1].data.symref->ref.global->name; res.data.symref.ref.global = i[1].data.symref->ref.global; } if ((res.data.symref.idx_len > 0) && (res.data.symref.idx_len != -1)) { int n, d; res.data.symref.idx = fawk_malloc(ctx, sizeof(fawk_arridx_t) * res.data.symref.idx_len); if (res.data.symref.idx == NULL) return FAWK_ER_ERROR; for(n = 0, d = res.data.symref.idx_len-1; n < res.data.symref.idx_len; n++,d--) { if (idx_steal_cell(&(res.data.symref.idx[d]), &STACKR(-1), 1) != 0) abort(); POP(); } } else res.data.symref.idx = NULL; cell = PUSH(); if (cell == NULL) return FAWK_ER_ERROR; *cell = res; } BREAK(2); case FAWKI_PUSH_NUM: fawk_assert(i[1].type == FAWKC_NUM); cell = PUSH(); if (cell == NULL) return FAWK_ER_ERROR; cell->type = FAWK_NUM; cell->data.num = i[1].data.num; BREAK(1); case FAWKI_PUSH_STR: fawk_assert(i[1].type == FAWKC_STR); cell = PUSH(); if (cell == NULL) return FAWK_ER_ERROR; cell->data.str = fawk_str_dup(ctx, i[1].data.str); cell->type = (cell->data.str == NULL) ? FAWK_NIL : FAWK_STR; BREAK(1); case FAWKI_PUSH_SYMVAL: topvar(ctx, 1); BREAK(0); case FAWKI_PUSH_TOPVAR: topvar(ctx, 0); BREAK(0); case FAWKI_PUSH_REL: cell = PUSH(); if (cell == NULL) return FAWK_ER_ERROR; fawk_cell_cpy(ctx, cell, &STACKR(((int)i[1].data.num)-1)); BREAK(1); case FAWKI_PUSH_NIL: cell = PUSH(); if (cell == NULL) return FAWK_ER_ERROR; cell->type = FAWK_NIL; BREAK(0); case FAWKI_SET: csrc = &STACKR(-1); cell = &STACKR(-2); fawk_assert(cell->type == FAWK_SYMREF); if ((cdst = symtab_deref(ctx, &cell->data.symref, 1, NULL)) == NULL) return FAWK_ER_ERROR; cellcpy(ctx, cdst, csrc); POP(); BREAK(0); case FAWKI_POP: NEED_STACK(1); POP(); BREAK(0); case FAWKI_POPJNZ: NEED_STACK(1); cell = &STACKR(-1); FAWK_CAST_TO_NUM(cell); if (cell->data.num != 0.0) { POP(); goto exec_jump; } POP(); BREAK(1); case FAWKI_POPJZ: NEED_STACK(1); cell = &STACKR(-1); FAWK_CAST_TO_NUM(cell); if (cell->data.num == 0.0) { POP(); goto exec_jump; } POP(); BREAK(1); case FAWKI_NEG: UNOP_ON_TOP(cell->data.num = -cell->data.num); BREAK(0); case FAWKI_NOT: UNOP_ON_TOP(cell->data.num = (cell->data.num == 0.0)); BREAK(0); case FAWKI_EQ: BINOP_STRNUM(n, csrc, cdst); cdst->data.num = (n != -2) && (n == 0); BREAK(0); case FAWKI_NEQ: BINOP_STRNUM(n, csrc, cdst); cdst->data.num = (n != -2) && (n != 0); BREAK(0); case FAWKI_LTEQ: BINOP_STRNUM(n, csrc, cdst); cdst->data.num = (n != -2) && (n <= 0); BREAK(0); case FAWKI_GTEQ: BINOP_STRNUM(n, csrc, cdst); cdst->data.num = (n != -2) && (n >= 0); BREAK(0); case FAWKI_IN: { fawk_arridx_t resi; NEED_STACK(2); csrc = &STACKR(-1); fawk_assert(csrc->type == FAWK_SYMREF); csrc = fawk_symtab_deref(ctx, &csrc->data.symref, 0, &cell); if (csrc->type != FAWK_ARRAY) { FAWK_ERROR(ctx, 64, (FAWK_ERR, "in: symbol is not an array, can't interpret 'in' it\n")); return FAWK_ER_ERROR; } POP(); cdst = &STACKR(-1); if (idx_steal_cell(&resi, cdst, 0) != 0) { FAWK_ERROR(ctx, 64, (FAWK_ERR, "in: invalid index type\n")); return FAWK_ER_ERROR; } n = fawk_htpp_has(&csrc->data.arr->hash, &resi); cell_free(ctx, cdst); cdst->type = FAWK_NUM; cdst->data.num = n; } BREAK(0); case FAWKI_LT: BINOP_STRNUM(n, csrc, cdst); cdst->data.num = (n != -2) && (n < 0); BREAK(0); case FAWKI_GT: BINOP_STRNUM(n, csrc, cdst); cdst->data.num = (n != -2) && (n > 0); BREAK(0); case FAWKI_ADD: BINOP_NUM(csrc, cdst); cdst->data.num += csrc->data.num; BREAK(0); case FAWKI_SUB: BINOP_NUM(csrc, cdst); cdst->data.num -= csrc->data.num; BREAK(0); case FAWKI_MUL: BINOP_NUM(csrc, cdst); cdst->data.num *= csrc->data.num; BREAK(0); case FAWKI_DIV: BINOP_NUM(csrc, cdst); cdst->data.num /= csrc->data.num; BREAK(0); case FAWKI_MOD: BINOP_NUM(csrc, cdst); cdst->data.num = fawk_fmod(cdst->data.num, csrc->data.num); BREAK(0); case FAWKI_INCDEC: { fawk_num_t diff; fawk_assert(i[1].type == FAWKC_NUM); diff = (((int)i[1].data.num) & FAWK_INCDEC_INC) ? +1 : -1; csrc = topvar(ctx, 1); cdst = &STACKR(-1); FAWK_CAST_TO_NUM(csrc); if (!(((int)i[1].data.num) & FAWK_INCDEC_POST)) { FAWK_CAST_TO_NUM(cdst); cdst->data.num += diff; } csrc->data.num += diff; BREAK(1); } case FAWKI_CONCAT: { fawk_str_t *ssrc, *sdst; NEED_STACK(2); csrc = &STACKR(-1); cdst = &STACKR(-2); FAWK_CAST_TO_STR(csrc); if ((csrc->type != FAWK_STR) && (csrc->type != FAWK_STRNUM)) return FAWK_ER_ERROR; FAWK_CAST_TO_STR(cdst); if ((cdst->type != FAWK_STR) && (cdst->type != FAWK_STRNUM)) return FAWK_ER_ERROR; ssrc = csrc->data.str; sdst = cdst->data.str; cdst->data.str = csrc->data.str = NULL; POP(); POP(); cell = PUSH(); if (cell == NULL) return FAWK_ER_ERROR; cell->data.str = fawk_str_concat(ctx, sdst, ssrc); cell->type = (cell->data.str == NULL) ? FAWK_NIL : FAWK_STR; } BREAK(0); case FAWKI_FORIN_FIRST: { fawk_arridx_t *list; size_t len; cdst = &STACKR(-1); fawk_assert(cdst->type == FAWK_SYMREF); csrc = symtab_deref(ctx, &cdst->data.symref, 0, NULL); list = fawk_array_dump_list(ctx, csrc, &len); if (list == NULL) { FAWK_ERROR(ctx, 64, (FAWK_ERR, "for-in: not an array\n")); return FAWK_ER_ERROR; } cell_free(ctx, cdst); cdst->type = FAWK_SYMREF; cdst->data.symref.is_local = 0; cdst->data.symref.ref.global = NULL; cdst->data.symref.idx_len = len; cdst->data.symref.idx = list; } BREAK(0); case FAWKI_FORIN_NEXT: { fawk_assert(i[1].type == FAWKC_NUM); csrc = &STACKR(-1); fawk_assert(csrc->type == FAWK_SYMREF); if ((csrc->data.symref.idx_len > 0) && (csrc->data.symref.idx_len != -1)) { cdst = &STACKR(-2); fawk_assert(cdst->type == FAWK_SYMREF); if ((cdst = symtab_deref(ctx, &cdst->data.symref, 1, NULL)) == NULL) return FAWK_ER_ERROR; cell_free(ctx, cdst); csrc->data.symref.idx_len--; if ((csrc->data.symref.idx[csrc->data.symref.idx_len].type == FAWK_STR) || (csrc->data.symref.idx[csrc->data.symref.idx_len].type == FAWK_STRNUM)) { cdst->type = FAWK_STR; cdst->data.str = csrc->data.symref.idx[csrc->data.symref.idx_len].data.str; csrc->data.symref.idx[csrc->data.symref.idx_len].data.str = NULL; } else if (csrc->data.symref.idx[csrc->data.symref.idx_len].type == FAWK_NIL) cdst->type = FAWK_NIL; else { cdst->type = FAWK_NUM; cdst->data.num = csrc->data.symref.idx[csrc->data.symref.idx_len].data.num; } ctx->ip = i[1].data.num - 1; break; } else { POP(); POP(); } } BREAK(1); case FAWKI_CALL: fawk_assert(i[1].type == FAWKC_NUM); exec_call(ctx, (int)i[1].data.num); BREAK(0); case FAWKI_RET: { int numargs, n; fawk_cell_t retval = STACKR(-1); ctx->sp--; ctx->stack.avail++; fawk_assert(i[1].type == FAWKC_NUM); numargs = i[1].data.num; while(STACKR(-1).type == FAWK_SYMREF) POP(); cell = &STACKR(-1); if (cell->type != FAWK_NUM) { FAWK_ERROR(ctx, 64, (FAWK_ERR, "invalid symref; tip: array-in-array can not be created with [] in one step\n")); return FAWK_ER_ERROR; } ctx->ip = cell->data.num; POP(); cell = &STACKR(-1); fawk_assert(cell->type == FAWK_NUM); ctx->fp = cell->data.num; POP(); for(n = 0; n <= numargs; n++) POP(); cell = PUSH(); if (cell == NULL) return FAWK_ER_ERROR; *cell = retval; cell = &STACKR(-2); if (cell->type == FAWK_CCALL_RET) { STACKR(-2) = STACKR(-1); STACKR(-1).type = FAWK_NIL; POP(); return FAWK_ER_FIN; } } break; case FAWKI_JMP: exec_jump:; { int addr = i[1].data.num; fawk_assert(i[1].type == FAWKC_NUM); fawk_assert(addr >= 0); fawk_assert(addr < ctx->code.used); ctx->ip = addr-2; } BREAK(1); case FAWKI_ABORT: FAWK_ERROR(ctx, 64, (FAWK_ERR, "libfawk abort")); if ((i[1].type == FAWKC_STR) || (i[1].type == FAWKC_CSTR)) libfawk_error(ctx, i[1].data.cstr, "", ctx->code.code[ctx->ip].line, 0); return FAWK_ER_ERROR; } ctx->ip++; } return FAWK_ER_STEPS; } FAWK_API int fawk_call1(fawk_ctx_t *ctx, const char *funcname) { fawk_cell_t *cell, *func = fawk_htpp_get(&ctx->symtab, funcname); if ((func == NULL) || (func->type != FAWK_FUNC)) return -1; cell = PUSH(); if (cell == NULL) return FAWK_ER_ERROR; cell->type = FAWK_CCALL_RET; cell = PUSH(); if (cell == NULL) return FAWK_ER_ERROR; cell->type = FAWK_SYMREF; cell->data.symref.is_local = cell->data.symref.idx_len = 0; cell->data.symref.idx = NULL; cell->data.symref.ref.global = func; return 0; } FAWK_API int fawk_call2(fawk_ctx_t *ctx, int argc) { fawk_cell_t *funcref = &STACKR(-argc-1), *func = funcref->data.symref.ref.global; if ((funcref->type != FAWK_SYMREF) || (func->type != FAWK_FUNC) || ((func->data.func.numargs == func->data.func.numfixedargs) && (func->data.func.numargs < argc))) { while(argc > -2) { POP(); argc--; } return -1; } exec_call(ctx, argc); ctx->ip++; return 0; } FAWK_API void fawk_close_include(fawk_ctx_t *ctx, fawk_src_t *src) { if (src->fn == NULL) return; if (ctx->parser.include != NULL) ctx->parser.include(ctx, src, 0, NULL); fawk_free(ctx, src->fn); src->fn = NULL; } FAWK_API void fawk_init(fawk_ctx_t *ctx) { memset(ctx, 0, sizeof(fawk_ctx_t)); fawk_htpp_init(&ctx->symtab, strhash, strkeyeq); fawk_builtin_init(ctx); ctx->parser.isp = &ctx->parser.include_stack[0]; } FAWK_API void fawk_uninit(fawk_ctx_t *ctx) { fawk_src_t *src; size_t n; fawk_htpp_entry_t *e; fawk_reset(ctx); for(n = 0; n < ctx->stack.used; n++) fawk_free(ctx, ctx->stack.page[n]); fawk_free(ctx, ctx->stack.page); for (e = fawk_htpp_first(&ctx->symtab); e; e = fawk_htpp_next(&ctx->symtab, e)) { fawk_free(ctx, e->key); fawk_cell_free(ctx, e->value); fawk_free(ctx, e->value); } fawk_htpp_uninit(&ctx->symtab); for(n = 0; n < ctx->code.used; n++) { switch(ctx->code.code[n].type) { case FAWKC_INS: case FAWKC_NUM: case FAWKC_CSTR: break; case FAWKC_STR: fawk_str_free(ctx, ctx->code.code[n].data.str); break; case FAWKC_SYMREF: fawk_free(ctx, ctx->code.code[n].data.symref->idx); fawk_free(ctx, ctx->code.code[n].data.symref); break; } } fawk_free(ctx, ctx->code.code); fawk_free(ctx, ctx->parser.buff); for(src = ctx->parser.include_stack; src <= ctx->parser.isp; src++) fawk_close_include(ctx, src); FAWK_PKG_CALL(ctx, p->uninit_cb, (p, ctx)); FAWK_PKG_CALL(ctx, free, (p)); } FAWK_API char *fawk_strdup(fawk_ctx_t *ctx, const char *s) { size_t l = strlen(s); char *ret = fawk_malloc(ctx, l+1); if (ret != NULL) memcpy(ret, s, l+1); return ret; } FAWK_API void fawk_errbuff(fawk_ctx_t *ctx, size_t len) { if (len > ctx->errbuff_alloced) { fawk_free(ctx, ctx->errbuff); ctx->errbuff_alloced = len; ctx->errbuff = fawk_malloc(ctx, len); } if (ctx->errbuff != NULL) *ctx->errbuff = '\0'; } FAWK_API void fawk_dump_cell(fawk_cell_t *cell, int verbose) { switch(cell->type) { case FAWK_NUM: if (verbose) printf("NUM:{"FAWK_NUM_PRINTF_FMT"}", cell->data.num); else printf(FAWK_NUM_PRINTF_FMT, cell->data.num); return; case FAWK_STR: if (verbose) printf("STR:{'%s' (ref=%ld, len=%ld/%ld)}", cell->data.str->str, (long)cell->data.str->refco, (long)cell->data.str->used, (long)cell->data.str->alloced); else printf("%s", cell->data.str->str); return; case FAWK_STRNUM: if (verbose) printf("STRNUM:{"FAWK_NUM_PRINTF_FMT" '%s' (ref=%ld, len=%ld/%ld)}", cell->data.str->num, cell->data.str->str, (long)cell->data.str->refco, (long)cell->data.str->used, (long)cell->data.str->alloced); else printf("%s", cell->data.str->str); return; case FAWK_ARRAY: printf("ARRAY:{uid=%ld len=%ld}", cell->data.arr->uid, (long)cell->data.arr->hash.used); return; case FAWK_FUNC: printf("FUNC:{%s}", cell->data.func.name); return; case FAWK_SYMREF: printf("SYMREF"); return; case FAWK_CCALL_RET: printf("CCAL_RET"); return; case FAWK_NIL: printf("NIL"); return; } printf(""); } static int getch(fawk_ctx_t *ctx) { int ret; if (ctx->parser.pushback > 0) { ret = ctx->parser.pushback; ctx->parser.pushback = -1; } else ret = ctx->parser.get_char(ctx, ctx->parser.isp); if (ret == EOF) ctx->parser.in_eof = 1; else if (ret == '\n') { ctx->parser.isp->line++; ctx->parser.isp->last_col = ctx->parser.isp->col; ctx->parser.isp->col = 0; } else { ctx->parser.isp->col++; } return ret; } static void ungetch(fawk_ctx_t *ctx, int chr) { fawk_assert(ctx->parser.pushback <= 0); ctx->parser.pushback = chr; if (chr == '\n') { ctx->parser.isp->line--; ctx->parser.isp->col = ctx->parser.isp->last_col; } ctx->parser.isp->col--; } #define append(chr,bad) \ do { \ if (ctx->parser.used >= ctx->parser.alloced) { \ char *bf; \ ctx->parser.alloced += 256; \ if ((bf = fawk_realloc(ctx, ctx->parser.buff, ctx->parser.alloced)) == NULL) { ctx->parser.alloced = 0; bad; } \ ctx->parser.buff = bf; \ } \ ctx->parser.buff[ctx->parser.used++] = chr; \ } while(0) #define del_last() ctx->parser.used-- static void fawk_readup(fawk_ctx_t *ctx, const char *accept) { int c; do { c = getch(ctx); append(c, return); } while((c != EOF) && (strchr(accept, c) != NULL)); ungetch(ctx, c); del_last(); } static void readtil(fawk_ctx_t *ctx, const char *reject) { int c; do { c = getch(ctx); append(c, return); } while((c != EOF) && (strchr(reject, c) == NULL)); ungetch(ctx, c); del_last(); } static int read_strlit(fawk_ctx_t *ctx, char term) { int chr, len; for(len = 0;;len++) { chr = getch(ctx); switch(chr) { case '\\': chr = getch(ctx); switch(chr) { case 'n': append('\n', return 0); break; case 't': append('\t', return 0); break; case '0': append('\0', return 0); break; default: append(chr, return 0); } break; default: if ((chr == term) || (chr == EOF)) { append('\0', return 0); return len; } append(chr, return 0); } } } static int read_numeric(fawk_ctx_t *ctx, fawk_num_t *dst, int had_decimal_dot, int numtoken) { int chr, chr2, had_e = 0; next:; chr = getch(ctx); append(chr, return -1); if (isdigit(chr)) goto next; if ((chr == '.') && (!had_decimal_dot)) { had_decimal_dot = 1; goto next; } if (((chr == 'e') || (chr == 'E')) && (!had_e)) { had_e = 1; chr = getch(ctx); append(chr, return -1); if (isdigit(chr)) goto next; if ((chr == '+') || (chr == '-')) { chr2 = getch(ctx); if (isdigit(chr2)) { append(chr2, return -1); goto next; } LIBFAWK_ERROR(ctx, "invalid numeric: e+ or e- must be followed by a digit", ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1, -1); } LIBFAWK_ERROR(ctx, "invalid numeric: e must be followed by sign or digit", ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1, -1); } ungetch(ctx, chr); del_last(); append('\0', return -1); *dst = fawk_strtod(ctx->parser.buff); return numtoken; } #define case_op_ch2(opchr,chr2,eqtok) \ case opchr: \ nchr = getch(ctx); append(chr, return -1); \ if (nchr == chr2) return eqtok; \ else { ungetch(ctx, nchr); return chr; } \ break #define lex_include(restart_stmt) \ if (ctx->parser.include != NULL) { \ char *fn = ctx->parser.buff; \ fawk_readup(ctx, " \t"); \ readtil(ctx, "\""); chr = getch(ctx); \ if (chr == '\"') { ctx->parser.used = 0; readtil(ctx, "\""); append('\0', return -1); getch(ctx); } \ ctx->parser.isp++; \ if (ctx->parser.isp == &ctx->parser.include_stack[FAWK_MAX_INCLUDE_STACK]) { \ sprintf(tmp, "Includes nested too deep\n"); \ LIBFAWK_ERROR(ctx, tmp, ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1, -1); \ } \ ctx->parser.isp->fn = fawk_strdup(ctx, fn); if (ctx->parser.isp->fn == NULL) { return -1; } \ ctx->parser.isp->line = ctx->parser.isp->col = 0; \ if (ctx->parser.include(ctx, ctx->parser.isp, 1, &ctx->parser.isp[-1]) != 0) { \ fawk_free(ctx, ctx->parser.isp->fn); ctx->parser.isp->fn = NULL; \ return -1; \ } \ {restart_stmt;} \ } \ sprintf(tmp, "Include not supported by the host application\n"); \ LIBFAWK_ERROR(ctx, tmp, ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1, -1); #define lex_got_eof(restart_stmt) \ handle_eof:; ctx->parser.in_eof = 0; \ fawk_close_include(ctx, ctx->parser.isp); \ if (ctx->parser.isp == &ctx->parser.include_stack[0]) { fawk_free(ctx, ctx->parser.buff); ctx->parser.buff = NULL; ctx->parser.alloced = ctx->parser.used = 0; return EOF; } \ ctx->parser.isp--; \ { restart_stmt; } #define lex_textblk_start(restart_stmt) \ if ((nchr = getch(ctx)) != '[') { ungetch(ctx, nchr); return '['; } \ ctx->parser.in_textblk = getch(ctx); ctx->parser.textblk_state = 0; restart_stmt; #define lex_textblk_exec(token_str,restart_stmt,nlchar) if (ctx->parser.in_textblk) { \ if (((chr = getch(ctx)) == ']') && (ctx->parser.textblk_state & 4)) { \ if (getch(ctx) != ']') { LIBFAWK_ERROR(ctx, "expected a second ']' for closing text block", ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1, -1); } \ ctx->parser.in_textblk = 0; restart_stmt; \ } \ ctx->parser.textblk_state &= ~4; \ if (chr == ctx->parser.in_textblk) { ctx->parser.textblk_state |= 4; } \ if (ctx->parser.textblk_state & 2) { ungetch(ctx, chr); ctx->parser.textblk_state &= ~2; return '@'; } \ textblk_closechar:; if (ctx->parser.textblk_state & 1) { \ if (chr == ctx->parser.in_textblk) { ctx->parser.textblk_state &= ~1; ctx->parser.textblk_state |= 2; ctx->parser.textblk_state |= 4; restart_stmt; } \ ungetch(ctx, chr); \ } else { \ ctx->parser.textblk_state |= (1 | 2 | 4); \ ungetch(ctx, chr); ctx->parser.used = 0; read_strlit(ctx, ctx->parser.in_textblk); \ lval->str = fawk_strdup(ctx, ctx->parser.buff); return (lval->str == NULL) ? -1 : token_str; \ } \ } \ do { chr = getch(ctx); } while((chr == ' ') || (chr == '\t') || (chr == '\r') || (chr == nlchar)); \ if (chr == ctx->parser.in_textblk) { ctx->parser.textblk_state |= 4; goto textblk_closechar; } #define FAWK_STR_ALLOC(len) \ fawk_malloc(ctx, sizeof(fawk_str_t) + (len)); if (res == NULL) return NULL; \ res->refco = 1; \ res->used = res->alloced = (len); FAWK_API fawk_str_t *fawk_str_new_from_literal(fawk_ctx_t *ctx, const char *s, size_t len_limit) { size_t slen = strlen(s), len = (len_limit == -1) ? slen : ((slen < len_limit) ? slen : len_limit); fawk_str_t *res = FAWK_STR_ALLOC(len); memcpy(res->str, s, len); res->str[len] = '\0'; return res; } FAWK_API fawk_str_t *fawk_str_clone(fawk_ctx_t *ctx, fawk_str_t *src, size_t enlarge) { fawk_str_t *res = FAWK_STR_ALLOC(src->used + enlarge); memcpy(res->str, src->str, src->used+1); res->num = src->num; return res; } FAWK_API fawk_str_t *fawk_str_dup(fawk_ctx_t *ctx, fawk_str_t *src) { src->refco++; if (src->refco == 0) { src->refco--; return fawk_str_clone(ctx, src, 0); } return src; } FAWK_API void fawk_str_free(fawk_ctx_t *ctx, fawk_str_t *src) { fawk_assert(src->refco > 0); src->refco--; if (src->refco == 0) { FAWK_PKG_CALL(ctx, p->str_free_cb, (p, ctx, src)); fawk_free(ctx, src); } } FAWK_API fawk_str_t *fawk_str_concat(fawk_ctx_t *ctx, fawk_str_t *s1, fawk_str_t *s2) { fawk_str_t *res = FAWK_STR_ALLOC(s1->used + s2->used); memcpy(res->str, s1->str, s1->used); memcpy(res->str+s1->used, s2->str, s2->used+1); fawk_str_free(ctx, s1); fawk_str_free(ctx, s2); return res; } FAWK_API fawk_cell_t *fawk_sym_lookup(fawk_ctx_t *ctx, const char *name) { return fawk_htpp_get(&ctx->symtab, name); } #define FAWK_SYMTAB_REG(typ,symname,funcname,err_ret) \ f = fawk_malloc(ctx, sizeof(fawk_cell_t)); if (f == NULL) return err_ret; \ goto setup; setup:; \ f->type = typ; \ f->name = fawk_strdup(ctx, symname); if (f->name == NULL) { fawk_free(ctx, f); return err_ret; } \ f->data.func.cfunc = NULL; \ f->data.func.name = funcname; \ fawk_htpp_set(&ctx->symtab, f->name, f); FAWK_API int fawk_symtab_regcfunc(fawk_ctx_t *ctx, const char *name, fawk_cfunc_t cfunc) { fawk_cell_t *f = fawk_htpp_get(&ctx->symtab, name); if (f != NULL) return -1; FAWK_SYMTAB_REG(FAWK_FUNC, name, f->name, -1); f->data.func.cfunc = cfunc; return 0; } FAWK_API int fawk_symtab_regfunc(fawk_ctx_t *ctx, const char *name, size_t addr, int numargs, int numfixedargs) { fawk_cell_t *f = fawk_htpp_get(&ctx->symtab, name); if (f == NULL) { FAWK_SYMTAB_REG(FAWK_FUNC, name, f->name, -1); f->data.func.ip = addr; f->data.func.numargs = numargs; f->data.func.numfixedargs = numfixedargs; } else { if (f->type != FAWK_FUNC) { if (f->type == FAWK_NIL) goto setup; else FAWK_ERROR(ctx, 64+strlen(name), (FAWK_ERR, "funcreg: '%s' collides with another global symbol\n", name)); } if ((f->data.func.ip == FAWK_CODE_INVALID) && (addr != FAWK_CODE_INVALID)) { f->data.func.ip = addr; f->data.func.numargs = numargs; } } return 0; } FAWK_API fawk_cell_t *fawk_symtab_regvar(fawk_ctx_t *ctx, const char *name, fawk_celltype_t tclass) { fawk_cell_t *f = fawk_htpp_get(&ctx->symtab, name); fawk_assert((tclass == FAWK_SCALAR) || (tclass == FAWK_ARRAY)); if (f == NULL) { FAWK_SYMTAB_REG(tclass, name, NULL, NULL); if (tclass == FAWK_ARRAY) fawk_array_init(ctx, f); } return f; } #ifndef _fawk__defines_h_ #define _fawk__defines_h_ typedef short fawk_int_t; #define fawk_chr yyctx->chr #define fawk_val yyctx->val #define fawk_lval yyctx->lval #define fawk_stack yyctx->stack #define fawk_debug yyctx->debug #define fawk_nerrs yyctx->nerrs #define fawk_errflag yyctx->errflag #define fawk_state yyctx->state #define fawk_yyn yyctx->yyn #define fawk_yym yyctx->yym #define fawk_jump yyctx->jump typedef union fawk_tokunion_u { char *str; fawk_num_t num; } fawk_tokunion_t; typedef fawk_tokunion_t fawk_STYPE; #define T_FUNCTION 257 #define T_EQ 258 #define T_NEQ 259 #define T_GTEQ 260 #define T_LTEQ 261 #define T_PLEQ 262 #define T_MIEQ 263 #define T_MUEQ 264 #define T_DIEQ 265 #define T_MOEQ 266 #define T_NIL 267 #define T_IF 268 #define T_ELSE 269 #define T_FOR 270 #define T_IN 271 #define T_DO 272 #define T_WHILE 273 #define T_RETURN 274 #define T_ID 275 #define T_STRING 276 #define T_NUM 277 #define T_OR 278 #define T_AND 279 #define T_MM 280 #define T_PP 281 #define fawk_ERRCODE 256 #ifndef fawk_INITSTACKSIZE #define fawk_INITSTACKSIZE 200 #endif typedef struct { unsigned stacksize; fawk_int_t *s_base; fawk_int_t *s_mark; fawk_int_t *s_last; fawk_STYPE *l_base; fawk_STYPE *l_mark; } fawk_STACKDATA; typedef struct { int errflag; int chr; fawk_STYPE val; fawk_STYPE lval; int nerrs; int yym, yyn, state; int jump; int stack_max_depth; int debug; fawk_STACKDATA stack; } fawk_yyctx_t; typedef enum { fawk_RES_NEXT, fawk_RES_DONE, fawk_RES_ABORT } fawk_res_t; FAWK_API int fawk_parse_init(fawk_yyctx_t *yyctx); FAWK_API fawk_res_t fawk_parse(fawk_yyctx_t *yyctx, fawk_ctx_t *ctx, int tok, fawk_STYPE *lval); FAWK_API void fawk_error(fawk_ctx_t *ctx, fawk_STYPE tok, const char *msg); #endif FAWK_API int fawk_parse_fawk(fawk_ctx_t *ctx); #ifdef YY_QUERY_API_VER #define YY_BYACCIC #define YY_API_MAJOR 1 #define YY_API_MINOR 0 #endif #define fawk_EMPTY (-1) #define fawk_clearin (fawk_chr = fawk_EMPTY) #define fawk_errok (fawk_errflag = 0) #define fawk_RECOVERING() (fawk_errflag != 0) #define fawk_ENOMEM (-2) #define fawk_EOF 0 static const fawk_int_t fawk_lhs[] = {-1,2,0,1,1,4,6,3,5,5,5,5,5,8,8,8,8,8,8,8,8,8,7,7,10,15,10,16,14,11,11,20,18,22,23,24,17,25,13,26,27,12,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,21,21,35,19,33,33,33,33,34,34,34,34,34,36,37,36,39,28,38,38,38,40,41,30,42,31,43,32,29,44,29,45,29,46,29,47,29,48,29,}; static const fawk_int_t fawk_len[] = {2,0,2,2,0,0,0,10,0,1,3,3,5,2,1,1,1,1,3,2,3,1,0,2,1,0,4,0,6,1,1,0,8,0,0,0,12,0,7,0,0,7,1,1,1,1,2,1,1,3,3,3,3,3,3,3,3,1,1,1,3,3,3,3,3,2,2,2,3,1,1,0,0,3,2,2,2,2,0,3,4,3,3,1,0,4,0,5,0,1,3,0,0,7,0,4,0,4,3,0,4,0,4,0,4,0,4,0,4,}; static const fawk_int_t fawk_defred[] = {1,0,0,0,2,0,5,3,0,0,9,0,0,0,6,0,10,0,11,0,22,0,0,12,44,0,0,37,39,0,72,43,42,0,0,0,0,0,0,22,7,21,0,23,0,14,15,16,17,0,29,30,0,47,48,57,58,59,69,0,0,0,0,19,0,78,0,0,0,77,76,0,0,46,0,0,0,0,0,91,94,96,0,0,0,0,0,0,0,0,86,13,25,99,101,103,105,107,0,75,74,0,0,0,0,0,0,18,0,49,20,0,0,0,0,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,0,33,0,0,0,0,0,0,0,0,0,26,0,0,0,0,0,0,0,0,0,40,81,82,79,0,0,92,0,87,28,31,0,0,0,84,80,0,90,0,34,38,41,0,0,32,0,0,0,35,0,36,}; static const fawk_int_t fawk_dgoto[] = {1,4,2,5,8,12,17,22,43,44,45,46,47,48,49,128,153,50,51,52,175,104,155,182,186,61,62,170,53,54,55,56,57,58,108,65,162,179,146,127,116,173,117,118,129,130,131,132,133,}; static const fawk_int_t fawk_sindex[] = {0,0,-249,-259,0,-249,0,0,-19,-44,0,13,2,18,0,-43,0,-38,0,45,0,62,-33,0,0,69,72,0,0,27,0,0,0,55,55,55,-162,-162,55,0,0,0,-162,0,430,0,0,0,0,-155,0,0,366,0,0,0,0,0,0,55,55,12,78,0,439,0,64,64,79,0,0,467,-18,0,55,55,55,55,-162,0,0,0,55,55,55,55,55,55,55,55,0,0,0,0,0,0,0,0,55,0,0,476,580,35,71,-150,55,0,-13,0,0,181,181,181,181,0,55,55,55,181,181,361,64,64,79,79,79,55,12,55,55,55,55,55,580,0,-162,0,92,504,-237,44,513,790,1039,541,93,0,580,580,580,580,580, 12,95,55,55,0,0,0,0,580,-31,0,55,0,0,0,74,550,12,0,0,55,0,12,0,0,0,55,580,0,55,580,97,0,12,0,}; static const fawk_int_t fawk_rindex[] = {0,0,139,0,0,139,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,0,112,0,0,0,0,0,0,0,81,0,0,0,0,0,812,836,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-35,608,0,0,0,0,84,0,0,908,931,937,963,0,0,0,0,969,995,899,863,872,153,378,402,100,0,0,0,0,0,0,-30,0,0,0,0,0,0,0,0,-40,17,109,0,0,-10,10,58,268,293,0,0,81,0,0,0,0,0,-27,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,316,0,119,-12,0,0,0,0,}; static const fawk_int_t fawk_gindex[] = {0,162,0,0,0,0,0,130,33,1229,0,0,0,0,0,0,0,0,0,37,0,-146,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,}; #define fawk_TABLESIZE 1411 static const fawk_int_t fawk_table[] = {35,95,11,19,95,42,70,38,3,168,33,98,34,171,98,35,6,83,95,95,42,9,38,95,70,33,41,34,98,98,24,100,85,140,100,24,184,24,158,159,24,41,24,14,8,35,15,8,100,100,42,102,38,95,102,33,24,34,97,13,35,97,172,98,16,42,83,38,102,102,33,41,34,69,70,97,97,35,141,73,97,85,42,100,38,20,63,33,35,34,39,21,40,42,105,38,98,103,33,104,34,89,104,102,90,39,87,110,23,59,97,88,60,30,92,115,104,104,106,90,24,73,24,138,73,73,73,73,73,73,137,73,156,176,165,39,167,160,185,4,71,88, 73,73,73,73,73,73,73,45,89,104,45,45,45,45,45,45,67,45,71,147,67,67,67,67,67,7,67,72,45,45,45,154,45,45,45,73,174,67,67,67,0,67,67,67,166,0,0,0,62,0,0,0,62,62,62,62,62,0,62,0,0,178,0,45,0,0,181,0,0,62,62,62,67,62,62,62,89,187,0,90,0,87,85,0,86,0,88,0,0,10,18,0,24,25,0,26,95,27,28,29,30,31,32,84,62,36,37,24,25,0,26,0,27,28,29,30,31,32,0,0,36,37,24,24,0,24,0,24,24,24,24,24,24,0,0,24,24,24,25,0,26,0,27,28,29,30,31,32,0,0,36,37,24,97,97,93,94,95,96,97,30,31,32,0,136,36,37,106,0,24,106,0,0,99,100, 0,0,30,31,32,24,0,36,37,106,106,0,0,30,31,32,0,108,36,37,108,0,0,0,0,73,73,73,73,73,73,73,73,73,108,108,0,0,73,0,93,0,0,93,106,73,73,73,73,0,0,0,0,45,45,45,45,93,93,0,0,0,67,67,67,67,45,0,0,108,0,0,0,45,45,67,0,0,0,0,0,89,67,67,90,0,87,85,0,86,0,88,93,0,62,62,62,62,63,0,0,0,63,63,63,63,63,62,63,0,98,0,0,0,62,62,0,0,0,63,63,63,64,63,63,63,64,64,64,64,64,0,64,0,0,0,0,0,0,0,0,0,0,64,64,64,0,64,64,64,89,0,0,90,63,87,85,0,86,89,88,0,90,0,87,85,0,86,0,88,0,0,91,82,0,83,79,84,64,0,0,107,82,0,83,79, 84,89,0,0,90,109,87,85,0,86,89,88,0,90,135,87,85,0,86,0,88,0,0,0,82,0,83,79,84,0,0,0,0,82,0,83,79,84,89,0,0,90,157,87,85,0,86,89,88,0,90,0,87,85,0,86,0,88,0,0,0,82,0,83,79,84,0,0,163,0,82,0,83,79,84,89,0,0,90,0,87,85,164,86,89,88,0,90,177,87,85,0,86,0,88,0,0,0,82,0,83,79,84,0,0,0,0,82,0,83,79,84,0,0,89,0,0,90,0,87,85,0,86,0,88,93,94,95,96,97,0,0,0,63,63,63,63,82,0,83,79,84,45,99,100,45,63,45,45,0,45,0,45,63,63,0,0,64,64,64,64,0,0,0,45,45,0,45,45,45,64,0,0,0,0,0,0,64,64,0,0,0,0,0,0,74,75,76, 77,0,0,0,0,0,74,75,76,77,78,0,0,0,0,0,0,80,81,78,0,0,0,0,0,0,80,81,0,0,0,0,0,0,74,75,76,77,0,0,0,0,0,74,75,76,77,78,0,0,0,0,0,0,80,81,78,0,0,0,0,0,0,80,81,0,0,0,0,0,0,74,75,76,77,0,0,0,0,0,74,75,76,77,78,0,0,0,0,0,0,80,81,78,0,0,0,0,0,0,80,81,0,0,0,0,0,0,74,75,76,77,0,0,0,0,0,74,75,76,77,78,0,0,0,0,0,0,80,81,78,0,0,0,0,0,89,80,81,90,0,87,85,0,86,0,88,74,75,76,77,0,0,0,0,0,0,0,0,82,78,83,66,84,66,66,66,80,81,0,0,0,0,0,0,45,45,45,45,66,66,66,0,66,66,66,65,0,65,65,65,0,0,0,0,45,45,0,0,0,0,0,0, 65,65,65,0,65,65,65,0,0,0,60,66,60,60,60,0,0,0,0,61,0,61,61,61,0,0,0,60,60,60,0,60,60,60,0,65,61,61,61,0,61,61,61,0,0,0,68,0,0,68,0,0,0,0,0,50,0,0,50,0,0,0,60,68,68,68,0,68,68,68,0,61,50,50,50,0,50,50,51,0,0,51,0,0,52,0,0,52,0,0,0,0,0,0,0,51,51,51,68,51,51,52,52,52,0,52,52,50,0,0,53,0,0,53,0,0,55,0,0,55,0,0,0,0,0,0,0,53,53,53,51,53,53,55,55,55,52,55,55,0,0,0,54,0,0,54,0,0,0,0,0,0,0,0,74,75,76,77,0,54,54,54,53,54,54,0,0,78,55,0,0,0,0,0,0,81,66,66,66,66,0,0,89,0,0,90,0,87,85,66,86,0,88,0,54, 0,66,66,0,0,65,65,65,65,0,82,0,83,0,84,0,0,0,65,0,0,0,0,0,0,65,65,0,0,0,0,0,60,60,60,60,0,0,0,0,0,61,61,61,61,60,0,0,0,0,0,0,60,60,61,0,0,0,0,0,0,61,61,0,0,0,0,0,68,68,68,68,0,0,0,0,0,50,50,50,50,68,0,0,0,0,0,0,68,68,50,0,0,0,0,0,0,50,50,0,51,51,51,51,0,0,52,52,52,52,0,0,0,51,0,0,0,0,0,52,51,51,0,0,0,0,52,52,0,0,0,0,53,53,53,53,0,0,55,55,55,55,0,0,0,53,0,0,0,0,0,55,53,53,0,0,0,0,55,55,0,0,0,0,54,54,54,54,0,64,0,0,0,66,67,68,0,54,71,0,0,0,0,0,54,54,0,0,0,0,0,0,0,0,0,0,0,0,0,101,102,0,0,0,0, 0,0,0,74,75,76,77,0,0,111,112,113,114,0,0,0,78,119,120,121,122,123,124,125,126,0,0,0,0,0,0,0,0,134,0,0,0,0,0,0,0,139,0,0,0,0,0,0,0,0,0,142,143,144,0,0,0,0,0,0,0,0,145,0,148,149,150,151,152,0,0,0,0,0,0,0,161,0,0,0,0,0,0,0,0,0,0,0,0,0,102,169,0,0,0,0,0,0,0,145,0,0,0,0,0,0,0,0,180,0,0,0,0,0,183,0,0,102,}; static const fawk_int_t fawk_check[] = {33,41,46,46,44,38,41,40,257,155,43,41,45,44,44,33,275,44,58,59,38,40,40,63,59,43,59,45,58,59,33,41,44,46,44,38,182,40,275,276,43,59,45,41,41,33,44,44,58,59,38,41,40,93,44,43,59,45,41,46,33,44,93,93, 46,38,93,40,58,59,43,59,45,36,37,58,59,33,91,42,63,93,38,93,40,123,59,43,33,45,123,46,125,38,61,40,61,60,43,41,45,37,44,93,40,123,42,125,46,40,93,47,40,275,269,78,58,59,40,40,123,37,125,273,40,41,42,43,44,45,59,47,40,59,41,123,41,93,41,0,59,41,58,59,60,61,62,63,64,37,41,93,40,41,42,43,44,45,37,47,41,128,41,42,43,44,45,5,47,39,58,59,60,136,62,63,64,93,164,58,59,60,-1,62,63,64,153,-1,-1,-1,37,-1,-1,-1,41,42,43,44,45,-1,47,-1,-1,170,-1,93,-1,-1,175,-1,-1,58,59,60,93,62,63,64,37,186,-1,40,-1,42, 43,-1,45,-1,47,-1,-1,275,275,-1,267,268,-1,270,278,272,273,274,275,276,277,64,93,280,281,267,268,-1,270,-1,272,273,274,275,276,277,-1,-1,280,281,267,268,-1,270,-1,272,273,274,275,276,277,-1,-1,280,281,267,268,-1,270,-1,272,273,274,275,276,277,-1,-1,280,281,267,278,279,262,263,264,265,266,275,276,277,-1,271,280,281,41,-1,267,44,-1,-1,280,281,-1,-1,275,276,277,267,-1,280,281,58,59,-1,-1,275,276,277,-1,41,280,281,44,-1,-1,-1,-1,258,259,260,261,262,263,264,265,266,58,59,-1,-1,271,-1,41,-1,-1,44, 93,278,279,280,281,-1,-1,-1,-1,258,259,260,261,58,59,-1,-1,-1,258,259,260,261,271,-1,-1,93,-1,-1,-1,278,279,271,-1,-1,-1,-1,-1,37,278,279,40,-1,42,43,-1,45,-1,47,93,-1,258,259,260,261,37,-1,-1,-1,41,42,43,44,45,271,47,-1,61,-1,-1,-1,278,279,-1,-1,-1,58,59,60,37,62,63,64,41,42,43,44,45,-1,47,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,58,59,60,-1,62,63,64,37,-1,-1,40,93,42,43,-1,45,37,47,-1,40,-1,42,43,-1,45,-1,47,-1,-1,59,60,-1,62,63,64,93,-1,-1,59,60,-1,62,63,64,37,-1,-1,40,41,42,43,-1,45,37,47,-1,40,41, 42,43,-1,45,-1,47,-1,-1,-1,60,-1,62,63,64,-1,-1,-1,-1,60,-1,62,63,64,37,-1,-1,40,41,42,43,-1,45,37,47,-1,40,-1,42,43,-1,45,-1,47,-1,-1,-1,60,-1,62,63,64,-1,-1,58,-1,60,-1,62,63,64,37,-1,-1,40,-1,42,43,44,45,37,47,-1,40,41,42,43,-1,45,-1,47,-1,-1,-1,60,-1,62,63,64,-1,-1,-1,-1,60,-1,62,63,64,-1,-1,37,-1,-1,40,-1,42,43,-1,45,-1,47,262,263,264,265,266,-1,-1,-1,258,259,260,261,60,-1,62,63,64,37,280,281,40,271,42,43,-1,45,-1,47,278,279,-1,-1,258,259,260,261,-1,-1,-1,59,60,-1,62,63,64,271,-1,-1,-1, -1,-1,-1,278,279,-1,-1,-1,-1,-1,-1,258,259,260,261,-1,-1,-1,-1,-1,258,259,260,261,271,-1,-1,-1,-1,-1,-1,278,279,271,-1,-1,-1,-1,-1,-1,278,279,-1,-1,-1,-1,-1,-1,258,259,260,261,-1,-1,-1,-1,-1,258,259,260,261,271,-1,-1,-1,-1,-1,-1,278,279,271,-1,-1,-1,-1,-1,-1,278,279,-1,-1,-1,-1,-1,-1,258,259,260,261,-1,-1,-1,-1,-1,258,259,260,261,271,-1,-1,-1,-1,-1,-1,278,279,271,-1,-1,-1,-1,-1,-1,278,279,-1,-1,-1,-1,-1,-1,258,259,260,261,-1,-1,-1,-1,-1,258,259,260,261,271,-1,-1,-1,-1,-1,-1,278,279,271,-1, -1,-1,-1,-1,37,278,279,40,-1,42,43,-1,45,-1,47,258,259,260,261,-1,-1,-1,-1,-1,-1,-1,-1,60,271,62,41,64,43,44,45,278,279,-1,-1,-1,-1,-1,-1,258,259,260,261,58,59,60,-1,62,63,64,41,-1,43,44,45,-1,-1,-1,-1,278,279,-1,-1,-1,-1,-1,-1,58,59,60,-1,62,63,64,-1,-1,-1,41,93,43,44,45,-1,-1,-1,-1,41,-1,43,44,45,-1,-1,-1,58,59,60,-1,62,63,64,-1,93,58,59,60,-1,62,63,64,-1,-1,-1,41,-1,-1,44,-1,-1,-1,-1,-1,41,-1,-1,44,-1,-1,-1,93,58,59,60,-1,62,63,64,-1,93,58,59,60,-1,62,63,41,-1,-1,44,-1,-1,41,-1,-1,44,-1, -1,-1,-1,-1,-1,-1,58,59,60,93,62,63,58,59,60,-1,62,63,93,-1,-1,41,-1,-1,44,-1,-1,41,-1,-1,44,-1,-1,-1,-1,-1,-1,-1,58,59,60,93,62,63,58,59,60,93,62,63,-1,-1,-1,41,-1,-1,44,-1,-1,-1,-1,-1,-1,-1,-1,258,259,260,261,-1,58,59,60,93,62,63,-1,-1,271,93,-1,-1,-1,-1,-1,-1,279,258,259,260,261,-1,-1,37,-1,-1,40,-1,42,43,271,45,-1,47,-1,93,-1,278,279,-1,-1,258,259,260,261,-1,60,-1,62,-1,64,-1,-1,-1,271,-1,-1,-1,-1,-1,-1,278,279,-1,-1,-1,-1,-1,258,259,260,261,-1,-1,-1,-1,-1,258,259,260,261,271,-1,-1,-1, -1,-1,-1,278,279,271,-1,-1,-1,-1,-1,-1,278,279,-1,-1,-1,-1,-1,258,259,260,261,-1,-1,-1,-1,-1,258,259,260,261,271,-1,-1,-1,-1,-1,-1,278,279,271,-1,-1,-1,-1,-1,-1,278,279,-1,258,259,260,261,-1,-1,258,259,260,261,-1,-1,-1,271,-1,-1,-1,-1,-1,271,278,279,-1,-1,-1,-1,278,279,-1,-1,-1,-1,258,259,260,261,-1,-1,258,259,260,261,-1,-1,-1,271,-1,-1,-1,-1,-1,271,278,279,-1,-1,-1,-1,278,279,-1,-1,-1,-1,258,259,260,261,-1,29,-1,-1,-1,33,34,35,-1,271,38,-1,-1,-1,-1,-1,278,279,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,59,60,-1,-1,-1,-1,-1,-1,-1,258,259,260,261,-1,-1,74,75,76,77,-1,-1,-1,271,82,83,84,85,86,87,88,89,-1,-1,-1,-1,-1,-1,-1,-1,98,-1,-1,-1,-1,-1,-1,-1,106,-1,-1,-1,-1,-1,-1,-1,-1,-1,116,117,118,-1,-1,-1,-1,-1,-1,-1,-1,127,-1,129,130,131,132,133,-1,-1,-1,-1,-1,-1,-1,141,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,155,156,-1,-1,-1,-1,-1,-1,-1,164,-1,-1,-1,-1,-1,-1,-1,-1,173,-1,-1,-1,-1,-1,179,-1,-1,182,}; #define fawk_FINAL 1 #define fawk_MAXTOKEN 281 #define fawk_UNDFTOKEN 332 #define fawk_TRANSLATE(a) ((a) > fawk_MAXTOKEN ? fawk_UNDFTOKEN : (a)) FAWK_API void fawk_error(fawk_ctx_t *ctx, fawk_STYPE tok, const char *msg) { libfawk_error(ctx, msg, ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1); } FAWK_API int fawk_lex_fawk(fawk_STYPE *lval, fawk_ctx_t *ctx); FAWK_API int fawk_parse_fawk(fawk_ctx_t *ctx) { fawk_parser_loop(fawk_yyctx_t, fawk_STYPE, fawk_lex_fawk, fawk_parse, ctx, fawk_RES_NEXT, fawk_RES_DONE); } static int fawk_growstack(fawk_yyctx_t *yyctx, fawk_STACKDATA *data) { int i; unsigned newsize; fawk_int_t *newss; fawk_STYPE *newvs; if ((newsize = data->stacksize) == 0) newsize = fawk_INITSTACKSIZE; else if (newsize >= yyctx->stack_max_depth) return fawk_ENOMEM; else if ((newsize *= 2) > yyctx->stack_max_depth) newsize = yyctx->stack_max_depth; i = (int)(data->s_mark - data->s_base); newss = (fawk_int_t *) realloc(data->s_base, newsize * sizeof(*newss)); if (newss == 0) return fawk_ENOMEM; data->s_base = newss; data->s_mark = newss + i; newvs = (fawk_STYPE *) realloc(data->l_base, newsize * sizeof(*newvs)); if (newvs == 0) return fawk_ENOMEM; data->l_base = newvs; data->l_mark = newvs + i; data->stacksize = newsize; data->s_last = data->s_base + newsize - 1; return 0; } static void fawk_freestack(fawk_STACKDATA *data) { free(data->s_base); free(data->l_base); memset(data, 0, sizeof(*data)); } #define fawk_ABORT goto yyabort #define fawk_REJECT goto yyabort #define fawk_ACCEPT goto yyaccept #define fawk_ERROR goto yyerrlab int fawk_parse_init(fawk_yyctx_t *yyctx) { memset(&yyctx->val, 0, sizeof(yyctx->val)); memset(&yyctx->lval, 0, sizeof(yyctx->lval)); yyctx->yym = 0; yyctx->yyn = 0; yyctx->nerrs = 0; yyctx->errflag = 0; yyctx->chr = fawk_EMPTY; yyctx->state = 0; memset(&yyctx->stack, 0, sizeof(yyctx->stack)); yyctx->stack_max_depth = fawk_INITSTACKSIZE > 10000 ? fawk_INITSTACKSIZE : 10000; if (yyctx->stack.s_base == NULL && fawk_growstack(yyctx, &yyctx->stack) == fawk_ENOMEM) return -1; yyctx->stack.s_mark = yyctx->stack.s_base; yyctx->stack.l_mark = yyctx->stack.l_base; yyctx->state = 0; *yyctx->stack.s_mark = 0; yyctx->jump = 0; return 0; } #define fawk_GETCHAR(labidx) \ do { \ if (used) { yyctx->jump = labidx; return fawk_RES_NEXT; } \ getchar_ ## labidx:; yyctx->chr = tok; yyctx->lval = *lval; used = 1; \ } while(0) fawk_res_t fawk_parse(fawk_yyctx_t *yyctx, fawk_ctx_t *ctx, int tok, fawk_STYPE *lval) { int used = 0; yyloop:; if (yyctx->jump == 1) { yyctx->jump = 0; goto getchar_1; } if (yyctx->jump == 2) { yyctx->jump = 0; goto getchar_2; } if ((yyctx->yyn = fawk_defred[yyctx->state]) != 0) goto yyreduce; if (yyctx->chr < 0) { fawk_GETCHAR(1); if (yyctx->chr < 0) yyctx->chr = fawk_EOF; } if (((yyctx->yyn = fawk_sindex[yyctx->state]) != 0) && (yyctx->yyn += yyctx->chr) >= 0 && yyctx->yyn <= fawk_TABLESIZE && fawk_check[yyctx->yyn] == (fawk_int_t) yyctx->chr) { if (yyctx->stack.s_mark >= yyctx->stack.s_last && fawk_growstack(yyctx, &yyctx->stack) == fawk_ENOMEM) goto yyoverflow; yyctx->state = fawk_table[yyctx->yyn]; *++yyctx->stack.s_mark = fawk_table[yyctx->yyn]; *++yyctx->stack.l_mark = yyctx->lval; yyctx->chr = fawk_EMPTY; if (yyctx->errflag > 0) --yyctx->errflag; goto yyloop; } if (((yyctx->yyn = fawk_rindex[yyctx->state]) != 0) && (yyctx->yyn += yyctx->chr) >= 0 && yyctx->yyn <= fawk_TABLESIZE && fawk_check[yyctx->yyn] == (fawk_int_t) yyctx->chr) { yyctx->yyn = fawk_table[yyctx->yyn]; goto yyreduce; } if (yyctx->errflag != 0) goto yyinrecovery; fawk_error(ctx, yyctx->lval, "syntax error"); goto yyerrlab; yyerrlab: ++yyctx->nerrs; yyinrecovery: if (yyctx->errflag < 3) { yyctx->errflag = 3; for(;;) { if (((yyctx->yyn = fawk_sindex[*yyctx->stack.s_mark]) != 0) && (yyctx->yyn += fawk_ERRCODE) >= 0 && yyctx->yyn <= fawk_TABLESIZE && fawk_check[yyctx->yyn] == (fawk_int_t) fawk_ERRCODE) { if (yyctx->stack.s_mark >= yyctx->stack.s_last && fawk_growstack(yyctx, &yyctx->stack) == fawk_ENOMEM) goto yyoverflow; yyctx->state = fawk_table[yyctx->yyn]; *++yyctx->stack.s_mark = fawk_table[yyctx->yyn]; *++yyctx->stack.l_mark = yyctx->lval; goto yyloop; } else { if (yyctx->stack.s_mark <= yyctx->stack.s_base) goto yyabort; --yyctx->stack.s_mark; --yyctx->stack.l_mark; } } } else { if (yyctx->chr == fawk_EOF) goto yyabort; yyctx->chr = fawk_EMPTY; goto yyloop; } yyreduce: yyctx->yym = fawk_len[yyctx->yyn]; if (yyctx->yym > 0) yyctx->val = yyctx->stack.l_mark[1 - yyctx->yym]; else memset(&yyctx->val, 0, sizeof yyctx->val); switch (yyctx->yyn) { case 1: { fawkc_addi(ctx, FAWKI_ABORT); fawkc_addcs(ctx, "uninitialized execute"); } break; case 2: { fawkc_addi(ctx, FAWKI_ABORT); fawkc_addcs(ctx, "ran beyond the script"); } break; case 5: { ctx->compiler.numfixedargs = -1; ctx->compiler.numargs = 0; } break; case 6: { fawk_symtab_regfunc(ctx, yyctx->stack.l_mark[-4].str, FAWK_CURR_IP(), ctx->compiler.numargs, ctx->compiler.numfixedargs); fawk_free(ctx, yyctx->stack.l_mark[-4].str); ctx->fp = ctx->sp; } break; case 7: { int n; fawkc_addi(ctx, FAWKI_PUSH_NIL); fawkc_addi(ctx, FAWKI_RET); fawkc_addnum(ctx, ctx->compiler.numargs + (ctx->compiler.numfixedargs >= 0)); for(n = 0; n < ctx->compiler.numargs + (ctx->compiler.numfixedargs >= 0); n++) { fawk_cell_t cell; fawk_pop(ctx, &cell); fawk_cell_free(ctx, &cell); } ctx->fp = 0; } break; case 9: { fawk_push_str(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); ctx->compiler.numargs++; } break; case 10: { ctx->compiler.numfixedargs = ctx->compiler.numargs; fawk_push_str(ctx, "VARARG"); } break; case 11: { fawk_push_str(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); ctx->compiler.numargs++; } break; case 12: { ctx->compiler.numfixedargs = ctx->compiler.numargs; fawk_push_str(ctx, "VARARG"); } break; case 13: { fawkc_addi(ctx, FAWKI_POP); } break; case 18: { fawkc_addi(ctx, FAWKI_RET); fawkc_addnum(ctx, ctx->compiler.numargs+ (ctx->compiler.numfixedargs >= 0)); } break; case 19: { fawkc_addi(ctx, FAWKI_PUSH_NIL); fawkc_addi(ctx, FAWKI_RET); fawkc_addnum(ctx, ctx->compiler.numargs+ (ctx->compiler.numfixedargs >= 0)); } break; case 24: { size_t jmp1 = fawk_pop_num(ctx, 1); ctx->code.code[jmp1].data.num = FAWK_CURR_IP(); } break; case 25: { fawkc_addi(ctx, FAWKI_JMP); FAWK_PUSH_IP(); fawkc_addnum(ctx, 777); } break; case 26: { size_t jmp_then_post = fawk_pop_num(ctx, 1), jmp_if = fawk_pop_num(ctx, 1); ctx->code.code[jmp_then_post].data.num = FAWK_CURR_IP(); ctx->code.code[jmp_if].data.num = jmp_then_post+1; } break; case 27: { fawkc_addi(ctx, FAWKI_POPJZ); FAWK_PUSH_IP(); fawkc_addnum(ctx, 777); } break; case 31: { fawkc_addi(ctx, FAWKI_FORIN_FIRST); fawkc_addi(ctx, FAWKI_JMP); fawkc_addnum(ctx, 0); FAWK_PUSH_IP(); } break; case 32: { size_t begin = fawk_pop_num(ctx, 1); fawkc_addi(ctx, FAWKI_FORIN_NEXT); fawkc_addnum(ctx, begin); ctx->code.code[begin-1].data.num = FAWK_CURR_IP()-2; } break; case 33: { fawkc_addi(ctx, FAWKI_POP); FAWK_PUSH_IP(); } break; case 34: { fawkc_addi(ctx, FAWKI_POPJZ); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); fawkc_addi(ctx, FAWKI_JMP); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); } break; case 35: { fawkc_addi(ctx, FAWKI_POP); fawkc_addi(ctx, FAWKI_JMP); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); } break; case 36: { size_t jback3rd, jskip, jout, rerun; jback3rd = fawk_pop_num(ctx, 1); jskip = fawk_pop_num(ctx, 1); jout = fawk_pop_num(ctx, 1); rerun = fawk_pop_num(ctx, 1); fawkc_addi(ctx, FAWKI_JMP); fawkc_addnum(ctx, jskip+1); ctx->code.code[jback3rd].data.num = rerun; ctx->code.code[jskip].data.num = jback3rd+1; ctx->code.code[jout].data.num = FAWK_CURR_IP(); } break; case 37: { FAWK_PUSH_IP(); } break; case 38: { fawkc_addi(ctx, FAWKI_POPJNZ); fawkc_addnum(ctx, fawk_pop_num(ctx, 1)); } break; case 39: { FAWK_PUSH_IP(); } break; case 40: { fawkc_addi(ctx, FAWKI_POPJZ); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); } break; case 41: { loop_pretest_jumpback(); } break; case 42: { fawkc_addi(ctx, FAWKI_PUSH_NUM); fawkc_addnum(ctx, yyctx->stack.l_mark[0].num); } break; case 43: { fawkc_addi(ctx, FAWKI_PUSH_STR); fawkc_adds(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); } break; case 44: { fawkc_addi(ctx, FAWKI_PUSH_NIL); } break; case 45: { fawkc_addi(ctx, FAWKI_PUSH_SYMVAL); } break; case 50: { fawkc_addi(ctx, FAWKI_EQ); } break; case 51: { fawkc_addi(ctx, FAWKI_NEQ); } break; case 52: { fawkc_addi(ctx, FAWKI_GTEQ); } break; case 53: { fawkc_addi(ctx, FAWKI_LTEQ); } break; case 54: { fawkc_addi(ctx, FAWKI_GT); } break; case 55: { fawkc_addi(ctx, FAWKI_LT); } break; case 56: { fawkc_addi(ctx, FAWKI_IN); } break; case 60: { fawkc_addi(ctx, FAWKI_ADD); } break; case 61: { fawkc_addi(ctx, FAWKI_SUB); } break; case 62: { fawkc_addi(ctx, FAWKI_MUL); } break; case 63: { fawkc_addi(ctx, FAWKI_DIV); } break; case 64: { fawkc_addi(ctx, FAWKI_MOD); } break; case 65: { fawkc_addi(ctx, FAWKI_NEG); } break; case 67: { fawkc_addi(ctx, FAWKI_NOT); } break; case 68: { fawkc_addi(ctx, FAWKI_CONCAT); } break; case 71: { fawkc_addi(ctx, FAWKI_PUSH_NUM); fawkc_addnum(ctx, 1); } break; case 72: { fawk_push_num(ctx, ctx->compiler.numidx); ctx->compiler.numidx = 0; } break; case 73: { fawkc_addi(ctx, FAWKI_MAKE_SYMREF); fawkc_addsymref(ctx, yyctx->stack.l_mark[-2].str, ctx->compiler.numidx, 0); fawkc_addnum(ctx, ctx->compiler.numidx); fawk_free(ctx, yyctx->stack.l_mark[-2].str); ctx->compiler.numidx = fawk_pop_num(ctx, 1); } break; case 74: { fawkc_addi(ctx, FAWKI_INCDEC); fawkc_addnum(ctx, FAWK_INCDEC_INC | FAWK_INCDEC_POST); } break; case 75: { fawkc_addi(ctx, FAWKI_INCDEC); fawkc_addnum(ctx, FAWK_INCDEC_POST); } break; case 76: { fawkc_addi(ctx, FAWKI_INCDEC); fawkc_addnum(ctx, FAWK_INCDEC_INC); } break; case 77: { fawkc_addi(ctx, FAWKI_INCDEC); fawkc_addnum(ctx, 0); } break; case 79: { ctx->compiler.numidx = -1; } break; case 80: { ctx->compiler.numidx++; } break; case 81: { ctx->compiler.numidx++; fawkc_addi(ctx, FAWKI_PUSH_STR); fawkc_adds(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); } break; case 82: { ctx->compiler.numidx++; fawkc_addi(ctx, FAWKI_PUSH_STR); fawkc_adds(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); } break; case 84: { parse_aix_expr(); } break; case 85: { fawkc_addi(ctx, FAWKI_CONCAT); } break; case 86: { fawk_push_num(ctx, ctx->compiler.numargs); ctx->compiler.numargs = 0; ctx->code.used--; } break; case 87: { size_t old_numargs = fawk_pop_num(ctx, 1); fawkc_addi(ctx, FAWKI_CALL); fawkc_addnum(ctx, ctx->compiler.numargs); ctx->compiler.numargs = old_numargs; } break; case 89: { ctx->compiler.numargs++; } break; case 90: { ctx->compiler.numargs++; } break; case 91: { fawkc_addi(ctx, FAWKI_POPJZ); FAWK_PUSH_IP(); fawkc_addnum(ctx, 777); } break; case 92: { fawkc_addi(ctx, FAWKI_JMP); FAWK_PUSH_IP(); fawkc_addnum(ctx, 888); } break; case 93: { size_t jmp1, jmp2; jmp2 = fawk_pop_num(ctx, 1); jmp1 = fawk_pop_num(ctx, 1); ctx->code.code[jmp1].data.num = jmp2+1; ctx->code.code[jmp2].data.num = FAWK_CURR_IP(); } break; case 94: { lazy_binop1(ctx, 1); } break; case 95: { lazy_binop2(ctx, 1); } break; case 96: { lazy_binop1(ctx, 0); } break; case 97: { lazy_binop2(ctx, 0); } break; case 98: { fawkc_addi(ctx, FAWKI_SET); fawkc_addi(ctx, FAWKI_PUSH_SYMVAL); } break; case 99: { fawkc_addi(ctx, FAWKI_PUSH_TOPVAR); } break; case 100: { fawkc_addi(ctx, FAWKI_ADD); fawkc_addi(ctx, FAWKI_SET); } break; case 101: { fawkc_addi(ctx, FAWKI_PUSH_TOPVAR); } break; case 102: { fawkc_addi(ctx, FAWKI_SUB); fawkc_addi(ctx, FAWKI_SET); } break; case 103: { fawkc_addi(ctx, FAWKI_PUSH_TOPVAR); } break; case 104: { fawkc_addi(ctx, FAWKI_MUL); fawkc_addi(ctx, FAWKI_SET); } break; case 105: { fawkc_addi(ctx, FAWKI_PUSH_TOPVAR); } break; case 106: { fawkc_addi(ctx, FAWKI_DIV); fawkc_addi(ctx, FAWKI_SET); } break; case 107: { fawkc_addi(ctx, FAWKI_PUSH_TOPVAR); } break; case 108: { fawkc_addi(ctx, FAWKI_MOD); fawkc_addi(ctx, FAWKI_SET); } break; } yyctx->stack.s_mark -= yyctx->yym; yyctx->state = *yyctx->stack.s_mark; yyctx->stack.l_mark -= yyctx->yym; yyctx->yym = fawk_lhs[yyctx->yyn]; if (yyctx->state == 0 && yyctx->yym == 0) { yyctx->state = fawk_FINAL; *++yyctx->stack.s_mark = fawk_FINAL; *++yyctx->stack.l_mark = yyctx->val; if (yyctx->chr < 0) { fawk_GETCHAR(2); if (yyctx->chr < 0) yyctx->chr = fawk_EOF; } if (yyctx->chr == fawk_EOF) goto yyaccept; goto yyloop; } if (((yyctx->yyn = fawk_gindex[yyctx->yym]) != 0) && (yyctx->yyn += yyctx->state) >= 0 && yyctx->yyn <= fawk_TABLESIZE && fawk_check[yyctx->yyn] == (fawk_int_t) yyctx->state) yyctx->state = fawk_table[yyctx->yyn]; else yyctx->state = fawk_dgoto[yyctx->yym]; if (yyctx->stack.s_mark >= yyctx->stack.s_last && fawk_growstack(yyctx, &yyctx->stack) == fawk_ENOMEM) goto yyoverflow; *++yyctx->stack.s_mark = (fawk_int_t) yyctx->state; *++yyctx->stack.l_mark = yyctx->val; goto yyloop; yyoverflow: fawk_error(ctx, yyctx->lval, "yacc stack overflow"); yyabort: fawk_freestack(&yyctx->stack); return fawk_RES_ABORT; yyaccept: fawk_freestack(&yyctx->stack); return fawk_RES_DONE; } FAWK_API int fawk_lex_fawk(fawk_STYPE *lval, fawk_ctx_t *ctx) { int chr, nchr; char tmp[128]; restart:; if (ctx->parser.in_eof) goto handle_eof; lex_textblk_exec(T_STRING, goto restart, '\n'); ctx->parser.used = 0; if (isalpha(chr) || (chr == '_')) { append(chr, return -1); for(;;) { chr = getch(ctx); if (!(isalpha(chr)) && !(isdigit(chr)) && (chr != '_')) { ungetch(ctx, chr); break; } append(chr, return -1); } append('\0', return -1); if (strcmp(ctx->parser.buff, "function") == 0) return T_FUNCTION; else if (strcmp(ctx->parser.buff, "for") == 0) return T_FOR; else if (strcmp(ctx->parser.buff, "do") == 0) return T_DO; else if (strcmp(ctx->parser.buff, "while") == 0) return T_WHILE; else if (strcmp(ctx->parser.buff, "if") == 0) return T_IF; else if (strcmp(ctx->parser.buff, "else") == 0) return T_ELSE; else if (strcmp(ctx->parser.buff, "in") == 0) return T_IN; else if (strcmp(ctx->parser.buff, "return") == 0) return T_RETURN; else if (strcmp(ctx->parser.buff, "include") == 0) { lex_include(goto restart); } else { lval->str = fawk_strdup(ctx, ctx->parser.buff); return (lval->str == NULL) ? -1 : T_ID; } } else if (isdigit(chr)) { append(chr, return -1); if (chr == '0') { nchr = getch(ctx); if (nchr == 'x') { fawk_readup(ctx, "0123456789abcdefABCDEF"); lval->num = strtol(ctx->parser.buff, NULL, 16); return T_NUM; } ungetch(ctx, nchr); } return read_numeric(ctx, &lval->num, 0, T_NUM); } else switch(chr) { case '@': case '?': case ':': case '(': case ')': case '\\': case ']': case ',': case '{': case '}': return chr; case '[': { lex_textblk_start(goto restart); } case '.': nchr = getch(ctx); if (!isdigit(nchr)) { ungetch(ctx, nchr); return '.'; } else { append('.', return -1); append(nchr, return -1); return read_numeric(ctx, &lval->num, 1, T_NUM); } break; case '+': nchr = getch(ctx); append(chr, return -1); switch(nchr) { case '+': return T_PP; case '=': return T_PLEQ; default: ungetch(ctx, nchr); return chr; } break; case '-': nchr = getch(ctx); append(chr, return -1); switch(nchr) { case '-': return T_MM; case '=': return T_MIEQ; default: ungetch(ctx, nchr); return chr; } break; case_op_ch2('/', '=', T_DIEQ); case_op_ch2('*', '=', T_MUEQ); case_op_ch2('%', '=', T_MOEQ); case_op_ch2('=', '=', T_EQ); case_op_ch2('!', '=', T_NEQ); case_op_ch2('<', '=', T_LTEQ); case_op_ch2('>', '=', T_GTEQ); case_op_ch2('&', '&', T_AND); case_op_ch2('|', '|', T_OR); case ';': return ';'; case '#': readtil(ctx, "\n\r"); fawk_readup(ctx, " \t\r\n"); goto restart; case '\"': if (read_strlit(ctx, '\"') == 0) return T_NIL; lval->str = fawk_strdup(ctx, ctx->parser.buff); return (lval->str == NULL) ? -1 : T_STRING; case EOF: { lex_got_eof(goto restart); } default: sprintf(tmp, "Invalid character on input: '%c'\n", chr); LIBFAWK_ERROR(ctx, tmp, ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1, -1); } } #ifndef _fbas__defines_h_ #define _fbas__defines_h_ typedef short fbas_int_t; #define fbas_chr yyctx->chr #define fbas_val yyctx->val #define fbas_lval yyctx->lval #define fbas_stack yyctx->stack #define fbas_debug yyctx->debug #define fbas_nerrs yyctx->nerrs #define fbas_errflag yyctx->errflag #define fbas_state yyctx->state #define fbas_yyn yyctx->yyn #define fbas_yym yyctx->yym #define fbas_jump yyctx->jump #define fbas_ctx_t fawk_ctx_t #define FAWK_PUSH_IP() fawk_push_num(ctx, ctx->code.used) #define FAWK_CURR_IP() ctx->code.used typedef union { char *str; fawk_num_t num; } fbas_tokunion_t; typedef fbas_tokunion_t fbas_STYPE; #define BT_NEQ 257 #define BT_GTEQ 258 #define BT_LTEQ 259 #define BT_NIL 260 #define BT_IF 261 #define BT_THEN 262 #define BT_ELSE 263 #define BT_FOR 264 #define BT_TO 265 #define BT_STEP 266 #define BT_NEXT 267 #define BT_DO 268 #define BT_WHILE 269 #define BT_UNTIL 270 #define BT_LOOP 271 #define BT_GOTO 272 #define BT_DEF 273 #define BT_END 274 #define BT_LET 275 #define BT_CURRFUNC 276 #define BT_ID 277 #define BT_STRING 278 #define BT_NUM 279 #define BT_OR 280 #define BT_AND 281 #define BT_IN 282 #define BT_MOD 283 #define BT_NOT 284 #define fbas_ERRCODE 256 #ifndef fbas_INITSTACKSIZE #define fbas_INITSTACKSIZE 200 #endif typedef struct { unsigned stacksize; fbas_int_t *s_base; fbas_int_t *s_mark; fbas_int_t *s_last; fbas_STYPE *l_base; fbas_STYPE *l_mark; } fbas_STACKDATA; typedef struct { int errflag; int chr; fbas_STYPE val; fbas_STYPE lval; int nerrs; int yym, yyn, state; int jump; int stack_max_depth; int debug; fbas_STACKDATA stack; } fbas_yyctx_t; typedef enum { fbas_RES_NEXT, fbas_RES_DONE, fbas_RES_ABORT } fbas_res_t; FAWK_API int fbas_parse_init(fbas_yyctx_t *yyctx); FAWK_API fbas_res_t fbas_parse(fbas_yyctx_t *yyctx, fbas_ctx_t *ctx, int tok, fbas_STYPE *lval); FAWK_API void fbas_error(fbas_ctx_t *ctx, fbas_STYPE tok, const char *msg); #endif FAWK_API int fawk_parse_fbas(fawk_ctx_t *ctx); static void bas_add_label(fawk_ctx_t *ctx, char *lab, double lineno) { char tmp[128]; if (lab == NULL) sprintf(lab = tmp, "%ld", (long)lineno); if (fawk_htpp_has(ctx->compiler.labels, lab)) libfawk_error(ctx, "Ignoring duplicate label", ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1); else fawk_htpp_set(ctx->compiler.labels, fawk_strdup(ctx, lab), (void *)((long)ctx->code.used+1)); } static void bas_add_jump(fawk_ctx_t *ctx, char *lab, double lineno) { char tmp[128]; void *ip; if (lab == NULL) sprintf(lab = tmp, "%ld", (long)lineno); fawkc_addi(ctx, FAWKI_JMP); ip = (void *)ctx->code.used; fawk_htpp_set(ctx->compiler.lablink, ip, (void *)fawk_strdup(ctx, lab)); fawkc_addnum(ctx, 7771); } static int bas_init_labels(fawk_ctx_t *ctx) { ctx->compiler.labels = fawk_malloc(ctx, sizeof(fawk_htpp_t)); if (ctx->compiler.labels == NULL) return -1; ctx->compiler.lablink = fawk_malloc(ctx, sizeof(fawk_htpp_t)); if (ctx->compiler.lablink == NULL) {fawk_free(ctx, ctx->compiler.labels); ctx->compiler.labels = NULL; return -1; } fawk_htpp_init(ctx->compiler.labels, (unsigned int (*)(const void *))strhash_case, (int (*)(const void *, const void *))strkeyeq_case); fawk_htpp_init(ctx->compiler.lablink, ptrhash, ptrkeyeq); return 0; } static int bas_uninit_labels(fawk_ctx_t *ctx) { fawk_htpp_entry_t *e; for(e = fawk_htpp_first(ctx->compiler.lablink); e != NULL; e = fawk_htpp_next(ctx->compiler.lablink, e)) { void *addr = fawk_htpp_get(ctx->compiler.labels, e->value); if (addr == NULL) { libfawk_error(ctx, "Undefined goto label:", ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1); libfawk_error(ctx, e->value, ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1); return -1; } ctx->code.code[(long)e->key].data.num = (long)addr-1; fawk_free(ctx, e->value); } for(e = fawk_htpp_first(ctx->compiler.labels); e != NULL; e = fawk_htpp_next(ctx->compiler.labels, e)) free(e->key); fawk_htpp_uninit(ctx->compiler.labels); fawk_htpp_uninit(ctx->compiler.lablink); fawk_free(ctx, ctx->compiler.labels); ctx->compiler.labels = NULL; fawk_free(ctx, ctx->compiler.lablink); ctx->compiler.lablink = NULL; return 0; } static void bas_end_main(fawk_ctx_t *ctx) { fawkc_addi(ctx, FAWKI_PUSH_NIL); fawkc_addi(ctx, FAWKI_RET); fawkc_addnum(ctx, 1); ctx->fp = 0; } #ifdef YY_QUERY_API_VER #define YY_BYACCIC #define YY_API_MAJOR 1 #define YY_API_MINOR 0 #endif #define fbas_EMPTY (-1) #define fbas_clearin (fbas_chr = fbas_EMPTY) #define fbas_errok (fbas_errflag = 0) #define fbas_RECOVERING() (fbas_errflag != 0) #define fbas_ENOMEM (-2) #define fbas_EOF 0 static const fbas_int_t fbas_lhs[] = {-1,2,0,3,3,7,3,3,6,6,10,10,5,5,1,1,8,8,9,9,9,9,9,9,9,11,11,13,13,18,21,17,22,17,14,25,14,26,27,14,23,15,15,30,32,28,33,34,36,37,29,35,35,31,31,39,16,38,38,38,38,44,40,45,41,42,43,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,49,19,48,48,48,48,48,50,51,50,52,46,53,47,55,12,54,54,54,56,57,59,4,58,58,58,58,58,24,24,}; static const fbas_int_t fbas_len[] = {2,0,3,3,2,0,4,0,3,2,1,1,1,2,1,0,1,2,1,1,1,1,1,1,1,2,2,2,1,3,0,5,0,4,2,0,6,0,0,10,3,1,1,0,0,9,0,0,0,0,14,0,2,1,2,0,3,1,1,1,1,0,6,0,6,5,5,1,1,1,1,2,1,3,3,3,3,3,3,3,3,1,1,3,3,3,3,3,2,2,2,3,0,3,0,3,4,3,3,1,0,4,0,4,0,4,0,5,0,1,3,0,0,0,12,0,1,3,3,5,2,0,}; static const fbas_int_t fbas_defred[] = {1,0,0,12,0,0,0,0,55,0,0,0,0,0,16,2,0,0,0,0,18,19,20,21,22,23,24,28,0,0,41,42,0,13,69,92,68,67,0,0,0,0,0,72,0,0,81,82,0,0,0,25,26,0,0,27,0,32,17,94,0,4,0,10,0,9,0,106,0,34,0,0,0,90,0,71,0,0,0,40,102,104,0,0,0,0,0,0,0,0,0,0,46,0,0,0,0,56,57,58,59,60,0,30,0,0,3,8,0,0,0,0,112,73,0,0,0,0,0,80,0,0,0,0,0,0,85,86,87,0,0,0,0,0,6,0,0,0,0,0,0,120,37,0,0,0,0,0,0,0,0,0,0,95,0,0,97,98,0,107,0,0,0,0,0,0,0,0,0,96,100,110,0,36,116,0,0,0,44,0,0,0,0,0,38,0,113,0,0,0,62, 64,0,0,117,0,118,0,0,0,45,0,0,0,0,49,54,39,0,119,0,0,0,0,114,0,50,}; static const fbas_int_t fbas_dgoto[] = {1,4,2,15,16,64,110,102,18,19,65,20,43,22,23,24,25,26,27,44,139,135,104,29,111,143,160,193,30,31,164,200,189,129,163,205,198,211,97,50,98,99,100,101,165,166,46,47,105,59,155,183,117,118,140,109,32,144,176,195,}; static const fbas_int_t fbas_sindex[] = {0,0,30,0,603,60,188,-249,0,-193,30,-231,26,4,0,0,30,603,626,2,0,0,0,0,0,0,0,0,19,380,0,0,-190,0,0,0,0,0,188,188,188,188,-186,0,58,627,0,0,38,-182,-10,0,0,60,41,0,42,0,0,0,339,0,2,0,60,0,188,0,363,0,-170,-20,-20,0,503,0,188,188,188,0,0,0,-186,188,188,188,188,188,188,188,188,188,0,-186,188,188,363,0,0,0,0,0,603,0,188,10,0,0,768,188,854,-154,0,0,-26,-26,-26,188,188,0,-13,-26,-26,-32,-20,-20,0,0,0,188,30,113,113,-161,0,188,768,-36,-184,703,70,0,0,-160,73, 795,801,768,60,60,60,-173,768,0,768,-38,0,0,188,0,30,-140,-43,-131,854,854,854,188,188,0,0,0,363,0,0,79,20,188,0,-133,-130,768,768,188,0,94,0,7,768,-123,0,0,768,-126,0,30,0,104,-115,-186,0,-109,363,108,188,0,0,0,-113,0,768,30,-114,363,0,-123,0,}; static const fbas_int_t fbas_rindex[] = {0,0,18,0,8,35,0,0,0,0,0,0,0,-3,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,0,0,0,-15,0,0,0,0,52,0,0,0,0,0,0,8,0,0,0,1,0,0,0,-209,0,0,121,147,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-108,0,0,0,0,0,8,0,0,75,0,0,14,128,-196,-104,0,0,261,447,473,0,0,0,543,511,537,420,157,194,0,0,0,0,0,0,0,0,0,0,15,0,0,130,0,0,0,0,0,445,400,-93,666,702,744,0,23,0,-30,0,0,0,128,0,0,0,33,0,-91,-108,-108,0,0,0,0,0,-94,0,0,0,0,0,0,0,0,31,32,0,0,0,0,0, -5,0,0,0,-24,0,0,0,0,0,174,34,0,0,-94,0,0,0,0,0,0,0,176,0,0,-91,0,0,0,}; static const fbas_int_t fbas_gindex[] = {0,0,0,6,0,903,22,0,0,50,125,0,922,0,0,0,0,0,178,907,1061,0,0,0,-60,0,0,0,0,0,0,-22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,0,0,0,0,0,}; #define fbas_TABLESIZE 1265 static const fbas_int_t fbas_table[] = {3,11,42,175,41,48,170,38,7,39,89,87,3,88,99,90,89,87,15,88,101,90,89,61,29,33,17,90,48,89,87,92,88,31,90,14,133,92,86,17,3,65,66,92,53,54,35,84,83,85,141,86,5,197,121,169,138,153,92,67,63,186,58,99,187,121,106,121,62,101,33,121,29,33,115,121,92,115,121,69,66,31,17,70,51,93,52,57,92,65,66,35,53,156,157,70,167,168,67,92,93,137,103,66,178,179,180,112,134,142,151,159,184,162,161,93,93,93,93,93,93,173,93,3,17,185,70,70,70,70,70,89,70,93,177,93,93,93,190,93, 194,191,208,70,199,70,70,70,201,70,203,204,207,215,209,89,87,88,88,214,90,212,89,121,89,89,89,83,93,108,35,109,47,84,83,85,121,86,70,89,121,89,89,89,51,89,52,107,88,55,88,88,88,216,171,0,0,0,83,0,83,83,83,0,84,88,0,88,88,88,0,88,0,0,89,83,0,83,83,83,0,83,0,0,34,0,42,0,41,0,0,38,0,39,174,84,0,84,84,84,88,35,36,37,76,77,78,0,40,0,83,91,84,0,84,84,84,91,84,94,95,48,11,91,11,11,0,92,11,11,91,75,11,11,11,11,11,11,11,15,11,111,15,0,196,0,15,84,0,0,15,15,15,15,15,15,14,15,0,14,0,0,75,14,0,75,0,14, 14,14,14,14,14,5,14,0,5,0,0,75,5,75,75,75,5,5,5,5,5,5,0,5,93,93,93,0,0,93,0,0,93,93,70,70,70,0,0,70,0,33,70,70,0,0,75,93,93,93,93,0,0,0,0,0,0,70,70,70,70,0,76,77,78,33,0,0,0,0,89,89,89,0,0,89,0,0,89,89,0,0,3,0,0,80,81,82,91,0,0,0,0,89,89,89,88,88,88,0,0,88,105,0,88,88,83,83,83,0,0,83,0,0,83,83,0,0,0,88,88,88,91,0,0,0,0,0,0,83,83,83,0,105,0,0,105,0,0,0,34,0,0,84,84,84,0,103,84,76,105,84,84,91,0,0,91,35,36,37,0,0,0,0,40,0,84,84,84,0,91,0,91,91,91,77,91,0,103,0,76,103,0,76,0,105,0,0,0,0,0,0,0, 0,0,103,0,76,0,76,76,76,0,0,0,91,77,0,0,77,75,75,75,79,0,75,0,0,75,75,0,0,0,77,0,77,77,77,0,0,103,0,76,75,75,75,113,89,87,78,88,0,90,0,79,74,0,79,0,0,0,0,0,0,0,84,83,85,77,86,0,79,0,79,79,79,0,0,0,0,78,0,0,78,0,0,74,0,0,74,0,0,0,0,0,0,0,78,0,78,78,78,6,74,0,7,79,0,0,8,0,0,0,9,0,10,11,12,13,0,14,0,0,0,0,0,6,0,0,7,0,0,78,8,0,0,0,9,74,0,11,12,13,6,14,0,7,0,0,0,8,0,0,0,9,0,0,11,12,35,0,0,0,0,105,0,0,105,105,0,0,89,87,0,88,0,90,0,0,91,91,91,105,105,91,0,0,91,91,84,83,85,0,86,0,0,0,0,0,0,0,0,91, 91,91,0,76,76,76,103,0,76,103,103,76,76,0,0,0,0,0,0,0,0,0,0,0,103,0,76,76,76,77,77,77,0,0,77,0,0,77,77,0,0,0,0,0,89,87,158,88,0,90,0,0,77,77,77,0,0,0,0,76,77,78,84,83,85,0,86,79,79,79,0,0,79,0,0,79,79,0,0,0,0,0,80,81,82,91,0,0,0,0,79,79,79,78,78,78,0,0,78,0,0,78,78,0,74,0,0,74,74,89,87,0,88,0,90,0,78,78,78,0,0,0,74,74,74,0,0,84,83,85,0,86,0,0,0,0,89,87,0,88,0,90,89,87,0,88,0,90,0,0,0,0,0,0,84,83,85,0,86,0,84,83,85,6,86,0,7,0,0,0,8,0,0,0,9,0,10,11,12,13,0,14,0,76,77,78,6,0,79,7,0,0,0,8,0,0, 0,9,0,0,11,12,35,0,5,0,80,81,82,91,28,0,53,49,0,0,0,56,60,0,0,0,0,28,28,21,43,0,0,43,0,68,43,43,0,28,0,43,21,21,43,43,43,0,43,0,0,0,75,0,21,0,96,0,0,0,0,0,0,76,77,78,61,0,0,61,28,0,0,61,0,0,61,61,28,0,61,61,61,0,61,21,80,81,82,91,0,0,119,21,0,0,0,0,0,0,0,0,0,130,0,0,28,0,63,0,0,63,28,0,0,63,0,0,63,63,28,21,63,63,63,0,63,21,76,77,78,0,0,0,0,21,148,149,150,0,0,0,0,0,0,0,0,0,0,0,0,80,81,82,91,76,77,78,0,0,0,76,77,78,0,0,172,0,0,0,45,0,0,0,28,28,28,0,0,81,82,91,28,0,0,0,82,91,0,21,21,21,0,0,0, 0,0,21,0,0,0,202,71,72,73,74,0,0,0,206,0,0,28,0,0,0,0,213,6,0,0,7,0,28,0,8,0,21,0,9,108,0,11,12,13,0,14,0,21,0,114,115,116,0,0,0,0,120,121,122,123,124,125,126,127,128,0,0,131,132,0,0,0,0,0,0,0,0,136,0,0,0,0,0,0,0,0,0,0,0,0,145,146,0,0,0,0,0,0,0,0,0,0,147,0,0,0,0,0,152,0,154,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,181,182,0,0,0,0,0,0,0,0,188,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,}; static const fbas_int_t fbas_check[] = {10,0,38,46,40,10,44,43,0,45,42,43,10,45,44,47,42,43,0,45,44,47,42,17,10,10,4,47, 277,42,43,46,45,10,47,0,96,40,64,17,10,10,10,46,10,276,277,60,61,62,110,64,0,46,263,93,46,93,61,40,58,41,58,93,44,274,60,263,18,93,10,267,58,58,41,271,91,44,274,29,61,58,60,273,277,10,279,61,91,58,58,277,58,277,278,10,269,270,40,61,282,91,61,61,164,165,166,277,102,263,271,41,172,40,274,40,41,42,43,44,45,261,47,10,102,46,41,42,43,44,45,10,47,58,265,60,61,62,271,64,46,271,202,58,267,60,61,62,274,64,46,266,261,213,46,42,43,10,45,273,47,274,41,271,43,44,45,10,93,41,274,41,265,60,61,62,267,64,93, 58,274,60,61,62,10,64,10,62,41,11,43,44,45,215,158,-1,-1,-1,41,-1,43,44,45,-1,10,58,-1,60,61,62,-1,64,-1,-1,93,58,-1,60,61,62,-1,64,-1,-1,260,-1,38,-1,40,-1,-1,43,-1,45,277,41,-1,43,44,45,93,277,278,279,257,258,259,-1,284,-1,93,283,58,-1,60,61,62,283,64,269,270,266,261,283,263,264,-1,282,267,268,283,10,271,272,273,274,275,276,277,261,279,273,264,-1,277,-1,268,93,-1,-1,272,273,274,275,276,277,261,279,-1,264,-1,-1,41,268,-1,44,-1,272,273,274,275,276,277,261,279,-1,264,-1,-1,58,268,60,61,62,272, 273,274,275,276,277,-1,279,257,258,259,-1,-1,262,-1,-1,265,266,257,258,259,-1,-1,262,-1,10,265,266,-1,-1,93,280,281,282,283,-1,-1,-1,-1,-1,-1,280,281,282,283,-1,257,258,259,10,-1,-1,-1,-1,257,258,259,-1,-1,262,-1,-1,265,266,-1,-1,10,-1,-1,280,281,282,283,-1,-1,-1,-1,280,281,282,257,258,259,-1,-1,262,10,-1,265,266,257,258,259,-1,-1,262,-1,-1,265,266,-1,-1,-1,280,281,282,10,-1,-1,-1,-1,-1,-1,280,281,282,-1,41,-1,-1,44,-1,-1,-1,260,-1,-1,257,258,259,-1,10,262,10,58,265,266,41,-1,-1,44,277,278, 279,-1,-1,-1,-1,284,-1,280,281,282,-1,58,-1,60,61,62,10,64,-1,41,-1,41,44,-1,44,-1,93,-1,-1,-1,-1,-1,-1,-1,-1,-1,58,-1,58,-1,60,61,62,-1,-1,-1,93,41,-1,-1,44,257,258,259,10,-1,262,-1,-1,265,266,-1,-1,-1,58,-1,60,61,62,-1,-1,93,-1,93,280,281,282,41,42,43,10,45,-1,47,-1,41,10,-1,44,-1,-1,-1,-1,-1,-1,-1,60,61,62,93,64,-1,58,-1,60,61,62,-1,-1,-1,-1,41,-1,-1,44,-1,-1,41,-1,-1,44,-1,-1,-1,-1,-1,-1,-1,58,-1,60,61,62,261,58,-1,264,93,-1,-1,268,-1,-1,-1,272,-1,274,275,276,277,-1,279,-1,-1,-1,-1,-1, 261,-1,-1,264,-1,-1,93,268,-1,-1,-1,272,93,-1,275,276,277,261,279,-1,264,-1,-1,-1,268,-1,-1,-1,272,-1,-1,275,276,277,-1,-1,-1,-1,262,-1,-1,265,266,-1,-1,42,43,-1,45,-1,47,-1,-1,257,258,259,280,281,262,-1,-1,265,266,60,61,62,-1,64,-1,-1,-1,-1,-1,-1,-1,-1,280,281,282,-1,257,258,259,262,-1,262,265,266,265,266,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,280,-1,280,281,282,257,258,259,-1,-1,262,-1,-1,265,266,-1,-1,-1,-1,-1,42,43,44,45,-1,47,-1,-1,280,281,282,-1,-1,-1,-1,257,258,259,60,61,62,-1,64,257,258, 259,-1,-1,262,-1,-1,265,266,-1,-1,-1,-1,-1,280,281,282,283,-1,-1,-1,-1,280,281,282,257,258,259,-1,-1,262,-1,-1,265,266,-1,262,-1,-1,265,266,42,43,-1,45,-1,47,-1,280,281,282,-1,-1,-1,280,281,282,-1,-1,60,61,62,-1,64,-1,-1,-1,-1,42,43,-1,45,-1,47,42,43,-1,45,-1,47,-1,-1,-1,-1,-1,-1,60,61,62,-1,64,-1,60,61,62,261,64,-1,264,-1,-1,-1,268,-1,-1,-1,272,-1,274,275,276,277,-1,279,-1,257,258,259,261,-1,262,264,-1,-1,-1,268,-1,-1,-1,272,-1,-1,275,276,277,-1,2,-1,280,281,282,283,4,-1,10,7,-1,-1,-1,11, 16,-1,-1,-1,-1,17,18,4,261,-1,-1,264,-1,29,267,268,-1,29,-1,272,17,18,275,276,277,-1,279,-1,-1,-1,42,-1,29,-1,50,-1,-1,-1,-1,-1,-1,257,258,259,261,-1,-1,264,60,-1,-1,268,-1,-1,271,272,68,-1,275,276,277,-1,279,60,280,281,282,283,-1,-1,82,68,-1,-1,-1,-1,-1,-1,-1,-1,-1,93,-1,-1,96,-1,261,-1,-1,264,102,-1,-1,268,-1,-1,271,272,110,96,275,276,277,-1,279,102,257,258,259,-1,-1,-1,-1,110,130,131,132,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,280,281,282,283,257,258,259,-1,-1,-1,257,258,259,-1,-1,160,-1,-1, -1,6,-1,-1,-1,164,165,166,-1,-1,281,282,283,172,-1,-1,-1,282,283,-1,164,165,166,-1,-1,-1,-1,-1,172,-1,-1,-1,195,38,39,40,41,-1,-1,-1,199,-1,-1,202,-1,-1,-1,-1,211,261,-1,-1,264,-1,213,-1,268,-1,202,-1,272,66,-1,275,276,277,-1,279,-1,213,-1,76,77,78,-1,-1,-1,-1,83,84,85,86,87,88,89,90,91,-1,-1,94,95,-1,-1,-1,-1,-1,-1,-1,-1,104,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,117,118,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,129,-1,-1,-1,-1,-1,135,-1,137,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,167,168,-1,-1,-1,-1,-1,-1,-1,-1,177,-1,-1,-1,-1,-1,183,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,204,}; #define fbas_FINAL 1 #define fbas_MAXTOKEN 284 #define fbas_UNDFTOKEN 346 #define fbas_TRANSLATE(a) ((a) > fbas_MAXTOKEN ? fbas_UNDFTOKEN : (a)) FAWK_API void fbas_error(fawk_ctx_t *ctx, fbas_STYPE tok, const char *msg) { libfawk_error(ctx, msg, ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1); } FAWK_API int fawk_lex_fbas(fbas_STYPE *lval, fawk_ctx_t *ctx); FAWK_API int fawk_parse_fbas(fawk_ctx_t *ctx) { fawk_parser_loop(fbas_yyctx_t, fbas_STYPE, fawk_lex_fbas, fbas_parse, ctx, fbas_RES_NEXT, fbas_RES_DONE); } static int fbas_growstack(fbas_yyctx_t *yyctx, fbas_STACKDATA *data) { int i; unsigned newsize; fbas_int_t *newss; fbas_STYPE *newvs; if ((newsize = data->stacksize) == 0) newsize = fbas_INITSTACKSIZE; else if (newsize >= yyctx->stack_max_depth) return fbas_ENOMEM; else if ((newsize *= 2) > yyctx->stack_max_depth) newsize = yyctx->stack_max_depth; i = (int)(data->s_mark - data->s_base); newss = (fbas_int_t *) realloc(data->s_base, newsize * sizeof(*newss)); if (newss == 0) return fbas_ENOMEM; data->s_base = newss; data->s_mark = newss + i; newvs = (fbas_STYPE *) realloc(data->l_base, newsize * sizeof(*newvs)); if (newvs == 0) return fbas_ENOMEM; data->l_base = newvs; data->l_mark = newvs + i; data->stacksize = newsize; data->s_last = data->s_base + newsize - 1; return 0; } static void fbas_freestack(fbas_STACKDATA *data) { free(data->s_base); free(data->l_base); memset(data, 0, sizeof(*data)); } #define fbas_ABORT goto yyabort #define fbas_REJECT goto yyabort #define fbas_ACCEPT goto yyaccept #define fbas_ERROR goto yyerrlab int fbas_parse_init(fbas_yyctx_t *yyctx) { memset(&yyctx->val, 0, sizeof(yyctx->val)); memset(&yyctx->lval, 0, sizeof(yyctx->lval)); yyctx->yym = 0; yyctx->yyn = 0; yyctx->nerrs = 0; yyctx->errflag = 0; yyctx->chr = fbas_EMPTY; yyctx->state = 0; memset(&yyctx->stack, 0, sizeof(yyctx->stack)); yyctx->stack_max_depth = fbas_INITSTACKSIZE > 10000 ? fbas_INITSTACKSIZE : 10000; if (yyctx->stack.s_base == NULL && fbas_growstack(yyctx, &yyctx->stack) == fbas_ENOMEM) return -1; yyctx->stack.s_mark = yyctx->stack.s_base; yyctx->stack.l_mark = yyctx->stack.l_base; yyctx->state = 0; *yyctx->stack.s_mark = 0; yyctx->jump = 0; return 0; } #define fbas_GETCHAR(labidx) \ do { \ if (used) { yyctx->jump = labidx; return fbas_RES_NEXT; } \ getchar_ ## labidx:; yyctx->chr = tok; yyctx->lval = *lval; used = 1; \ } while(0) fbas_res_t fbas_parse(fbas_yyctx_t *yyctx, fbas_ctx_t *ctx, int tok, fbas_STYPE *lval) { int used = 0; yyloop:; if (yyctx->jump == 1) { yyctx->jump = 0; goto getchar_1; } if (yyctx->jump == 2) { yyctx->jump = 0; goto getchar_2; } if ((yyctx->yyn = fbas_defred[yyctx->state]) != 0) goto yyreduce; if (yyctx->chr < 0) { fbas_GETCHAR(1); if (yyctx->chr < 0) yyctx->chr = fbas_EOF; } if (((yyctx->yyn = fbas_sindex[yyctx->state]) != 0) && (yyctx->yyn += yyctx->chr) >= 0 && yyctx->yyn <= fbas_TABLESIZE && fbas_check[yyctx->yyn] == (fbas_int_t) yyctx->chr) { if (yyctx->stack.s_mark >= yyctx->stack.s_last && fbas_growstack(yyctx, &yyctx->stack) == fbas_ENOMEM) goto yyoverflow; yyctx->state = fbas_table[yyctx->yyn]; *++yyctx->stack.s_mark = fbas_table[yyctx->yyn]; *++yyctx->stack.l_mark = yyctx->lval; yyctx->chr = fbas_EMPTY; if (yyctx->errflag > 0) --yyctx->errflag; goto yyloop; } if (((yyctx->yyn = fbas_rindex[yyctx->state]) != 0) && (yyctx->yyn += yyctx->chr) >= 0 && yyctx->yyn <= fbas_TABLESIZE && fbas_check[yyctx->yyn] == (fbas_int_t) yyctx->chr) { yyctx->yyn = fbas_table[yyctx->yyn]; goto yyreduce; } if (yyctx->errflag != 0) goto yyinrecovery; fbas_error(ctx, yyctx->lval, "syntax error"); goto yyerrlab; yyerrlab: ++yyctx->nerrs; yyinrecovery: if (yyctx->errflag < 3) { yyctx->errflag = 3; for(;;) { if (((yyctx->yyn = fbas_sindex[*yyctx->stack.s_mark]) != 0) && (yyctx->yyn += fbas_ERRCODE) >= 0 && yyctx->yyn <= fbas_TABLESIZE && fbas_check[yyctx->yyn] == (fbas_int_t) fbas_ERRCODE) { if (yyctx->stack.s_mark >= yyctx->stack.s_last && fbas_growstack(yyctx, &yyctx->stack) == fbas_ENOMEM) goto yyoverflow; yyctx->state = fbas_table[yyctx->yyn]; *++yyctx->stack.s_mark = fbas_table[yyctx->yyn]; *++yyctx->stack.l_mark = yyctx->lval; goto yyloop; } else { if (yyctx->stack.s_mark <= yyctx->stack.s_base) goto yyabort; --yyctx->stack.s_mark; --yyctx->stack.l_mark; } } } else { if (yyctx->chr == fbas_EOF) goto yyabort; yyctx->chr = fbas_EMPTY; goto yyloop; } yyreduce: yyctx->yym = fbas_len[yyctx->yyn]; if (yyctx->yym > 0) yyctx->val = yyctx->stack.l_mark[1 - yyctx->yym]; else memset(&yyctx->val, 0, sizeof yyctx->val); switch (yyctx->yyn) { case 1: { if (bas_init_labels(ctx) != 0) return -1; fawk_symtab_regfunc(ctx, "main", FAWK_CURR_IP(), 1, -1); ctx->fp = ctx->sp; } break; case 2: { bas_end_main(ctx); if (bas_uninit_labels(ctx) != 0) return -1; } break; case 5: { bas_end_main(ctx); } break; case 16: { bas_add_label(ctx, NULL, yyctx->stack.l_mark[0].num); } break; case 17: { bas_add_label(ctx, yyctx->stack.l_mark[-1].str, 0); } break; case 19: { fawkc_addi(ctx, FAWKI_POP); } break; case 25: { bas_add_jump(ctx, yyctx->stack.l_mark[0].str, 0); } break; case 26: { bas_add_jump(ctx, NULL, yyctx->stack.l_mark[0].num); } break; case 29: { fawkc_addi(ctx, FAWKI_SET); fawkc_addi(ctx, FAWKI_PUSH_SYMVAL); fawkc_addi(ctx, FAWKI_POP); } break; case 30: { fawkc_addi(ctx, FAWKI_POP); } break; case 32: { fawkc_addi(ctx, FAWKI_POP); } break; case 34: { size_t jmp1 = fawk_pop_num(ctx, 1); ctx->code.code[jmp1].data.num = FAWK_CURR_IP(); } break; case 35: { size_t jmp1 = fawk_pop_num(ctx, 1); ctx->code.code[jmp1].data.num = FAWK_CURR_IP(); } break; case 37: { fawkc_addi(ctx, FAWKI_JMP); FAWK_PUSH_IP(); fawkc_addnum(ctx, 777); } break; case 38: { size_t jmp_then_post = fawk_pop_num(ctx, 1), jmp_if = fawk_pop_num(ctx, 1); ctx->code.code[jmp_then_post].data.num = FAWK_CURR_IP(); ctx->code.code[jmp_if].data.num = jmp_then_post+1; } break; case 40: { fawkc_addi(ctx, FAWKI_POPJZ); FAWK_PUSH_IP(); fawkc_addnum(ctx, 777); } break; case 43: { fawkc_addi(ctx, FAWKI_FORIN_FIRST); fawkc_addi(ctx, FAWKI_JMP); fawkc_addnum(ctx, 0); FAWK_PUSH_IP(); } break; case 44: { size_t begin = fawk_pop_num(ctx, 1); fawkc_addi(ctx, FAWKI_FORIN_NEXT); fawkc_addnum(ctx, begin); ctx->code.code[begin-1].data.num = FAWK_CURR_IP()-2; } break; case 46: { fawkc_addi(ctx, FAWKI_MAKE_SYMREF); fawkc_addsymref(ctx, yyctx->stack.l_mark[-1].str, 0, ctx->compiler.funcdef_offs); fawkc_addnum(ctx, 0); } break; case 47: { fawkc_addi(ctx, FAWKI_SET); fawkc_addi(ctx, FAWKI_PUSH_SYMVAL); fawkc_addi(ctx, FAWKI_POP); fawkc_addi(ctx, FAWKI_JMP); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); FAWK_PUSH_IP(); } break; case 48: { fawkc_addi(ctx, FAWKI_MAKE_SYMREF); fawkc_addsymref(ctx, yyctx->stack.l_mark[-6].str, 0, ctx->compiler.funcdef_offs); fawkc_addnum(ctx, 0); fawkc_addi(ctx, FAWKI_PUSH_SYMVAL); fawkc_addi(ctx, FAWKI_SUB); } break; case 49: { fawkc_addi(ctx, FAWKI_PUSH_REL); fawkc_addnum(ctx, -1); fawkc_addi(ctx, FAWKI_PUSH_REL); fawkc_addnum(ctx, -3); fawkc_addi(ctx, FAWKI_PUSH_REL); fawkc_addnum(ctx, -2); fawkc_addi(ctx, FAWKI_SUB); fawkc_addi(ctx, FAWKI_MUL); fawkc_addi(ctx, FAWKI_PUSH_NUM); fawkc_addnum(ctx, 0); fawkc_addi(ctx, FAWKI_GTEQ); fawkc_addi(ctx, FAWKI_POPJZ); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); fawkc_addi(ctx, FAWKI_MAKE_SYMREF); fawkc_addsymref(ctx, yyctx->stack.l_mark[-8].str, 0, ctx->compiler.funcdef_offs); fawkc_addnum(ctx, 0); fawkc_addi(ctx, FAWKI_PUSH_TOPVAR); fawkc_addi(ctx, FAWKI_PUSH_REL); fawkc_addnum(ctx, -3); fawkc_addi(ctx, FAWKI_ADD); fawkc_addi(ctx, FAWKI_SET); fawkc_addi(ctx, FAWKI_POP); fawkc_addi(ctx, FAWKI_POP); fawkc_addi(ctx, FAWKI_POP); fawkc_addi(ctx, FAWKI_JMP); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); fawkc_addi(ctx, FAWKI_JMP); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); } break; case 50: { size_t jumpin, jback3rd, jskip, jout, rerun; jback3rd = fawk_pop_num(ctx, 1); jskip = fawk_pop_num(ctx, 1); jout = fawk_pop_num(ctx, 1); rerun = fawk_pop_num(ctx, 1); jumpin = fawk_pop_num(ctx, 1); fawkc_addi(ctx, FAWKI_JMP); fawkc_addnum(ctx, jskip+1); ctx->code.code[jumpin].data.num = jback3rd+1; ctx->code.code[jback3rd].data.num = rerun; ctx->code.code[jskip].data.num = jback3rd+1; ctx->code.code[jout].data.num = FAWK_CURR_IP(); fawkc_addi(ctx, FAWKI_POP); fawkc_addi(ctx, FAWKI_POP); } break; case 51: { fawkc_addi(ctx, FAWKI_PUSH_NUM); fawkc_addnum(ctx, 1); } break; case 54: { fawkc_addi(ctx, FAWKI_POP); } break; case 55: { FAWK_PUSH_IP(); } break; case 61: { fawkc_addi(ctx, FAWKI_POPJZ); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); } break; case 62: { loop_pretest_jumpback(); } break; case 63: { fawkc_addi(ctx, FAWKI_POPJNZ); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); } break; case 64: { loop_pretest_jumpback(); } break; case 65: { fawkc_addi(ctx, FAWKI_POPJNZ); fawkc_addnum(ctx, fawk_pop_num(ctx, 1));} break; case 66: { fawkc_addi(ctx, FAWKI_POPJZ); fawkc_addnum(ctx, fawk_pop_num(ctx, 1));} break; case 67: { fawkc_addi(ctx, FAWKI_PUSH_NUM); fawkc_addnum(ctx, yyctx->stack.l_mark[0].num); } break; case 68: { fawkc_addi(ctx, FAWKI_PUSH_STR); fawkc_adds(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); } break; case 69: { fawkc_addi(ctx, FAWKI_PUSH_NIL); } break; case 70: { fawkc_addi(ctx, FAWKI_PUSH_SYMVAL); } break; case 74: { fawkc_addi(ctx, FAWKI_EQ); } break; case 75: { fawkc_addi(ctx, FAWKI_NEQ); } break; case 76: { fawkc_addi(ctx, FAWKI_GTEQ); } break; case 77: { fawkc_addi(ctx, FAWKI_LTEQ); } break; case 78: { fawkc_addi(ctx, FAWKI_GT); } break; case 79: { fawkc_addi(ctx, FAWKI_LT); } break; case 80: { fawkc_addi(ctx, FAWKI_IN); } break; case 83: { fawkc_addi(ctx, FAWKI_ADD); } break; case 84: { fawkc_addi(ctx, FAWKI_SUB); } break; case 85: { fawkc_addi(ctx, FAWKI_MUL); } break; case 86: { fawkc_addi(ctx, FAWKI_DIV); } break; case 87: { fawkc_addi(ctx, FAWKI_MOD); } break; case 88: { fawkc_addi(ctx, FAWKI_NEG); } break; case 90: { fawkc_addi(ctx, FAWKI_NOT); } break; case 91: { fawkc_addi(ctx, FAWKI_CONCAT); } break; case 92: { fawk_push_num(ctx, ctx->compiler.numidx); ctx->compiler.numidx = 0; } break; case 93: { fawkc_addi(ctx, FAWKI_MAKE_SYMREF); fawkc_addsymref(ctx, yyctx->stack.l_mark[-2].str, ctx->compiler.numidx, ctx->compiler.funcdef_offs); fawkc_addnum(ctx, ctx->compiler.numidx); fawk_free(ctx, yyctx->stack.l_mark[-2].str); ctx->compiler.numidx = fawk_pop_num(ctx, 1); } break; case 95: { ctx->compiler.numidx = -1; } break; case 96: { ctx->compiler.numidx++; } break; case 97: { ctx->compiler.numidx++; fawkc_addi(ctx, FAWKI_PUSH_STR); fawkc_adds(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); } break; case 98: { ctx->compiler.numidx++; fawkc_addi(ctx, FAWKI_PUSH_STR); fawkc_adds(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); } break; case 100: { parse_aix_expr(); } break; case 101: { fawkc_addi(ctx, FAWKI_CONCAT); } break; case 102: { lazy_binop1(ctx, 1); } break; case 103: { lazy_binop2(ctx, 1); } break; case 104: { lazy_binop1(ctx, 0); } break; case 105: { lazy_binop2(ctx, 0); } break; case 106: { fawk_push_num(ctx, ctx->compiler.numargs); ctx->compiler.numargs = 0; } break; case 107: { size_t old_numargs; old_numargs = fawk_pop_num(ctx, 1); fawkc_addi(ctx, FAWKI_CALL); fawkc_addnum(ctx, ctx->compiler.numargs); ctx->compiler.numargs = old_numargs; } break; case 109: { ctx->compiler.numargs++; } break; case 110: { ctx->compiler.numargs++; } break; case 111: { fawk_cell_t *lc = fawk_push_alloc(ctx), *lc2 = fawk_push_alloc(ctx); lc->data.arr = (fawk_arr_t *)ctx->compiler.labels; lc2->data.arr = (fawk_arr_t *)ctx->compiler.lablink; if (bas_init_labels(ctx) != 0) return -1; fawk_push_num(ctx, ctx->fp); fawkc_addi(ctx, FAWKI_JMP); FAWK_PUSH_IP(); fawkc_addnum(ctx, 777); ctx->fp = ctx->sp; ctx->compiler.funcdef_offs = ctx->sp; } break; case 112: { ctx->compiler.numfixedargs = -1; ctx->compiler.numargs = 0; } break; case 113: { fawk_symtab_regfunc(ctx, yyctx->stack.l_mark[-4].str, FAWK_CURR_IP(), ctx->compiler.numargs, ctx->compiler.numfixedargs); ctx->fp = ctx->sp; ctx->parser.curr_func = yyctx->stack.l_mark[-4].str; fawkc_addi(ctx, FAWKI_PUSH_NIL); } break; case 114: { int res; size_t jmp_over, n; fawk_cell_t cell; fawkc_addi(ctx, FAWKI_RET); fawkc_addnum(ctx, ctx->compiler.numargs + (ctx->compiler.numfixedargs >= 0)); for(n = 0; n < ctx->compiler.numargs + (ctx->compiler.numfixedargs >= 0); n++) { fawk_cell_t cell; fawk_pop(ctx, &cell); fawk_cell_free(ctx, &cell); } ctx->fp = 0; jmp_over = fawk_pop_num(ctx, 1); ctx->code.code[jmp_over].data.num = FAWK_CURR_IP(); ctx->fp = fawk_pop_num(ctx, 1); ctx->compiler.funcdef_offs = 0; res = bas_uninit_labels(ctx); fawk_pop(ctx, &cell); ctx->compiler.lablink = (fawk_htpp_t *)cell.data.arr; fawk_pop(ctx, &cell); ctx->compiler.labels = (fawk_htpp_t *)cell.data.arr; free(ctx->parser.curr_func); ctx->parser.curr_func = NULL; if (res != 0) return -1; } break; case 116: { fawk_push_str(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); ctx->compiler.numargs++; } break; case 117: { ctx->compiler.numfixedargs = ctx->compiler.numargs; fawk_push_str(ctx, "VARARG"); } break; case 118: { fawk_push_str(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); ctx->compiler.numargs++; } break; case 119: { ctx->compiler.numfixedargs = ctx->compiler.numargs; fawk_push_str(ctx, "VARARG"); } break; } yyctx->stack.s_mark -= yyctx->yym; yyctx->state = *yyctx->stack.s_mark; yyctx->stack.l_mark -= yyctx->yym; yyctx->yym = fbas_lhs[yyctx->yyn]; if (yyctx->state == 0 && yyctx->yym == 0) { yyctx->state = fbas_FINAL; *++yyctx->stack.s_mark = fbas_FINAL; *++yyctx->stack.l_mark = yyctx->val; if (yyctx->chr < 0) { fbas_GETCHAR(2); if (yyctx->chr < 0) yyctx->chr = fbas_EOF; } if (yyctx->chr == fbas_EOF) goto yyaccept; goto yyloop; } if (((yyctx->yyn = fbas_gindex[yyctx->yym]) != 0) && (yyctx->yyn += yyctx->state) >= 0 && yyctx->yyn <= fbas_TABLESIZE && fbas_check[yyctx->yyn] == (fbas_int_t) yyctx->state) yyctx->state = fbas_table[yyctx->yyn]; else yyctx->state = fbas_dgoto[yyctx->yym]; if (yyctx->stack.s_mark >= yyctx->stack.s_last && fbas_growstack(yyctx, &yyctx->stack) == fbas_ENOMEM) goto yyoverflow; *++yyctx->stack.s_mark = (fbas_int_t) yyctx->state; *++yyctx->stack.l_mark = yyctx->val; goto yyloop; yyoverflow: fbas_error(ctx, yyctx->lval, "yacc stack overflow"); yyabort: fbas_freestack(&yyctx->stack); return fbas_RES_ABORT; yyaccept: fbas_freestack(&yyctx->stack); return fbas_RES_DONE; } FAWK_API int fawk_lex_fbas(fbas_STYPE *lval, fawk_ctx_t *ctx) { int chr, nchr; char tmp[128]; restart:; if (ctx->parser.in_eof) goto handle_eof; lex_textblk_exec(BT_STRING, goto restart, '\r'); ctx->parser.used = 0; if (isalpha(chr) || (chr == '_')) { append(chr, return -1); for(;;) { chr = getch(ctx); if (!(isalpha(chr)) && !(isdigit(chr)) && (chr != '_') && (chr != '$')) { ungetch(ctx, chr); break; } append(chr, return -1); if (chr == '$') { append('\0', return -1); goto got_id; } } append('\0', return -1); if (genht_strcasecmp(ctx->parser.buff, "if") == 0) return BT_IF; else if (genht_strcasecmp(ctx->parser.buff, "then") == 0) return BT_THEN; else if (genht_strcasecmp(ctx->parser.buff, "else") == 0) return BT_ELSE; else if (genht_strcasecmp(ctx->parser.buff, "for") == 0) return BT_FOR; else if (genht_strcasecmp(ctx->parser.buff, "to") == 0) return BT_TO; else if (genht_strcasecmp(ctx->parser.buff, "step") == 0) return BT_STEP; else if (genht_strcasecmp(ctx->parser.buff, "next") == 0) return BT_NEXT; else if (genht_strcasecmp(ctx->parser.buff, "while") == 0) return BT_WHILE; else if (genht_strcasecmp(ctx->parser.buff, "do") == 0) return BT_DO; else if (genht_strcasecmp(ctx->parser.buff, "loop") == 0) return BT_LOOP; else if (genht_strcasecmp(ctx->parser.buff, "until") == 0) return BT_UNTIL; else if (genht_strcasecmp(ctx->parser.buff, "goto") == 0) return BT_GOTO; else if (genht_strcasecmp(ctx->parser.buff, "def") == 0) return BT_DEF; else if (genht_strcasecmp(ctx->parser.buff, "define") == 0) return BT_DEF; else if (genht_strcasecmp(ctx->parser.buff, "function") == 0) return BT_DEF; else if (genht_strcasecmp(ctx->parser.buff, "end") == 0) return BT_END; else if (genht_strcasecmp(ctx->parser.buff, "and") == 0) return BT_AND; else if (genht_strcasecmp(ctx->parser.buff, "or") == 0) return BT_OR; else if (genht_strcasecmp(ctx->parser.buff, "in") == 0) return BT_IN; else if (genht_strcasecmp(ctx->parser.buff, "let") == 0) return BT_LET; else if (genht_strcasecmp(ctx->parser.buff, "mod") == 0) return BT_MOD; else if (genht_strcasecmp(ctx->parser.buff, "not") == 0) return BT_NOT; else if ((ctx->parser.curr_func != NULL) && (genht_strcasecmp(ctx->parser.buff, ctx->parser.curr_func) == 0)) return BT_CURRFUNC; else if (genht_strcasecmp(ctx->parser.buff, "rem") == 0) { readtil(ctx, "\n"); goto restart; } else if (genht_strcasecmp(ctx->parser.buff, "include") == 0) { lex_include(goto restart); } else { got_id:; lval->str = fawk_strdup(ctx, ctx->parser.buff); return (lval->str == NULL) ? -1 : BT_ID; } } else if (isdigit(chr)) { append(chr, return -1); if (chr == '0') { nchr = getch(ctx); if (nchr == 'x') { fawk_readup(ctx, "0123456789abcdefABCDEF"); lval->num = strtol(ctx->parser.buff, NULL, 16); return BT_NUM; } ungetch(ctx, nchr); } return read_numeric(ctx, &lval->num, 0, BT_NUM); } else switch(chr) { case '@': case '?': case ':': case '(': case ')': case '\\': case ']': case ',': case '{': case '}': case '+': case '-': case '*': case '/': case '%': case '=': case '\n': return chr; case '[': { lex_textblk_start(goto restart); } case '.': nchr = getch(ctx); if (!isdigit(nchr)) { ungetch(ctx, nchr); return '.'; } else { append('.', return -1); append(nchr, return -1); return read_numeric(ctx, &lval->num, 1, BT_NUM); } break; case '<': nchr = getch(ctx); append(chr, return -1); switch(nchr) { case '=': return BT_LTEQ; case '>': return BT_NEQ; default: ungetch(ctx, nchr); return chr; } break; case_op_ch2('>', '=', BT_GTEQ); case '\"': if (read_strlit(ctx, '\"') == 0) return BT_NIL; lval->str = fawk_strdup(ctx, ctx->parser.buff); return (lval->str == NULL) ? -1 : BT_STRING; case EOF: { lex_got_eof(goto restart); } default: sprintf(tmp, "Invalid character on input: '%c'\n", chr); LIBFAWK_ERROR(ctx, tmp, ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1, -1); } } #ifndef _fpas__defines_h_ #define _fpas__defines_h_ typedef short fpas_int_t; #define fpas_chr yyctx->chr #define fpas_val yyctx->val #define fpas_lval yyctx->lval #define fpas_stack yyctx->stack #define fpas_debug yyctx->debug #define fpas_nerrs yyctx->nerrs #define fpas_errflag yyctx->errflag #define fpas_state yyctx->state #define fpas_yyn yyctx->yyn #define fpas_yym yyctx->yym #define fpas_jump yyctx->jump #define fpas_ctx_t fawk_ctx_t #define FAWK_PUSH_IP() fawk_push_num(ctx, ctx->code.used) #define FAWK_CURR_IP() ctx->code.used #define pas_func_ret() \ fawkc_addi(ctx, FAWKI_RET); \ fawkc_addnum(ctx, ctx->compiler.numargs + (ctx->compiler.numfixedargs >= 0)); typedef union { char *str; fawk_num_t num; } fpas_tokunion_t; typedef fpas_tokunion_t fpas_STYPE; #define PT_NEQ 257 #define PT_GTEQ 258 #define PT_LTEQ 259 #define PT_NIL 260 #define PT_IF 261 #define PT_THEN 262 #define PT_ELSE 263 #define PT_DO 264 #define PT_WHILE 265 #define PT_REPEAT 266 #define PT_UNTIL 267 #define PT_FOR 268 #define PT_DOWN 269 #define PT_TO 270 #define PT_FUNCTION 271 #define PT_BEGIN 272 #define PT_END 273 #define PT_CURRFUNC 274 #define PT_EXIT 275 #define PT_ID 276 #define PT_STRING 277 #define PT_NUM 278 #define PT_OR 279 #define PT_AND 280 #define PT_IN 281 #define PT_NOT 282 #define fpas_ERRCODE 256 #ifndef fpas_INITSTACKSIZE #define fpas_INITSTACKSIZE 200 #endif typedef struct { unsigned stacksize; fpas_int_t *s_base; fpas_int_t *s_mark; fpas_int_t *s_last; fpas_STYPE *l_base; fpas_STYPE *l_mark; } fpas_STACKDATA; typedef struct { int errflag; int chr; fpas_STYPE val; fpas_STYPE lval; int nerrs; int yym, yyn, state; int jump; int stack_max_depth; int debug; fpas_STACKDATA stack; } fpas_yyctx_t; typedef enum { fpas_RES_NEXT, fpas_RES_DONE, fpas_RES_ABORT } fpas_res_t; FAWK_API int fpas_parse_init(fpas_yyctx_t *yyctx); FAWK_API fpas_res_t fpas_parse(fpas_yyctx_t *yyctx, fpas_ctx_t *ctx, int tok, fpas_STYPE *lval); FAWK_API void fpas_error(fpas_ctx_t *ctx, fpas_STYPE tok, const char *msg); #endif FAWK_API int fawk_parse_fpas(fawk_ctx_t *ctx); #ifdef YY_QUERY_API_VER #define YY_BYACCIC #define YY_API_MAJOR 1 #define YY_API_MINOR 0 #endif #define fpas_EMPTY (-1) #define fpas_clearin (fpas_chr = fpas_EMPTY) #define fpas_errok (fpas_errflag = 0) #define fpas_RECOVERING() (fpas_errflag != 0) #define fpas_ENOMEM (-2) #define fpas_EOF 0 static const fpas_int_t fpas_lhs[] = {-1,3,0,2,2,5,5,5,5,5,5,5,13,13,12,12,15,15,6,18,8,9,19,9,11,11,22,21,23,24,25,20,1,1,10,10,28,29,26,30,27,14,32,14,31,33,35,4,34,34,34,34,34,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,39,16,38,38,38,38,38,40,41,40,42,36,43,37,45,7,44,44,44,}; static const fpas_int_t fpas_len[] = {2,0,2,2,0,1,1,1,1,1,1,3,1,1,0,2,2,1,4,0,5,1,0,5,1,1,0,7,0,0,0,11,2,3,1,1,0,0,6,0,5,3,0,5,3,0,0,12,0,1,3,3,5,1,1,1,1,2,1,3,3,3,3,3,3,3,3,1,1,3,3,3,3,3,2,2,2,3,0,3,0,3,4,3,3,1,0,4,0,4,0,4,0,5,0,1,3,}; static const fpas_int_t fpas_defred[] = {1,0,0,0,2,0,45,3,0,0,49,0,0,0,0,0,50,46,51,0,0,0,14,52,0,55,0,36,39,0,14,0,0,0,78,54,53,0,0,0,0,0,0,5,0,7,8,9,10,17,15,0,0,24,25,34,35,0,67,68,58,56,0,0,14,0,0,0,47,0,22,80,0,0,0,0,57,16,0,0,0,0,88,90,0,0,0,0,0,0,0,0,0,0,92,12,0,13,44,0,0,0,0,11,19,0,0,59,0,0,0,0,0,0,66,0,0,0,0,0,0,0,0,0,0,42,41,37,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,0,23,83,84,81,0,0,0,93,43,38,0,0,82,86,96,0,27,0,0,0,30,0,0,0,0,0,0,31,}; static const fpas_int_t fpas_dgoto[] = {1,166,4,2,5,42,43,60,45,46,47,48,24,96,49,50,61,52,131,105,53,54,157,143,161,170,55,56,63,141,64,57,140,8,12,20,58,59,106,71,151,163,112,113,139,124,}; static const fpas_int_t fpas_sindex[] = {0,0,-255,-247,0,-255,0,0,2,-35,0,6,16,26,-15,-31,0,0,0,28,-189,49,0,0,105,0,250,0,0,-176,0,40,48,69,0,0,0,250,250,250,250,-165,60,0,0,0,0,0,0,0,0,66,469,0,0,0,0,276,0,0,0,0,131,250,0,68,-153,141,0,73,0,0,54,54,89,384,0,0,77,250,250,250,0,0,-165,250,250,250,250,250,250,250,250,250,0,0,-57,0,0,393,217,85,-165,0,0,250,-18,0,250,589,589,589,250,250,0,589,589,589,-2,54,54,89,89,89,250,0,0,0,250,0,-117,250,426,-208,174,469,528,554,460,110,276,276,469,250, 0,469,0,0,0,0,469,-30,250,0,0,0,469,276,0,0,0,-177,0,250,-116,250,0,469,250,469,-111,469,276,0,}; static const fpas_int_t fpas_rindex[] = {0,0,155,0,0,155,0,0,0,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-50,0,0,0,0,0,0,0,0,0,0,80,0,0,0,0,0,0,519,0,0,0,0,0,0,0,0,0,0,0,0,0,-45,0,0,0,0,0,0,561,617,-11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-37,0,0,810,858,883,0,0,0,922,952,966,785,652,710,17,43,71,115,0,0,0,0,0,0,0,0,0,0,-47,992,814,116,0,0,0,-40,0,0,-39,0,0,0,0,-27,0,115,0,0,0,-172,0,0,0,0,0,0,0,0,0,0,-26,0,-106,0,-105,0,0,}; static const fpas_int_t fpas_gindex[] = {0,0,157,0,0,-20,0,137,0,0,0,0,-17,-102,8,20,1210,1244,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,}; #define fpas_TABLESIZE 1412 static const fpas_int_t fpas_table[] = {79,78,126,79,79,79,79,79,79,21,79,11,18,67,159,19,3,85,87,40,20,79,79,79,79,79,76,79,133,6,76,76,76,76,76,93,76,95,94,155,91,89,9,90,17,92,78,100,76,76,76,76,13,76,71,162,79,14,71,71,71,71,71,158,71,97,85,87,147,148,173,48,16,134,21,15,71,71,71,71,72,71,76,22,72,72,72,72,72,48,72,93,164,165,94,23,91,29,29,68,65,92,72,72,72,72,69,72,73,70,71,34,73,73,73,73,73,58,73,77,58,95,58,58,78,58,101,58,102,94,73,73,73,73,104,73,72,95,108,6,58,58,58,41,58,40,129, 144,37,97,38,153,95,172,168,4,94,95,32,33,154,44,7,160,73,97,0,0,93,0,0,94,0,91,89,0,90,0,92,41,97,40,0,0,37,0,38,0,0,0,0,86,85,87,44,88,0,0,0,0,0,0,0,0,44,0,125,0,0,0,0,0,41,21,40,0,18,37,0,38,79,79,79,40,20,79,79,79,0,0,0,0,79,79,0,0,78,44,0,0,0,10,79,79,79,18,76,76,76,0,0,76,76,76,0,41,0,40,76,76,37,0,38,0,0,0,0,149,76,76,76,0,0,0,71,71,71,44,44,71,71,71,0,0,0,0,71,71,41,0,40,0,0,37,44,38,71,71,71,0,72,72,72,0,0,72,72,72,0,44,0,0,72,72,41,0,40,0,0,37,0,38,72,72,72,0,0,0,73,73,73,0,0,73, 73,73,0,58,58,58,73,73,0,6,0,0,0,0,0,0,73,73,73,0,0,0,0,0,0,58,58,58,0,0,0,25,26,0,0,0,27,28,0,29,0,0,0,30,31,32,33,34,35,36,0,0,0,39,79,80,81,0,0,98,0,0,0,0,0,0,0,25,26,0,0,0,27,28,0,29,82,83,84,30,103,32,33,34,35,36,0,93,0,39,94,107,91,89,0,90,93,92,0,94,25,91,89,0,90,0,92,0,0,0,86,85,87,0,88,0,34,35,36,86,85,87,39,88,0,0,0,0,0,93,0,0,94,146,91,89,0,90,0,92,0,0,0,25,26,0,0,0,27,28,128,29,86,85,87,30,88,32,33,34,35,36,0,93,0,39,94,0,91,89,152,90,93,92,0,94,25,91,89,0,90,0,92,0,0,0,86,85,87, 0,88,0,34,35,36,86,85,87,39,88,0,0,25,26,0,0,0,27,28,0,29,0,0,0,30,0,32,33,34,35,36,0,56,0,39,56,0,56,56,0,56,93,56,0,94,0,91,89,0,90,0,92,0,0,0,56,56,56,0,56,0,0,0,0,86,85,87,93,88,0,94,0,91,89,0,90,0,92,75,0,75,75,75,0,0,0,0,0,0,0,86,85,87,0,88,0,75,75,75,75,0,75,93,0,0,94,0,91,89,0,90,0,92,0,0,0,0,79,80,81,0,0,0,0,0,0,79,80,81,88,75,0,0,127,74,0,74,74,74,82,83,84,0,0,0,0,0,0,82,83,84,0,74,74,74,74,0,74,0,79,80,81,0,0,0,0,0,0,0,69,0,69,69,69,0,0,0,0,0,0,0,82,83,84,0,0,74,69,69,69,69,0,69, 79,80,81,0,0,0,0,0,0,79,80,81,0,0,0,0,0,0,0,0,0,0,82,83,84,0,0,0,69,0,0,82,83,84,70,0,70,70,70,0,0,0,0,0,0,0,0,0,0,0,0,0,70,70,70,70,0,70,0,56,56,56,0,0,0,0,0,0,79,80,81,0,0,0,0,0,0,0,0,0,0,56,56,56,0,0,70,0,0,0,0,83,84,0,79,80,81,0,0,0,0,75,75,75,0,0,75,75,75,77,0,0,77,75,75,0,0,0,84,0,0,0,0,75,75,75,0,77,77,77,77,0,77,0,61,0,0,61,91,0,0,91,0,0,0,0,0,0,0,0,0,0,61,61,61,61,91,74,74,74,0,77,74,74,74,0,0,0,0,74,74,0,0,0,0,0,0,0,0,74,74,74,62,0,0,62,61,0,0,0,91,0,69,69,69,0,0,69,69,69,62,62,62, 62,69,69,0,63,0,0,63,0,0,0,69,69,69,0,0,0,0,0,0,0,0,63,63,63,63,0,0,0,0,0,62,0,0,0,0,0,0,0,0,0,0,0,60,0,0,60,70,70,70,0,0,70,70,70,0,63,0,0,70,70,60,60,60,60,0,0,0,0,70,70,70,0,65,0,0,65,0,0,0,0,0,0,0,0,0,0,64,0,0,64,65,65,65,65,60,0,0,0,0,0,0,0,0,0,64,64,64,64,0,0,0,0,89,0,0,89,0,0,0,0,0,77,77,77,65,0,77,77,77,0,89,0,0,77,77,0,0,0,64,0,0,0,0,77,77,77,61,61,61,0,0,61,61,61,0,91,91,91,61,61,0,0,91,91,89,0,0,0,61,61,61,0,91,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,62,62,0,0,62,62,62,0,0, 0,0,62,62,0,0,0,0,0,0,0,0,62,62,62,63,63,63,0,0,63,63,63,0,0,0,0,63,63,0,0,0,0,0,0,0,0,63,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,60,60,0,0,60,60,60,0,0,0,0,60,60,0,0,0,0,0,0,0,0,60,60,60,0,0,0,0,0,65,65,65,0,0,65,65,65,0,0,0,0,65,65,64,64,64,0,0,64,64,64,65,65,65,51,64,64,0,0,66,0,0,0,0,0,64,64,64,0,0,0,76,0,0,89,89,89,0,0,0,0,89,89,0,0,0,0,51,0,0,62,89,0,0,0,0,0,51,0,0,0,72,73,74,75,0,0,0,0,0,0,0,0,0,114,0,0,0,0,0,0,0,0,0,0,0,0,99,0,0,51,0,130,0,0,0,0,0,0,0,0,0,0,109,110,111,0,0,0,115,116,117, 118,119,120,121,122,123,0,0,0,0,0,0,0,0,0,0,0,132,51,51,135,0,0,0,136,137,0,0,0,0,0,0,0,0,0,51,138,0,0,0,142,0,0,145,0,0,150,0,0,0,51,0,0,0,0,156,0,0,0,0,0,0,0,0,138,0,0,0,0,0,0,0,0,0,0,167,0,169,0,0,171,}; static const fpas_int_t fpas_check[] = {37,46,59,40,41,42,43,44,45,59,47,46,59,30,44,46,271,44,44,59,59,58,59,60,61,62,37,64,46,276,41,42,43,44,45,37,47,57,40,141,42,43,40,45,59,47,91,64,59,60,61,62,46,64,37,157,93,41,41,42,43,44,45,93,47,57,93,93,276,277,172,41,46,91,46,59,59,60,61,62,37,64,93,272,41,42,43,44,45,59,47,37,269,270,40, 46,42,269,270,59,276,47,59,60,61,62,58,64,37,40,93,276,41,42,43,44,45,37,47,59,40,141,42,43,58,45,58,47,281,40,59,60,61,62,61,64,93,157,61,59,60,61,62,38,64,40,61,264,43,141,45,41,172,264,270,0,41,41,264,264,140,24,5,152,93,157,-1,-1,37,-1,-1,40,-1,42,43,-1,45,-1,47,38,172,40,-1,-1,43,-1,45,-1,-1,-1,-1,60,61,62,57,64,-1,-1,-1,-1,-1,-1,-1,-1,67,-1,263,-1,-1,-1,-1,-1,38,263,40,-1,263,43,-1,45,257,258,259,263,263,262,263,264,-1,-1,-1,-1,269,270,-1,-1,281,100,-1,-1,-1,276,279,280,281,276,257,258, 259,-1,-1,262,263,264,-1,38,-1,40,269,270,43,-1,45,-1,-1,-1,-1,93,279,280,281,-1,-1,-1,257,258,259,140,141,262,263,264,-1,-1,-1,-1,269,270,38,-1,40,-1,-1,43,157,45,279,280,281,-1,257,258,259,-1,-1,262,263,264,-1,172,-1,-1,269,270,38,-1,40,-1,-1,43,-1,45,279,280,281,-1,-1,-1,257,258,259,-1,-1,262,263,264,-1,257,258,259,269,270,-1,263,-1,-1,-1,-1,-1,-1,279,280,281,-1,-1,-1,-1,-1,-1,279,280,281,-1,-1,-1,260,261,-1,-1,-1,265,266,-1,268,-1,-1,-1,272,273,274,275,276,277,278,-1,-1,-1,282,257,258, 259,-1,-1,262,-1,-1,-1,-1,-1,-1,-1,260,261,-1,-1,-1,265,266,-1,268,279,280,281,272,273,274,275,276,277,278,-1,37,-1,282,40,41,42,43,-1,45,37,47,-1,40,260,42,43,-1,45,-1,47,-1,-1,-1,60,61,62,-1,64,-1,276,277,278,60,61,62,282,64,-1,-1,-1,-1,-1,37,-1,-1,40,41,42,43,-1,45,-1,47,-1,-1,-1,260,261,-1,-1,-1,265,266,267,268,60,61,62,272,64,274,275,276,277,278,-1,37,-1,282,40,-1,42,43,44,45,37,47,-1,40,260,42,43,-1,45,-1,47,-1,-1,-1,60,61,62,-1,64,-1,276,277,278,60,61,62,282,64,-1,-1,260,261,-1,-1,-1, 265,266,-1,268,-1,-1,-1,272,-1,274,275,276,277,278,-1,37,-1,282,40,-1,42,43,-1,45,37,47,-1,40,-1,42,43,-1,45,-1,47,-1,-1,-1,60,61,62,-1,64,-1,-1,-1,-1,60,61,62,37,64,-1,40,-1,42,43,-1,45,-1,47,41,-1,43,44,45,-1,-1,-1,-1,-1,-1,-1,60,61,62,-1,64,-1,59,60,61,62,-1,64,37,-1,-1,40,-1,42,43,-1,45,-1,47,-1,-1,-1,-1,257,258,259,-1,-1,-1,-1,-1,-1,257,258,259,64,93,-1,-1,264,41,-1,43,44,45,279,280,281,-1,-1,-1,-1,-1,-1,279,280,281,-1,59,60,61,62,-1,64,-1,257,258,259,-1,-1,-1,-1,-1,-1,-1,41,-1,43,44, 45,-1,-1,-1,-1,-1,-1,-1,279,280,281,-1,-1,93,59,60,61,62,-1,64,257,258,259,-1,-1,-1,-1,-1,-1,257,258,259,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,279,280,281,-1,-1,-1,93,-1,-1,279,280,281,41,-1,43,44,45,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,59,60,61,62,-1,64,-1,257,258,259,-1,-1,-1,-1,-1,-1,257,258,259,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,279,280,281,-1,-1,93,-1,-1,-1,-1,280,281,-1,257,258,259,-1,-1,-1,-1,257,258,259,-1,-1,262,263,264,41,-1,-1,44,269,270,-1,-1,-1,281,-1,-1,-1,-1,279,280,281,-1,59,60,61,62, -1,64,-1,41,-1,-1,44,41,-1,-1,44,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,59,60,61,62,59,257,258,259,-1,93,262,263,264,-1,-1,-1,-1,269,270,-1,-1,-1,-1,-1,-1,-1,-1,279,280,281,41,-1,-1,44,93,-1,-1,-1,93,-1,257,258,259,-1,-1,262,263,264,59,60,61,62,269,270,-1,41,-1,-1,44,-1,-1,-1,279,280,281,-1,-1,-1,-1,-1,-1,-1,-1,59,60,61,62,-1,-1,-1,-1,-1,93,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,41,-1,-1,44,257,258,259,-1,-1,262,263,264,-1,93,-1,-1,269,270,59,60,61,62,-1,-1,-1,-1,279,280,281,-1,41,-1,-1,44,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,41,-1,-1,44,59,60,61,62,93,-1,-1,-1,-1,-1,-1,-1,-1,-1,59,60,61,62,-1,-1,-1,-1,41,-1,-1,44,-1,-1,-1,-1,-1,257,258,259,93,-1,262,263,264,-1,59,-1,-1,269,270,-1,-1,-1,93,-1,-1,-1,-1,279,280,281,257,258,259,-1,-1,262,263,264,-1,262,263,264,269,270,-1,-1,269,270,93,-1,-1,-1,279,280,281,-1,279,280,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,257,258,259,-1,-1,262,263,264,-1,-1,-1,-1,269,270,-1,-1,-1,-1,-1,-1,-1,-1,279,280,281,257,258,259,-1,-1,262,263,264,-1,-1,-1,-1, 269,270,-1,-1,-1,-1,-1,-1,-1,-1,279,280,281,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,257,258,259,-1,-1,262,263,264,-1,-1,-1,-1,269,270,-1,-1,-1,-1,-1,-1,-1,-1,279,280,281,-1,-1,-1,-1,-1,257,258,259,-1,-1,262,263,264,-1,-1,-1,-1,269,270,257,258,259,-1,-1,262,263,264,279,280,281,24,269,270,-1,-1,29,-1,-1,-1,-1,-1,279,280,281,-1,-1,-1,41,-1,-1,262,263,264,-1,-1,-1,-1,269,270,-1,-1,-1,-1,57,-1,-1,26,279,-1,-1,-1,-1,-1,67,-1,-1,-1,37,38,39,40,-1,-1,-1,-1,-1,-1,-1,-1,-1,84,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,63,-1,-1,100,-1,102,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,79,80,81,-1,-1,-1,85,86,87,88,89,90,91,92,93,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,105,140,141,108,-1,-1,-1,112,113,-1,-1,-1,-1,-1,-1,-1,-1,-1,157,124,-1,-1,-1,128,-1,-1,131,-1,-1,134,-1,-1,-1,172,-1,-1,-1,-1,143,-1,-1,-1,-1,-1,-1,-1,-1,152,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,163,-1,165,-1,-1,168,}; #define fpas_FINAL 1 #define fpas_MAXTOKEN 282 #define fpas_UNDFTOKEN 330 #define fpas_TRANSLATE(a) ((a) > fpas_MAXTOKEN ? fpas_UNDFTOKEN : (a)) FAWK_API void fpas_error(fpas_ctx_t *ctx, fpas_STYPE tok, const char *msg) { libfawk_error(ctx, msg, ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1); } FAWK_API int fawk_lex_fpas(fpas_STYPE *lval, fawk_ctx_t *ctx); FAWK_API int fawk_parse_fpas(fawk_ctx_t *ctx) { fawk_parser_loop(fpas_yyctx_t, fpas_STYPE, fawk_lex_fpas, fpas_parse, ctx, fpas_RES_NEXT, fpas_RES_DONE); } static int fpas_growstack(fpas_yyctx_t *yyctx, fpas_STACKDATA *data) { int i; unsigned newsize; fpas_int_t *newss; fpas_STYPE *newvs; if ((newsize = data->stacksize) == 0) newsize = fpas_INITSTACKSIZE; else if (newsize >= yyctx->stack_max_depth) return fpas_ENOMEM; else if ((newsize *= 2) > yyctx->stack_max_depth) newsize = yyctx->stack_max_depth; i = (int)(data->s_mark - data->s_base); newss = (fpas_int_t *) realloc(data->s_base, newsize * sizeof(*newss)); if (newss == 0) return fpas_ENOMEM; data->s_base = newss; data->s_mark = newss + i; newvs = (fpas_STYPE *) realloc(data->l_base, newsize * sizeof(*newvs)); if (newvs == 0) return fpas_ENOMEM; data->l_base = newvs; data->l_mark = newvs + i; data->stacksize = newsize; data->s_last = data->s_base + newsize - 1; return 0; } static void fpas_freestack(fpas_STACKDATA *data) { free(data->s_base); free(data->l_base); memset(data, 0, sizeof(*data)); } #define fpas_ABORT goto yyabort #define fpas_REJECT goto yyabort #define fpas_ACCEPT goto yyaccept #define fpas_ERROR goto yyerrlab int fpas_parse_init(fpas_yyctx_t *yyctx) { memset(&yyctx->val, 0, sizeof(yyctx->val)); memset(&yyctx->lval, 0, sizeof(yyctx->lval)); yyctx->yym = 0; yyctx->yyn = 0; yyctx->nerrs = 0; yyctx->errflag = 0; yyctx->chr = fpas_EMPTY; yyctx->state = 0; memset(&yyctx->stack, 0, sizeof(yyctx->stack)); yyctx->stack_max_depth = fpas_INITSTACKSIZE > 10000 ? fpas_INITSTACKSIZE : 10000; if (yyctx->stack.s_base == NULL && fpas_growstack(yyctx, &yyctx->stack) == fpas_ENOMEM) return -1; yyctx->stack.s_mark = yyctx->stack.s_base; yyctx->stack.l_mark = yyctx->stack.l_base; yyctx->state = 0; *yyctx->stack.s_mark = 0; yyctx->jump = 0; return 0; } #define fpas_GETCHAR(labidx) \ do { \ if (used) { yyctx->jump = labidx; return fpas_RES_NEXT; } \ getchar_ ## labidx:; yyctx->chr = tok; yyctx->lval = *lval; used = 1; \ } while(0) fpas_res_t fpas_parse(fpas_yyctx_t *yyctx, fpas_ctx_t *ctx, int tok, fpas_STYPE *lval) { int used = 0; yyloop:; if (yyctx->jump == 1) { yyctx->jump = 0; goto getchar_1; } if (yyctx->jump == 2) { yyctx->jump = 0; goto getchar_2; } if ((yyctx->yyn = fpas_defred[yyctx->state]) != 0) goto yyreduce; if (yyctx->chr < 0) { fpas_GETCHAR(1); if (yyctx->chr < 0) yyctx->chr = fpas_EOF; } if (((yyctx->yyn = fpas_sindex[yyctx->state]) != 0) && (yyctx->yyn += yyctx->chr) >= 0 && yyctx->yyn <= fpas_TABLESIZE && fpas_check[yyctx->yyn] == (fpas_int_t) yyctx->chr) { if (yyctx->stack.s_mark >= yyctx->stack.s_last && fpas_growstack(yyctx, &yyctx->stack) == fpas_ENOMEM) goto yyoverflow; yyctx->state = fpas_table[yyctx->yyn]; *++yyctx->stack.s_mark = fpas_table[yyctx->yyn]; *++yyctx->stack.l_mark = yyctx->lval; yyctx->chr = fpas_EMPTY; if (yyctx->errflag > 0) --yyctx->errflag; goto yyloop; } if (((yyctx->yyn = fpas_rindex[yyctx->state]) != 0) && (yyctx->yyn += yyctx->chr) >= 0 && yyctx->yyn <= fpas_TABLESIZE && fpas_check[yyctx->yyn] == (fpas_int_t) yyctx->chr) { yyctx->yyn = fpas_table[yyctx->yyn]; goto yyreduce; } if (yyctx->errflag != 0) goto yyinrecovery; fpas_error(ctx, yyctx->lval, "syntax error"); goto yyerrlab; yyerrlab: ++yyctx->nerrs; yyinrecovery: if (yyctx->errflag < 3) { yyctx->errflag = 3; for(;;) { if (((yyctx->yyn = fpas_sindex[*yyctx->stack.s_mark]) != 0) && (yyctx->yyn += fpas_ERRCODE) >= 0 && yyctx->yyn <= fpas_TABLESIZE && fpas_check[yyctx->yyn] == (fpas_int_t) fpas_ERRCODE) { if (yyctx->stack.s_mark >= yyctx->stack.s_last && fpas_growstack(yyctx, &yyctx->stack) == fpas_ENOMEM) goto yyoverflow; yyctx->state = fpas_table[yyctx->yyn]; *++yyctx->stack.s_mark = fpas_table[yyctx->yyn]; *++yyctx->stack.l_mark = yyctx->lval; goto yyloop; } else { if (yyctx->stack.s_mark <= yyctx->stack.s_base) goto yyabort; --yyctx->stack.s_mark; --yyctx->stack.l_mark; } } } else { if (yyctx->chr == fpas_EOF) goto yyabort; yyctx->chr = fpas_EMPTY; goto yyloop; } yyreduce: yyctx->yym = fpas_len[yyctx->yyn]; if (yyctx->yym > 0) yyctx->val = yyctx->stack.l_mark[1 - yyctx->yym]; else memset(&yyctx->val, 0, sizeof yyctx->val); switch (yyctx->yyn) { case 1: { fawkc_addi(ctx, FAWKI_ABORT); fawkc_addcs(ctx, "uninitialized execute"); } break; case 2: { fawkc_addi(ctx, FAWKI_ABORT); fawkc_addcs(ctx, "ran beyond the script"); } break; case 6: { fawkc_addi(ctx, FAWKI_POP); } break; case 18: { fawkc_addi(ctx, FAWKI_SET); fawkc_addi(ctx, FAWKI_PUSH_SYMVAL); fawkc_addi(ctx, FAWKI_POP); } break; case 19: { fawkc_addi(ctx, FAWKI_POP); } break; case 21: { pas_func_ret(); } break; case 22: { fawkc_addi(ctx, FAWKI_POP); } break; case 23: { pas_func_ret(); } break; case 26: { fawkc_addi(ctx, FAWKI_FORIN_FIRST); fawkc_addi(ctx, FAWKI_JMP); fawkc_addnum(ctx, 0); FAWK_PUSH_IP(); } break; case 27: { size_t begin = fawk_pop_num(ctx, 1); fawkc_addi(ctx, FAWKI_FORIN_NEXT); fawkc_addnum(ctx, begin); ctx->code.code[begin-1].data.num = FAWK_CURR_IP()-2; } break; case 28: { fawkc_addi(ctx, FAWKI_MAKE_SYMREF); fawkc_addsymref(ctx, yyctx->stack.l_mark[-2].str, 0, ctx->compiler.funcdef_offs); fawkc_addnum(ctx, 0); } break; case 29: { fawkc_addi(ctx, FAWKI_SET); fawkc_addi(ctx, FAWKI_PUSH_SYMVAL); fawkc_addi(ctx, FAWKI_POP); fawkc_addi(ctx, FAWKI_JMP); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); FAWK_PUSH_IP(); } break; case 30: { fawkc_addi(ctx, FAWKI_MAKE_SYMREF); fawkc_addsymref(ctx, yyctx->stack.l_mark[-6].str, 0, ctx->compiler.funcdef_offs); fawkc_addnum(ctx, 0); fawkc_addi(ctx, FAWKI_PUSH_SYMVAL); fawkc_addi(ctx, FAWKI_SUB); fawkc_addi(ctx, FAWKI_PUSH_NUM); fawkc_addnum(ctx, yyctx->stack.l_mark[0].num); fawkc_addi(ctx, FAWKI_PUSH_REL); fawkc_addnum(ctx, -1); fawkc_addi(ctx, FAWKI_PUSH_REL); fawkc_addnum(ctx, -3); fawkc_addi(ctx, FAWKI_PUSH_REL); fawkc_addnum(ctx, -2); fawkc_addi(ctx, FAWKI_SUB); fawkc_addi(ctx, FAWKI_MUL); fawkc_addi(ctx, FAWKI_PUSH_NUM); fawkc_addnum(ctx, 0); fawkc_addi(ctx, FAWKI_GTEQ); fawkc_addi(ctx, FAWKI_POPJZ); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); fawkc_addi(ctx, FAWKI_MAKE_SYMREF); fawkc_addsymref(ctx, yyctx->stack.l_mark[-6].str, 0, ctx->compiler.funcdef_offs); fawkc_addnum(ctx, 0); fawkc_addi(ctx, FAWKI_PUSH_TOPVAR); fawkc_addi(ctx, FAWKI_PUSH_REL); fawkc_addnum(ctx, -3); fawkc_addi(ctx, FAWKI_ADD); fawkc_addi(ctx, FAWKI_SET); fawkc_addi(ctx, FAWKI_POP); fawkc_addi(ctx, FAWKI_POP); fawkc_addi(ctx, FAWKI_POP); fawkc_addi(ctx, FAWKI_JMP); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); fawkc_addi(ctx, FAWKI_JMP); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); } break; case 31: { size_t jumpin, jback3rd, jskip, jout, rerun; jback3rd = fawk_pop_num(ctx, 1); jskip = fawk_pop_num(ctx, 1); jout = fawk_pop_num(ctx, 1); rerun = fawk_pop_num(ctx, 1); jumpin = fawk_pop_num(ctx, 1); fawkc_addi(ctx, FAWKI_JMP); fawkc_addnum(ctx, jskip+1); ctx->code.code[jumpin].data.num = jback3rd+1; ctx->code.code[jback3rd].data.num = rerun; ctx->code.code[jskip].data.num = jback3rd+1; ctx->code.code[jout].data.num = FAWK_CURR_IP(); fawkc_addi(ctx, FAWKI_POP); fawkc_addi(ctx, FAWKI_POP); } break; case 32: { yyctx->val.num = +1; } break; case 33: { yyctx->val.num = -1; } break; case 36: { FAWK_PUSH_IP(); } break; case 37: { fawkc_addi(ctx, FAWKI_POPJZ); FAWK_PUSH_IP(); fawkc_addnum(ctx, 0); } break; case 38: { loop_pretest_jumpback(); } break; case 39: { FAWK_PUSH_IP(); } break; case 40: { fawkc_addi(ctx, FAWKI_POPJZ); fawkc_addnum(ctx, fawk_pop_num(ctx, 1));} break; case 41: { size_t jmp1 = fawk_pop_num(ctx, 1); ctx->code.code[jmp1].data.num = FAWK_CURR_IP(); } break; case 42: { fawkc_addi(ctx, FAWKI_JMP); FAWK_PUSH_IP(); fawkc_addnum(ctx, 777); } break; case 43: { size_t jmp_then_post = fawk_pop_num(ctx, 1), jmp_if = fawk_pop_num(ctx, 1); ctx->code.code[jmp_then_post].data.num = FAWK_CURR_IP(); ctx->code.code[jmp_if].data.num = jmp_then_post+1; } break; case 44: { fawkc_addi(ctx, FAWKI_POPJZ); FAWK_PUSH_IP(); fawkc_addnum(ctx, 777); } break; case 45: { ctx->compiler.numfixedargs = -1; ctx->compiler.numargs = 0; } break; case 46: { fawk_symtab_regfunc(ctx, yyctx->stack.l_mark[-5].str, FAWK_CURR_IP(), ctx->compiler.numargs, ctx->compiler.numfixedargs); ctx->fp = ctx->sp; ctx->parser.curr_func = yyctx->stack.l_mark[-5].str; fawkc_addi(ctx, FAWKI_PUSH_NIL); } break; case 47: { int n; pas_func_ret(); for(n = 0; n < ctx->compiler.numargs + (ctx->compiler.numfixedargs >= 0); n++) { fawk_cell_t cell; fawk_pop(ctx, &cell); fawk_cell_free(ctx, &cell); } ctx->fp = 0; free(ctx->parser.curr_func); ctx->parser.curr_func = NULL; } break; case 49: { fawk_push_str(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); ctx->compiler.numargs++; } break; case 50: { ctx->compiler.numfixedargs = ctx->compiler.numargs; fawk_push_str(ctx, "VARARG"); } break; case 51: { fawk_push_str(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); ctx->compiler.numargs++; } break; case 52: { ctx->compiler.numfixedargs = ctx->compiler.numargs; fawk_push_str(ctx, "VARARG"); } break; case 53: { fawkc_addi(ctx, FAWKI_PUSH_NUM); fawkc_addnum(ctx, yyctx->stack.l_mark[0].num); } break; case 54: { fawkc_addi(ctx, FAWKI_PUSH_STR); fawkc_adds(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); } break; case 55: { fawkc_addi(ctx, FAWKI_PUSH_NIL); } break; case 56: { fawkc_addi(ctx, FAWKI_PUSH_SYMVAL); } break; case 60: { fawkc_addi(ctx, FAWKI_EQ); } break; case 61: { fawkc_addi(ctx, FAWKI_NEQ); } break; case 62: { fawkc_addi(ctx, FAWKI_GTEQ); } break; case 63: { fawkc_addi(ctx, FAWKI_LTEQ); } break; case 64: { fawkc_addi(ctx, FAWKI_GT); } break; case 65: { fawkc_addi(ctx, FAWKI_LT); } break; case 66: { fawkc_addi(ctx, FAWKI_IN); } break; case 69: { fawkc_addi(ctx, FAWKI_ADD); } break; case 70: { fawkc_addi(ctx, FAWKI_SUB); } break; case 71: { fawkc_addi(ctx, FAWKI_MUL); } break; case 72: { fawkc_addi(ctx, FAWKI_DIV); } break; case 73: { fawkc_addi(ctx, FAWKI_MOD); } break; case 74: { fawkc_addi(ctx, FAWKI_NEG); } break; case 76: { fawkc_addi(ctx, FAWKI_NOT); } break; case 77: { fawkc_addi(ctx, FAWKI_CONCAT); } break; case 78: { fawk_push_num(ctx, ctx->compiler.numidx); ctx->compiler.numidx = 0; } break; case 79: { fawkc_addi(ctx, FAWKI_MAKE_SYMREF); fawkc_addsymref(ctx, yyctx->stack.l_mark[-2].str, ctx->compiler.numidx, 0); fawkc_addnum(ctx, ctx->compiler.numidx); fawk_free(ctx, yyctx->stack.l_mark[-2].str); ctx->compiler.numidx = fawk_pop_num(ctx, 1); } break; case 81: { ctx->compiler.numidx = -1; } break; case 82: { ctx->compiler.numidx++; } break; case 83: { ctx->compiler.numidx++; fawkc_addi(ctx, FAWKI_PUSH_STR); fawkc_adds(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); } break; case 84: { ctx->compiler.numidx++; fawkc_addi(ctx, FAWKI_PUSH_STR); fawkc_adds(ctx, yyctx->stack.l_mark[0].str); fawk_free(ctx, yyctx->stack.l_mark[0].str); } break; case 86: { parse_aix_expr(); } break; case 87: { fawkc_addi(ctx, FAWKI_CONCAT); } break; case 88: { lazy_binop1(ctx, 1); } break; case 89: { lazy_binop2(ctx, 1); } break; case 90: { lazy_binop1(ctx, 0); } break; case 91: { lazy_binop2(ctx, 0); } break; case 92: { fawk_push_num(ctx, ctx->compiler.numargs); ctx->compiler.numargs = 0; ctx->code.used--; } break; case 93: { size_t old_numargs; old_numargs = fawk_pop_num(ctx, 1); fawkc_addi(ctx, FAWKI_CALL); fawkc_addnum(ctx, ctx->compiler.numargs); ctx->compiler.numargs = old_numargs; } break; case 95: { ctx->compiler.numargs++; } break; case 96: { ctx->compiler.numargs++; } break; } yyctx->stack.s_mark -= yyctx->yym; yyctx->state = *yyctx->stack.s_mark; yyctx->stack.l_mark -= yyctx->yym; yyctx->yym = fpas_lhs[yyctx->yyn]; if (yyctx->state == 0 && yyctx->yym == 0) { yyctx->state = fpas_FINAL; *++yyctx->stack.s_mark = fpas_FINAL; *++yyctx->stack.l_mark = yyctx->val; if (yyctx->chr < 0) { fpas_GETCHAR(2); if (yyctx->chr < 0) yyctx->chr = fpas_EOF; } if (yyctx->chr == fpas_EOF) goto yyaccept; goto yyloop; } if (((yyctx->yyn = fpas_gindex[yyctx->yym]) != 0) && (yyctx->yyn += yyctx->state) >= 0 && yyctx->yyn <= fpas_TABLESIZE && fpas_check[yyctx->yyn] == (fpas_int_t) yyctx->state) yyctx->state = fpas_table[yyctx->yyn]; else yyctx->state = fpas_dgoto[yyctx->yym]; if (yyctx->stack.s_mark >= yyctx->stack.s_last && fpas_growstack(yyctx, &yyctx->stack) == fpas_ENOMEM) goto yyoverflow; *++yyctx->stack.s_mark = (fpas_int_t) yyctx->state; *++yyctx->stack.l_mark = yyctx->val; goto yyloop; yyoverflow: fpas_error(ctx, yyctx->lval, "yacc stack overflow"); yyabort: fpas_freestack(&yyctx->stack); return fpas_RES_ABORT; yyaccept: fpas_freestack(&yyctx->stack); return fpas_RES_DONE; } FAWK_API int fawk_lex_fpas(fpas_STYPE *lval, fawk_ctx_t *ctx) { int chr, nchr; char tmp[128]; restart:; if (ctx->parser.in_eof) goto handle_eof; lex_textblk_exec(PT_STRING, goto restart, '\n'); ctx->parser.used = 0; if (isalpha(chr) || (chr == '_')) { append(chr, return -1); for(;;) { chr = getch(ctx); if (!(isalpha(chr)) && !(isdigit(chr)) && (chr != '_') && (chr != '$')) { ungetch(ctx, chr); break; } append(chr, return -1); if (chr == '$') { append('\0', return -1); goto got_id; } } append('\0', return -1); if (strcmp(ctx->parser.buff, "if") == 0) return PT_IF; else if (strcmp(ctx->parser.buff, "then") == 0) return PT_THEN; else if (strcmp(ctx->parser.buff, "else") == 0) return PT_ELSE; else if (strcmp(ctx->parser.buff, "exit") == 0) return PT_EXIT; else if (strcmp(ctx->parser.buff, "for") == 0) return PT_FOR; else if (strcmp(ctx->parser.buff, "down") == 0) return PT_DOWN; else if (strcmp(ctx->parser.buff, "to") == 0) return PT_TO; else if (strcmp(ctx->parser.buff, "while") == 0) return PT_WHILE; else if (strcmp(ctx->parser.buff, "do") == 0) return PT_DO; else if (strcmp(ctx->parser.buff, "repeat") == 0) return PT_REPEAT; else if (strcmp(ctx->parser.buff, "until") == 0) return PT_UNTIL; else if (strcmp(ctx->parser.buff, "function") == 0) return PT_FUNCTION; else if (strcmp(ctx->parser.buff, "begin") == 0) return PT_BEGIN; else if (strcmp(ctx->parser.buff, "end") == 0) return PT_END; else if (strcmp(ctx->parser.buff, "and") == 0) return PT_AND; else if (strcmp(ctx->parser.buff, "or") == 0) return PT_OR; else if (strcmp(ctx->parser.buff, "in") == 0) return PT_IN; else if (strcmp(ctx->parser.buff, "not") == 0) return PT_NOT; else if ((ctx->parser.curr_func != NULL) && (strcmp(ctx->parser.buff, ctx->parser.curr_func) == 0)) return PT_CURRFUNC; else if (strcmp(ctx->parser.buff, "include") == 0) { lex_include(goto restart); } else { got_id:; lval->str = fawk_strdup(ctx, ctx->parser.buff); return (lval->str == NULL) ? -1 : PT_ID; } } else if (chr == '{') { readtil(ctx, "}"); getch(ctx); goto restart; } else if (isdigit(chr)) { append(chr, return -1); if (chr == '0') { nchr = getch(ctx); if (nchr == 'x') { fawk_readup(ctx, "0123456789abcdefABCDEF"); lval->num = strtol(ctx->parser.buff, NULL, 16); return PT_NUM; } ungetch(ctx, nchr); } return read_numeric(ctx, &lval->num, 0, PT_NUM); } else switch(chr) { case '@': case '?': case ':': case ';': case '(': case ')': case '\\': case ']': case ',': case '+': case '-': case '*': case '/': case '%': case '=': return chr; case '[': { lex_textblk_start(goto restart); } case '.': nchr = getch(ctx); if (!isdigit(nchr)) { ungetch(ctx, nchr); return '.'; } else { append('.', return -1); append(nchr, return -1); return read_numeric(ctx, &lval->num, 1, PT_NUM); } break; case '<': nchr = getch(ctx); append(chr, return -1); switch(nchr) { case '=': return PT_LTEQ; case '>': return PT_NEQ; default: ungetch(ctx, nchr); return chr; } break; case_op_ch2('>', '=', PT_GTEQ); case '\'': if (read_strlit(ctx, '\'') == 0) return PT_NIL; lval->str = fawk_strdup(ctx, ctx->parser.buff); return (lval->str == NULL) ? -1 : PT_STRING; case EOF: { lex_got_eof(goto restart); } default: sprintf(tmp, "Invalid character on input: '%c'\n", chr); LIBFAWK_ERROR(ctx, tmp, ctx->parser.isp->fn, ctx->parser.isp->line+1, ctx->parser.isp->col+1, -1); } } fungw-1.2.0/libfungwbind/lua/0000755000175100017510000000000014047742763014311 5ustar svnsvnfungw-1.2.0/libfungwbind/lua/Plug.tmpasm0000644000175100017510000000052213110722271016420 0ustar svnsvnput /local/fungw/mod {fungw_lua} put /local/fungw/mod_dir {lua} switch ?libs/script/lua/presents case {true} put /local/fungw/mod_cflags libs/script/lua/cflags put /local/fungw/mod_ldflags libs/script/lua/ldflags put /local/fungw/mod_src {fungw_lua.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/lua/fungw_lua.pup0000644000175100017510000000012613757460755017032 0ustar svnsvn$desc lua binding engine $state works $script-ext lua .lua default buildin autoload 0 fungw-1.2.0/libfungwbind/lua/fungw_lua.c0000644000175100017510000001703413757344474016455 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2017, 2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include #include #include #define LUA_OBJ_NAME "__fgw_obj__" /* Extract the fgw object from a lua state (stored there as a global variable) */ static fgw_obj_t *fgws_lua_get_obj(lua_State *lst) { lua_getglobal(lst, LUA_OBJ_NAME); return lua_touserdata(lst, -1); } /* Convert fgw type to lua type and push */ static void fgw_lua_push(fgw_ctx_t *fctx, lua_State *lst, fgw_arg_t *arg) { # define FGW_LUA_PUSH_LONG(lst, val) lua_pushinteger(lst, val); return; # define FGW_LUA_PUSH_DOUBLE(lst, val) lua_pushnumber(lst, val); return; # define FGW_LUA_PUSH_PTR(lst, val) lua_pushlightuserdata(lst, val); return; # define FGW_LUA_PUSH_STR(lst, val) lua_pushstring(lst, val); return; # define FGW_LUA_PUSH_NIL(lst, val) lua_pushnil(lst); return; if (FGW_IS_TYPE_CUSTOM(arg->type)) fgw_arg_conv(fctx, arg, FGW_AUTO); switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(lst, FGW_LUA_PUSH_LONG); ARG_CONV_CASE_LLONG(lst, FGW_LUA_PUSH_DOUBLE); ARG_CONV_CASE_DOUBLE(lst, FGW_LUA_PUSH_DOUBLE); ARG_CONV_CASE_LDOUBLE(lst, FGW_LUA_PUSH_DOUBLE); ARG_CONV_CASE_PTR(lst, FGW_LUA_PUSH_PTR); ARG_CONV_CASE_STR(lst, FGW_LUA_PUSH_STR); ARG_CONV_CASE_CLASS(lst, FGW_LUA_PUSH_NIL); ARG_CONV_CASE_INVALID(lst, FGW_LUA_PUSH_NIL); } if (arg->type & FGW_PTR) { FGW_LUA_PUSH_PTR(lst, arg->val.ptr_void); } else { FGW_LUA_PUSH_NIL(lst, 0); } } /* Read the lua stack and convert the result to an fgw arg */ static void fgw_lua_toarg(lua_State *lst, fgw_arg_t *dst, int n) { switch(lua_type(lst, n)) { case LUA_TNUMBER: dst->type = FGW_DOUBLE; dst->val.nat_double = lua_tonumber(lst, n); break; case LUA_TBOOLEAN: dst->type = FGW_INT; dst->val.nat_int = !!lua_toboolean(lst, n); break; case LUA_TSTRING: dst->type = FGW_STR | FGW_DYN; dst->val.str = fgw_strdup(lua_tostring(lst, n)); break; case LUA_TLIGHTUSERDATA: dst->type = FGW_PTR; dst->val.ptr_void = lua_touserdata(lst, n); break; case LUA_TNIL: default: dst->type = FGW_PTR; dst->val.ptr_void = NULL; break; } } /* API: the script is calling an fgw function */ static int fgws_lua_call_fgw(lua_State *lst) { fgw_obj_t *obj; int argc, n; fgw_arg_t res, *argv, argv_static[16]; fgw_func_t *func; fgw_error_t err; lua_Debug ar; /* Get the current function-name */ lua_getstack(lst, 0, &ar); lua_getinfo(lst, "n", &ar); obj = fgws_lua_get_obj(lst); func = fgw_func_lookup(obj->parent, ar.name); if (func == NULL) return 0; argc = lua_gettop(lst); if ((argc + 1) > (sizeof(argv_static) / sizeof(argv_static[0]))) argv = malloc((argc + 1) * sizeof(fgw_arg_t)); else argv = argv_static; argv[0].type = FGW_FUNC; argv[0].val.argv0.func = func; argv[0].val.argv0.user_call_ctx = obj->script_user_call_ctx; for (n = 1; n < argc; n++) fgw_lua_toarg(lst, &argv[n], n); /* Run command */ res.type = FGW_PTR; res.val.ptr_void = NULL; err = func->func(&res, argc, argv); /* Free the array */ fgw_argv_free(obj->parent, argc, argv); if (argv != argv_static) free(argv); if (err != 0) return 0; fgw_lua_push(obj->parent, lst, &res); if (res.type & FGW_DYN) free(res.val.ptr_void); return 1; } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_lua_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { lua_State *lst = obj->script_data; lua_register(lst, name, fgws_lua_call_fgw); } /* API: fgw calls a lua function */ static fgw_error_t fgws_lua_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; lua_State *lst = obj->script_data; int i; lua_getglobal(lst, argv[0].val.func->name); for (i = 1; i < argc; i++) fgw_lua_push(obj->parent, lst, &argv[i]); fgws_ucc_save(obj); lua_call(lst, argc-1, 1); fgws_ucc_restore(obj); fgw_lua_toarg(lst, res, 1); lua_pop(lst, 1); return FGW_SUCCESS; } /* API: unload the script */ static int fgws_lua_unload(fgw_obj_t *obj) { if (obj->script_data != NULL) lua_close(obj->script_data); obj->script_data = NULL; return 0; } /* Helper function for the script to register its functions */ static int fgws_lua_freg(lua_State *lst) { fgw_obj_t *obj; const char *name; int argc; fgw_func_t *func; obj = fgws_lua_get_obj(lst); argc = lua_gettop(lst); if (argc != 2) { fgw_async_error(obj, "fgw_func_reg: wrong number of arguments: need 2\n"); return 0; } name = lua_tostring(lst, 1); if (name == NULL) { fgw_async_error(obj, "fgw_func_reg: empty name\n"); return 0; } func = fgw_func_reg(obj, name, fgws_lua_call_script); if (func == NULL) { fgw_async_error(obj, "fgw_func_reg: failed to register function\n"); fgw_async_error(obj, name); fgw_async_error(obj, "\n"); return 0; } return 1; } /* API: init the interpreter so that functions can be registered */ static int fgws_lua_init(fgw_obj_t *obj, const char *filename, const char *opts) { lua_State *lst; /* initialize the interpreter */ obj->script_data = lst = luaL_newstate(); if (lst == NULL) { fgw_async_error(obj, "fgws_lua_init: failed to set up the interpreter\n"); return -1; } /* Load default libs */ #if LUA_VERSION_NUM < 500 luaopen_base(lst); luaopen_io(lst); luaopen_string(lst); luaopen_math(lst); #else luaL_openlibs(lst); #endif /* add the lua->fgw glue */ lua_register(lst, "fgw_func_reg", fgws_lua_freg); lua_pushlightuserdata(lst, obj); lua_setglobal(lst, LUA_OBJ_NAME); return 0; } /* API: load a script into an object */ static int fgws_lua_load(fgw_obj_t *obj, const char *filename, const char *opts) { lua_State *lst = obj->script_data; int res; /* Load the file */ res = luaL_loadfile(lst, filename); if (res != 0) { fgw_async_error(obj, "fgws_lua_load: failed to load the script\n"); lua_close(obj->script_data); obj->script_data = NULL; return -1; } /* Run the main part */ if (lua_pcall(lst, 0, 0, 0) != 0) { fgw_async_error(obj, "fgws_lua_load: failed to execute the script\n"); lua_close(obj->script_data); obj->script_data = NULL; return -2; } return 0; } static int fgws_lua_test_parse(const char *filename, FILE *f) { const char *exts[] = {".lua", NULL }; return fgw_test_parse_fn(filename, exts); } /* API: engine registration */ static const fgw_eng_t fgw_lua_eng = { "lua", fgws_lua_call_script, fgws_lua_init, fgws_lua_load, fgws_lua_unload, fgws_lua_reg_func, NULL, fgws_lua_test_parse, ".lua" }; int pplg_check_ver_fungw_lua(int version_we_need) { return 0; } int pplg_init_fungw_lua(void) { fgw_eng_reg(&fgw_lua_eng); return 0; } void pplg_uninit_fungw_lua(void) { fgw_eng_unreg(fgw_lua_eng.name); } fungw-1.2.0/libfungwbind/scconfig_hooks.h0000644000175100017510000000703114047742662016676 0ustar svnsvn/* fungw - function gateway Copyright (C) 2017 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* These functions can be called from an scconfig hooks.c to get libfungw bindings configured without having to have or compile or run a separate scconfig process for the lib. Assumes libfungw/scconfig_hhoks.h functions has been already called. */ #include #include #include "arg.h" #include "log.h" #include "dep.h" #include "db.h" #include "tmpasm_scconfig.h" static int fungwbind_hook_postinit(void) { db_mkdir("/local"); put("/local/fungw/bindings_all", "c fawk lua mawk tcl picol python python3 mruby perl duktape mujs estutter funlisp cli"); /* TODO: welltype should be on the above list */ put("/local/fungw/bindings", ""); return 0; } static int fungwbind_hook_detect_target(void) { int static_disable_python = 0; put("libs/script/fawk/presents", strue); put("libs/script/c/presents", strue); require("libs/script/lua/*", 0, 0); require("libs/script/mawk/*", 0, 0); require("libs/script/tcl/*", 0, 0); if (require("libs/script/python/*", 0, 0) == 0) static_disable_python = 3; if (require("libs/script/python3/*", 0, 0) == 0) static_disable_python = 2; require("libs/script/mruby/*", 0, 0); require("libs/script/perl/*", 0, 0); require("libs/script/duktape/*", 0, 0); require("libs/script/mujs/*", 0, 0); require("libs/script/estutter/*", 0, 0); require("libs/script/funlisp/*", 0, 0); require("libs/script/picol/*", 0, 0); #if 0 if (require("libs/script/welltype/wtc/*", 0, 0) == 0) require("libs/script/welltype/*", 0, 0); #endif /* for cli */ require("libs/proc/fork/*", 0, 0); require("libs/io/pipe/*", 0, 0); if (static_disable_python != 0) { char path[64]; if (static_disable_python == 2) strcpy(path, "/local/fungw/binding/static-disable/python"); else sprintf(path, "/local/fungw/binding/static-disable/python%d", static_disable_python); put(path, strue); } return 0; } #define gen_lang(langdir) \ fprintf(stderr, "Generating libfungwbind/" langdir "/Makefile (%d)\n", generr |= tmpasm(root, langdir "/Plug.tmpasm", langdir "/Makefile")); /* Root is the path for the fungwlug lib dir (trunk/libfungwbind) */ static int fungwbind_hook_generate(const char *root) { int generr = 0; gen_lang("c"); gen_lang("fawk"); gen_lang("lua"); gen_lang("mawk"); gen_lang("tcl"); gen_lang("picol"); gen_lang("python"); gen_lang("python3"); gen_lang("duktape"); gen_lang("mujs"); gen_lang("mruby"); gen_lang("perl"); gen_lang("estutter"); gen_lang("funlisp"); #if 0 gen_lang("welltype"); #endif gen_lang("cli"); fprintf(stderr, "Generating libfungwbind/Makefile (%d)\n", generr |= tmpasm(root, "Makefile.in", "Makefile")); fprintf(stderr, "Generating libfungwbind/libfungwbind.mak (%d)\n", generr |= tmpasm(root, "libfungwbind.mak.in", "libfungwbind.mak")); return generr; } fungw-1.2.0/libfungwbind/python/0000755000175100017510000000000014047742763015051 5ustar svnsvnfungw-1.2.0/libfungwbind/python/Plug.tmpasm0000644000175100017510000000054413110722271017164 0ustar svnsvnput /local/fungw/mod {fungw_python} put /local/fungw/mod_dir {python} switch ?libs/script/python/presents case {true} put /local/fungw/mod_cflags libs/script/python/cflags put /local/fungw/mod_ldflags libs/script/python/ldflags put /local/fungw/mod_src {fungw_python.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/python/fungw_python.pup0000644000175100017510000000020713757460755020332 0ustar svnsvn$desc python binding engine $state works $script-ext python .py $script-ext python .python default buildin autoload 0 conflict python3 fungw-1.2.0/libfungwbind/python/fungw_python.c0000644000175100017510000003027113757344474017753 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2018, 2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include /* Python redefines this blindly */ #undef _POSIX_C_SOURCE #include typedef struct { PyObject *root_module, *root_dict; PyObject *module, *dict; char modname[64]; } py_ctx_t; typedef struct { char *name; fgw_obj_t *obj; py_ctx_t *ctx; } py_func_t; static PyMethodDef python_empty_method[] = { {NULL, NULL, 0, NULL} }; static void fgws_python_obj2arg(fgw_arg_t *dst, PyObject *src) { PyTypeObject *type = Py_TYPE(src); if (type == &PyString_Type) { dst->type = FGW_STR | FGW_DYN; dst->val.str = fgw_strdup(PyString_AsString(PyObject_Str(src))); } else if (type == &PyFloat_Type) { dst->type = FGW_DOUBLE; dst->val.nat_double = PyFloat_AsDouble(src); } else if (type == &PyInt_Type) { dst->type = FGW_INT; dst->val.nat_int = PyInt_AS_LONG(src); } else if (type == &PyBool_Type) { dst->type = FGW_INT; dst->val.nat_int = PyBool_Check(src) ? 1 : 0; } else if (type == &PyLong_Type) { dst->type = FGW_LONG; dst->val.nat_long = PyLong_AsLong(src); } else { fprintf(stderr, "fgws_python_obj2arg: ignoring unknown type %s\n", type->tp_name); dst->type = FGW_PTR; dst->val.ptr_void = NULL; } } static const char *fgws_python_print_ptr(void *ptr) { static char buf[64]; sprintf(buf, "%p", ptr); return buf; } PyObject *fgws_python_arg2obj(fgw_ctx_t *fctx, fgw_arg_t *arg) { # define FGW_PY_SET_LONG(lst, val) return PyLong_FromLong(val); # define FGW_PY_SET_DOUBLE(lst, val) return PyFloat_FromDouble(val); # define FGW_PY_SET_PTR(lst, val) return PyString_FromString(fgws_python_print_ptr(val)); # define FGW_PY_SET_STR(lst, val) return PyString_FromString(val); # define FGW_PY_SET_NIL(lst, val) return Py_False; if (FGW_IS_TYPE_CUSTOM(arg->type)) fgw_arg_conv(fctx, arg, FGW_AUTO); /* if fails, it remains custom and will be unhandled */ switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(NULL, FGW_PY_SET_LONG); ARG_CONV_CASE_LLONG(NULL, FGW_PY_SET_DOUBLE); ARG_CONV_CASE_DOUBLE(NULL, FGW_PY_SET_DOUBLE); ARG_CONV_CASE_LDOUBLE(NULL, FGW_PY_SET_DOUBLE); ARG_CONV_CASE_PTR(lst, FGW_PY_SET_PTR); ARG_CONV_CASE_STR(NULL, FGW_PY_SET_STR); ARG_CONV_CASE_CLASS(lst, FGW_PY_SET_NIL); ARG_CONV_CASE_INVALID(lst, FGW_PY_SET_NIL); } return Py_None; } /* API: the script is calling an fgw function */ static PyObject *fgws_python_call_fgw(PyObject *self, PyObject *args) { py_func_t *func_ctx = (py_func_t *)self; fgw_func_t *func; int argc, n; fgw_arg_t res, *argv, argv_static[16]; fgw_error_t err; PyObject *py_res; func = fgw_func_lookup(func_ctx->obj->parent, func_ctx->name); if (func == NULL) { fgw_async_error(func_ctx->obj, "fgws_python_call_fgw: function to be called is not found:"); fgw_async_error(func_ctx->obj, func_ctx->name); fgw_async_error(func_ctx->obj, "\n"); return Py_None; } argc = PyTuple_Size(args); if ((argc + 1) > (sizeof(argv_static) / sizeof(argv_static[0]))) argv = malloc((argc + 1) * sizeof(fgw_arg_t)); else argv = argv_static; /* Set the first param */ argv[0].type = FGW_FUNC; argv[0].val.argv0.func = func; argv[0].val.argv0.user_call_ctx = func_ctx->obj->script_user_call_ctx; /* Convert all params to fgw args */ for (n = 0; n < argc; n++) fgws_python_obj2arg(&argv[n+1], PyTuple_GetItem(args, n)); /* Call the target function */ res.type = FGW_PTR; res.val.ptr_void = NULL; err = func->func(&res, argc+1, argv); /* Free the array */ fgw_argv_free(func_ctx->obj->parent, argc, argv); if (argv != argv_static) free(argv); if (err != 0) return 0; py_res = fgws_python_arg2obj(func->obj->parent, &res); if (res.type & FGW_DYN) free(res.val.ptr_void); return py_res; } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_python_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { py_ctx_t *ctx = obj->script_data; py_func_t *func_ctx; PyObject *new; PyMethodDef *new_func_define; char *new_name = fgw_strdup(name); PyMethodDef met[] = { {NULL, fgws_python_call_fgw, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; met->ml_name = new_name; new_func_define = malloc(sizeof(met)); memcpy(new_func_define, met, sizeof(met)); new = PyCFunction_New(new_func_define, NULL); /* Misuse m_self to assing a 'self' to the function. Easy access to function name later. */ func_ctx = malloc(sizeof(py_func_t)); func_ctx->name = new_name; func_ctx->obj = obj; func_ctx->ctx = ctx; ((PyCFunctionObject *)new)->m_self = (PyObject *)func_ctx; if ((new == NULL) || PyDict_SetItemString(ctx->root_dict, new_name, new)) { fgw_async_error(obj, "fgws_python_reg_func: failed to register function:"); fgw_async_error(obj, new_name); fgw_async_error(obj, "\n"); if (PyErr_Occurred()) PyErr_Print(); free(new_name); free(func_ctx); return; } Py_DECREF(new); } /* API: fgw calls a python function */ static fgw_error_t fgws_python_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; py_ctx_t *ctx = obj->script_data; PyObject *pfunc, *pargs, *pv, *pret; const char *func_name = argv[0].val.func->name; int n; pfunc = PyDict_GetItemString(ctx->dict, func_name); if ((pfunc == NULL) || !PyCallable_Check(pfunc)) { fgw_async_error(obj, "Not a callable python object:"); fgw_async_error(obj, func_name); fgw_async_error(obj, "\n"); if (PyErr_Occurred()) PyErr_Print(); return FGW_ERR_NOT_FOUND; } /* Create the args-list */ pargs = PyTuple_New(argc-1); for (n = 1; n < argc; n++ ) { pv = fgws_python_arg2obj(obj->parent, &argv[n]); PyTuple_SetItem(pargs, n-1, pv); Py_DECREF(pv); } fgws_ucc_save(obj); pret = PyObject_CallObject(pfunc, pargs); fgws_ucc_restore(obj); if (pret != NULL) fgws_python_obj2arg(res, pret); else res->type = FGW_INVALID; Py_DECREF(pargs); if (pret != NULL) { Py_DECREF(pret); } if (PyErr_Occurred()) PyErr_Print(); #warning TODO: return value } static int py_global_inited = 0; /* API: unload the script */ static int fgws_python_unload(fgw_obj_t *obj) { py_ctx_t *ctx = obj->script_data; /* Free all registrations */ { PyCFunctionObject *func; PyMethodDef *ml; PyDictObject *mp; PyDictEntry *ep, *table; int fill; py_func_t *func_ctx; mp = (PyDictObject *)(ctx->root_dict); table = mp->ma_table; fill = mp->ma_fill; /* Go through all dict-items */ for (ep = table; fill > 0; ep++) { if (ep->me_key == NULL) continue; fill--; /* Is it a valid function */ if (!PyCallable_Check(ep->me_value)) continue; func = (PyCFunctionObject *)(ep->me_value); ml = func->m_ml; if ((ml == NULL) || (func->m_self == NULL) || (ml->ml_meth != fgws_python_call_fgw)) continue; /* not registered here */ func_ctx = (py_func_t *)func->m_self; func->m_self = NULL; PyDict_DelItemString(ctx->root_dict, func_ctx->name); free(func_ctx->name); free(func_ctx); } } /* free global objects */ if (ctx->module != NULL) { /* ... but python2.7 sometimes crashes if the root module is dereferenced */ #if 0 Py_DECREF(ctx->module); Py_DECREF(ctx->root_module); #endif } free(ctx); py_global_inited--; if (py_global_inited == 0) Py_Finalize(); return 0; } /* Helper function for the script to register its functions */ static PyObject *fgws_python_freg(PyObject *self, PyObject *args) { py_func_t *func_ctx = (py_func_t *)self; fgw_obj_t *obj = func_ctx->obj; PyObject *pfunc; const char *name; int argc; fgw_func_t *func; argc = PyTuple_Size(args); if (argc != 1) { fgw_async_error(obj, "fgw_func_reg: wrong number of arguments: need 1\n"); return 0; } name = PyString_AsString(PyObject_Str(PyTuple_GetItem(args, 0))); if (name == NULL) { fgw_async_error(obj, "fgw_func_reg: empty name\n"); return 0; } pfunc = PyDict_GetItemString(func_ctx->ctx->root_dict, name); if (pfunc == NULL) { fgw_async_error(obj, "fgw_func_reg: function doesn't exist:"); fgw_async_error(obj, name); fgw_async_error(obj, "\n"); if (PyErr_Occurred()) PyErr_Print(); return 0; } func = fgw_func_reg(obj, name, fgws_python_call_script); if (func == NULL) { fgw_async_error(obj, "fgw_python_func_reg: failed to register function: "); fgw_async_error(obj, name); fgw_async_error(obj, "\n"); return 0; } return Py_True; } /* API: init the interpreter so that functions can be registered */ static int fgws_python_init(fgw_obj_t *obj, const char *filename, const char *opts) { py_ctx_t *ctx; static long py_global_cnt = 0; ctx = calloc(sizeof(py_ctx_t), 1); if (ctx == NULL) { fgw_async_error(obj, "fgws_python_init: failed to allocate context\n"); return -1; } obj->script_data =ctx; if (py_global_inited == 0) { Py_Initialize(); PyRun_SimpleString("import sys"); } sprintf(ctx->modname, "_fungw_%ld_", py_global_cnt++); ctx->root_module = Py_InitModule(ctx->modname, python_empty_method); ctx->root_dict = PyModule_GetDict(ctx->root_module); py_global_inited++; /* add the python->fgw glue */ { py_ctx_t *ctx = obj->script_data; py_func_t *func_ctx; PyObject *new; PyMethodDef *new_func_define; char *new_name = fgw_strdup("fgw_func_reg"); PyMethodDef met[] = { {"fgw_func_reg", fgws_python_freg, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; met->ml_name = new_name; new_func_define = malloc(sizeof(met)); memcpy(new_func_define, met, sizeof(met)); new = PyCFunction_New(new_func_define, NULL); /* Misuse m_self to assing a 'self' to the function. Easy access to function name later. */ func_ctx = malloc(sizeof(py_func_t)); func_ctx->name = new_name; func_ctx->obj = obj; func_ctx->ctx = ctx; ((PyCFunctionObject *)new)->m_self = (PyObject *)func_ctx; if ((new == NULL) || PyDict_SetItemString(ctx->root_dict, new_name, new)) { fgw_async_error(obj, "fgws_python_init: failed to register function: fgw_func_reg\n"); if (PyErr_Occurred()) PyErr_Print(); free(new_name); free(func_ctx); return -1; } Py_DECREF(new); } return 0; } /* API: load a script into an object */ static int fgws_python_load(fgw_obj_t *obj, const char *filename, const char *opts) { py_ctx_t *ctx = obj->script_data; PyObject *modules = PyImport_GetModuleDict(); PyObject *imp = PyDict_GetItemString(modules, "imp"); if (imp == NULL) { imp = PyImport_ImportModule("imp"); if (imp == NULL) abort(); } else { Py_INCREF(imp); } ctx->module = PyObject_CallMethod(imp, "load_source", "ss", ctx->modname, filename); Py_INCREF(imp); if (ctx->module == NULL) { fgw_async_error(obj, "Failed to load python script:"); fgw_async_error(obj, filename); fgw_async_error(obj, "\n"); if (PyErr_Occurred()) PyErr_Print(); return -1; } ctx->dict = PyModule_GetDict(ctx->module); return 0; } static int fgws_python_test_parse(const char *filename, FILE *f) { const char *exts[] = {".py", ".python", NULL }; return fgw_test_parse_fn(filename, exts); } /* API: engine registration */ static const fgw_eng_t fgw_python_eng = { "python", fgws_python_call_script, fgws_python_init, fgws_python_load, fgws_python_unload, fgws_python_reg_func, NULL, fgws_python_test_parse, ".py" }; int pplg_check_ver_fungw_python(int version_we_need) { return 0; } int pplg_init_fungw_python(void) { fgw_eng_reg(&fgw_python_eng); return 0; } void pplg_uninit_fungw_python(void) { fgw_eng_unreg(fgw_python_eng.name); } fungw-1.2.0/libfungwbind/perl/0000755000175100017510000000000014047742763014472 5ustar svnsvnfungw-1.2.0/libfungwbind/perl/Plug.tmpasm0000644000175100017510000000052713300234127016605 0ustar svnsvnput /local/fungw/mod {fungw_perl} put /local/fungw/mod_dir {perl} switch ?libs/script/perl/presents case {true} put /local/fungw/mod_cflags libs/script/perl/cflags put /local/fungw/mod_ldflags libs/script/perl/ldflags put /local/fungw/mod_src {fungw_perl.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/perl/fungw_perl.c0000644000175100017510000002137413757344474017021 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2018, 2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include #include #include #include /* Perl doesn't offer an official user data pointer in the interpereter struct, but in return the init function can't create variables or in the interpreter before parsing a script; this workaround abuses an unused field (as of perl 5.26.2). */ #define perl_user_data IXpv typedef struct { PerlInterpreter *interp; int freg_delay; long fregs_used, fregs_alloced; char **fregs; fgw_obj_t *obj; } perl_ctx_t; static void fgws_perl_sv2arg(fgw_arg_t *dst, SV *src) { if (SvIOK(src)) { dst->type = FGW_INT; dst->val.nat_int = SvIV(src); } else if (SvNOK(src)) { dst->type = FGW_DOUBLE; dst->val.nat_double = SvNV(src); } else if (SvPOK(src)) { dst->type = FGW_STR | FGW_DYN; dst->val.str = fgw_strdup(SvPV_nolen(src)); } else { dst->type = FGW_PTR; dst->val.ptr_void = NULL; fprintf(stderr, "fgws_perl_sv2arg: ignoring unknown type\n"); } } static SV *fgws_perl_arg2sv(fgw_ctx_t *fctx, perl_ctx_t *ctx, fgw_arg_t *arg) { # define FGW_PUSH_LONG(lst, val) return newSViv(val); # define FGW_PUSH_DOUBLE(lst, val) return newSVnv(val); # define FGW_PUSH_PTR(lst, val) return newSViv((long)val); # define FGW_PUSH_STR(lst, val) return newSVpv(val, 0); # define FGW_PUSH_NIL(lst, val) return newSVpv("", 0); if (FGW_IS_TYPE_CUSTOM(arg->type)) fgw_arg_conv(fctx, arg, FGW_AUTO); /* if fails, it remains custom and will be unhandled */ switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(NULL, FGW_PUSH_LONG); ARG_CONV_CASE_LLONG(NULL, FGW_PUSH_DOUBLE); ARG_CONV_CASE_DOUBLE(NULL, FGW_PUSH_DOUBLE); ARG_CONV_CASE_LDOUBLE(NULL, FGW_PUSH_DOUBLE); ARG_CONV_CASE_PTR(NULL, FGW_PUSH_PTR); ARG_CONV_CASE_STR(NULL, FGW_PUSH_STR); ARG_CONV_CASE_CLASS(NULL, FGW_PUSH_NIL); ARG_CONV_CASE_INVALID(NULL, FGW_PUSH_NIL); } return newSVpv("", 0); } /* API: the script is calling an fgw function */ static XS(fgws_perl_call_fgw) { perl_ctx_t *ctx = (perl_ctx_t *)my_perl->perl_user_data; fgw_func_t *func; GV *gv; SV *func_name_sv; const char *func_name; int argc, n; fgw_arg_t res, *argv, argv_static[16]; fgw_error_t err; dXSARGS; SP -= items; /* Figure the function name */ gv = CvGV(cv); func_name_sv = sv_newmortal(); gv_efullname3(func_name_sv, gv, Nullch); func_name = SvPV_nolen(func_name_sv) + 6; /* +6 is for truncating the "main::" prefix */ /* Set up args */ argc = items; if ((argc + 1) > (sizeof(argv_static) / sizeof(argv_static[0]))) argv = malloc((argc + 1) * sizeof(fgw_arg_t)); else argv = argv_static; /* Set the first param */ func = fgw_func_lookup(ctx->obj->parent, func_name); if (func == NULL) { fgw_async_error(ctx->obj, "fgws_perl_call_fgw: function to be called is not found:"); fgw_async_error(ctx->obj, func_name); fgw_async_error(ctx->obj, "\n"); return XSRETURN_EMPTY; } argv[0].type = FGW_FUNC; argv[0].val.argv0.func = func; argv[0].val.argv0.user_call_ctx = ctx->obj->script_user_call_ctx; /* Convert all params to fgw args */ for(n = 0; n < argc; n++) fgws_perl_sv2arg(&argv[n+1], ST(n)); /* Call the target function */ res.type = FGW_PTR; res.val.ptr_void = NULL; err = func->func(&res, argc+1, argv); /* Free the array */ fgw_argv_free(ctx->obj->parent, argc+1, argv); if (argv != argv_static) free(argv); if (err != 0) return XSRETURN_EMPTY; ST(0) = fgws_perl_arg2sv(func->obj->parent, ctx, &res); sv_2mortal(ST(0)); if (res.type & FGW_DYN) free(res.val.ptr_void); return XSRETURN(1); } /* Helper function for the script to register its functions */ static void fgws_perl_freg(void *client_data, const char *name) { perl_ctx_t *ctx = client_data; if (ctx->freg_delay) { /* still loading the script - delay command registrations */ if (ctx->fregs_used >= ctx->fregs_alloced) { ctx->fregs_alloced += 32; ctx->fregs = realloc(ctx->fregs, sizeof(char *) * ctx->fregs_alloced); } ctx->fregs[ctx->fregs_used] = fgw_strdup(name); ctx->fregs_used++; } else { PERL_SET_CONTEXT(ctx->interp); newXS((char *)name, fgws_perl_call_fgw, __FILE__); } } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_perl_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { fgws_perl_freg(obj->script_data, name); } /* API: fgw calls a perl function */ static fgw_error_t fgws_perl_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; perl_ctx_t *ctx = obj->script_data; const char *func_name = argv[0].val.func->name; I32 ret; int n; dSP; PERL_SET_CONTEXT(ctx->interp); ENTER; SAVETMPS; PUSHMARK(SP); EXTEND(SP, argc-1); for(n = 1; n < argc; n++) PUSHs(sv_2mortal(fgws_perl_arg2sv(obj->parent, ctx, &argv[n]))); PUTBACK; fgws_ucc_save(obj); ret = call_pv(func_name, G_SCALAR); fgws_ucc_restore(obj); SPAGAIN; if (ret != 1) { res->type = FGW_PTR; res->val.ptr_void = NULL; return FGW_ERR_UNKNOWN; } fgws_perl_sv2arg(res, POPs); PUTBACK; FREETMPS; LEAVE; return 0; } /* API: unload the script */ static int fgws_perl_unload(fgw_obj_t *obj) { perl_ctx_t *ctx = obj->script_data; if (ctx->interp != NULL) { PERL_SET_CONTEXT(ctx->interp); perl_destruct(ctx->interp); perl_free(ctx->interp); } free(ctx); return 0; } /* fgw_func_reg() when called from the perl script - register a script function in fungw */ static XS(fgws_perl_func_reg) { dXSARGS; const char *name; perl_ctx_t *ctx = (perl_ctx_t *)my_perl->perl_user_data; fgw_func_t *func; SP -= items; name = SvPV_nolen(ST(0)); func = fgw_func_reg(ctx->obj, name, fgws_perl_call_script); if (func == NULL) { fgw_async_error(ctx->obj, "fgw_perl_func_reg: failed to register function: "); fgw_async_error(ctx->obj, name); fgw_async_error(ctx->obj, "\n"); ST(0) = newSVpvs("0"); } else ST(0) = newSVpvs("1"); sv_2mortal(ST(0)); return XSRETURN(1); } extern void boot_DynaLoader(pTHX_ CV* cv); static void xs_init(pTHX) { char *file = __FILE__; dXSUB_SYS; newXS("fgw_func_reg", fgws_perl_func_reg, __FILE__); newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); } /* API: init the interpreter so that functions can be registered */ static int fgws_perl_init(fgw_obj_t *obj, const char *filename, const char *opts) { perl_ctx_t *ctx = calloc(sizeof(perl_ctx_t), 1); /* Start up a perl interpreter */ ctx->interp = perl_alloc(); if (ctx->interp == NULL) { free(ctx); return -1; } PERL_SET_CONTEXT(ctx->interp); perl_construct(ctx->interp); obj->script_data = ctx; ctx->interp->perl_user_data = ctx; ctx->obj = obj; ctx->freg_delay = 1; return 0; } /* API: load a script into an object */ static int fgws_perl_load(fgw_obj_t *obj, const char *filename, const char *opts) { perl_ctx_t *ctx = obj->script_data; char *tmp[2]; long n; tmp[0] = NULL; tmp[1] = fgw_strdup(filename); /* Load the script */ PERL_SET_CONTEXT(ctx->interp); perl_parse(ctx->interp, xs_init, 2, tmp, NULL); /* execute delayed command registrations */ for(n = 0; n < ctx->fregs_used; n++) { newXS(ctx->fregs[n], fgws_perl_call_fgw, __FILE__); free(ctx->fregs[n]); } free(ctx->fregs); ctx->fregs = NULL; ctx->fregs_used = ctx->fregs_alloced = 0; ctx->freg_delay = 0; perl_run(ctx->interp); free(tmp[1]); return 0; } static int fgws_perl_test_parse(const char *filename, FILE *f) { const char *exts[] = {".pl", ".perl", NULL }; return fgw_test_parse_fn(filename, exts); } /* API: engine registration */ static const fgw_eng_t fgw_perl_eng = { "perl", fgws_perl_call_script, fgws_perl_init, fgws_perl_load, fgws_perl_unload, fgws_perl_reg_func, NULL, fgws_perl_test_parse, ".pl" }; int pplg_check_ver_fungw_perl(int version_we_need) { return 0; } int pplg_init_fungw_perl(void) { fgw_eng_reg(&fgw_perl_eng); return 0; } void pplg_uninit_fungw_perl(void) { fgw_eng_unreg(fgw_perl_eng.name); } fungw-1.2.0/libfungwbind/perl/fungw_perl.pup0000644000175100017510000000015613757460755017377 0ustar svnsvn$desc perl binding engine $state works $script-ext perl .pl $script-ext perl .perl default buildin autoload 0 fungw-1.2.0/libfungwbind/mujs/0000755000175100017510000000000014047742763014506 5ustar svnsvnfungw-1.2.0/libfungwbind/mujs/Plug.tmpasm0000644000175100017510000000052514036737110016626 0ustar svnsvnput /local/fungw/mod {fungw_mujs} put /local/fungw/mod_dir {mujs} switch ?libs/script/mujs/presents case {true} put /local/fungw/mod_cflags libs/script/mujs/cflags put /local/fungw/mod_ldflags libs/script/mujs/ldflags put /local/fungw/mod_src {fungw_mujs.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/mujs/fungw_mujs.pup0000644000175100017510000000022214036737110017400 0ustar svnsvn$desc mujs/javascript binding engine $state works $script-ext mujs .js $lang-alias mujs javascript $lang-alias mujs js default buildin autoload 0 fungw-1.2.0/libfungwbind/mujs/fungw_mujs.c0000644000175100017510000002007614036751405017032 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2021 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include #include #define FGW_FUNC_PROP "__fungw_function_name__" static void fgws_mujs_print(js_State *ctx) { int n, argc = js_gettop(ctx); for(n = 1; n < argc; n++) { if (n > 1) putchar(' '); printf("%s", js_tostring(ctx, n)); } putchar('\n'); } static void fgws_mujs_push_arg(fgw_ctx_t *fctx, js_State *ctx, fgw_arg_t *arg) { char tmp[128]; # define FGW_mujs_PUSH_DOUBLE(lst, val) js_pushnumber(ctx, val); return; # define FGW_mujs_PUSH_STR(lst, val) js_pushstring(ctx, val); return; # define FGW_mujs_PUSH_NIL(lst, val) js_pushnull(ctx); return; # define FGW_mujs_PUSH_PTR(lst, val) sprintf(tmp, "%p", val); js_pushstring(ctx, tmp); return; if (FGW_IS_TYPE_CUSTOM(arg->type)) fgw_arg_conv(fctx, arg, FGW_AUTO); /* if fails, it remains custom and will be unhandled */ switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(NULL, FGW_mujs_PUSH_DOUBLE); ARG_CONV_CASE_LLONG(NULL, FGW_mujs_PUSH_DOUBLE); ARG_CONV_CASE_DOUBLE(NULL, FGW_mujs_PUSH_DOUBLE); ARG_CONV_CASE_LDOUBLE(NULL, FGW_mujs_PUSH_DOUBLE); ARG_CONV_CASE_PTR(NULL, FGW_mujs_PUSH_PTR); ARG_CONV_CASE_STR(NULL, FGW_mujs_PUSH_STR); ARG_CONV_CASE_CLASS(NULL, FGW_mujs_PUSH_NIL); ARG_CONV_CASE_INVALID(NULL, FGW_mujs_PUSH_NIL); } js_pushnull(ctx); } static void fgws_mujs_js2arg(js_State *ctx, fgw_arg_t *dst, int src_idx) { int type = js_type(ctx, src_idx); switch(type) { case JS_ISBOOLEAN: dst->type = FGW_INT; dst->val.nat_int = js_toboolean(ctx, src_idx); break; case JS_ISNUMBER: dst->type = FGW_DOUBLE; dst->val.nat_double = js_tonumber(ctx, src_idx); break; case JS_ISSTRING: dst->type = FGW_STR | FGW_DYN; dst->val.str = fgw_strdup(js_tostring(ctx, src_idx)); break; case JS_ISNULL: dst->type = FGW_PTR; dst->val.ptr_void = NULL; break; case JS_ISUNDEFINED: case JS_ISFUNCTION: case JS_ISOBJECT: /* can't convert these */ fprintf(stderr, "fgws_mujs_js2arg: ignoring unconvertable type %d\n", type); } } /* API: the script is calling an fgw function */ static void fgws_mujs_call_fgw(js_State *ctx) { fgw_func_t *f; int n, argc; fgw_arg_t res, *argv, argv_static[16]; fgw_error_t err; fgw_obj_t *obj = js_getcontext(ctx); const char *fname; /* figure the fgw function to call */ js_currentfunction(ctx); js_getproperty(ctx, -1, FGW_FUNC_PROP); fname = js_tostring(ctx, -1); f = fgw_func_lookup(obj->parent, fname); js_pop(ctx, 1); argc = js_gettop(ctx); if ((argc - 1) > (sizeof(argv_static) / sizeof(argv_static[0]))) argv = malloc((argc - 1) * sizeof(fgw_arg_t)); else argv = argv_static; /* Set the first param */ argv[0].type = FGW_FUNC; argv[0].val.argv0.func = f; argv[0].val.argv0.user_call_ctx = obj->script_user_call_ctx; for(n = 2; n < argc; n++) fgws_mujs_js2arg(ctx, &argv[n-1], -n); /* Call the target function */ res.type = FGW_PTR; res.val.ptr_void = NULL; err = f->func(&res, argc-1, argv); for(n = 2; n < argc; n++) fgw_arg_free(obj->parent, &argv[n-1]); /* Free the array */ fgw_argv_free(f->obj->parent, argc, argv); if (argv != argv_static) free(argv); if (err != 0) { js_pushnull(ctx); return; } if ((res.type == FGW_PTR) && (res.val.ptr_void == NULL)) { js_pushnull(ctx); return; } fgws_mujs_push_arg(f->obj->parent, ctx, &res); if (res.type & FGW_DYN) free(res.val.ptr_void); } static int fgws_mujs_freg_in_script(js_State *ctx, const char *name, void (*f)(js_State *ctx), void *fgw_func) { js_newcfunction(ctx, f, name, 1); js_pushstring(ctx, name); js_defproperty(ctx, -2, FGW_FUNC_PROP, JS_READONLY | JS_DONTENUM | JS_DONTCONF); js_setglobal(ctx, name); return 0; } /* API: fgw calls a mujs function */ static fgw_error_t fgws_mujs_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; js_State *ctx = obj->script_data; int n; js_getglobal(ctx, argv[0].val.func->name); js_pushnull(ctx); for(n = 1; n < argc; n++) fgws_mujs_push_arg(obj->parent, ctx, &argv[n]); fgws_ucc_save(obj); js_pcall(ctx, argc-1); fgws_ucc_restore(obj); fgws_mujs_js2arg(ctx, res, -1); js_pop(ctx, 1); /* result */ return 0; } /* Helper function for the script to register its functions */ static void fgws_mujs_freg_in_fungw(js_State *ctx) { const char *fn; int argc = js_gettop(ctx); fgw_obj_t *obj = js_getcontext(ctx); fgw_func_t *func; if (argc != 2) { fprintf(stderr, "fgw_func_reg: called with wrong number of arguments (must be 1)\n"); goto error; } fn = js_tostring(ctx, -1); js_getglobal(ctx, fn); if (js_type(ctx, -1) != JS_ISFUNCTION) { fgw_async_error(obj, "fgw_func_reg: global function does not exist:"); fgw_async_error(obj, fn); fgw_async_error(obj, "\n"); goto error; } func = fgw_func_reg(obj, fn, fgws_mujs_call_script); if (func == NULL) { fgw_async_error(obj, "fgw_func_reg: failed to register function\n"); fgw_async_error(obj, fn); fgw_async_error(obj, "\n"); goto error; } js_pushboolean(ctx, 1); return; error:; js_pushboolean(ctx, 0); } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_mujs_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { fgws_mujs_freg_in_script(obj->script_data, name, fgws_mujs_call_fgw, f); } /* API: unload the script */ static int fgws_mujs_unload(fgw_obj_t *obj) { js_State *ctx = obj->script_data; js_freestate(ctx); return 0; } static void fgws_mujs_report(js_State *ctx, const char *message) { fgw_obj_t *obj = js_getcontext(ctx); fgw_async_error(obj, "mujs error:"); fgw_async_error(obj, message); fgw_async_error(obj, "\n"); } /* API: init the interpreter so that functions can be registered */ static int fgws_mujs_init(fgw_obj_t *obj, const char *filename, const char *opts) { js_State *ctx = js_newstate(NULL, NULL, JS_STRICT); if (ctx == NULL) return -1; obj->script_data = ctx; js_setreport(ctx, fgws_mujs_report); /* add the mujs->fgw glue */ fgws_mujs_freg_in_script(ctx, "print", fgws_mujs_print, ""); fgws_mujs_freg_in_script(ctx, "fgw_func_reg", fgws_mujs_freg_in_fungw, ""); js_setcontext(ctx, obj); return 0; } /* API: load a script into an object */ static int fgws_mujs_load(fgw_obj_t *obj, const char *filename, const char *opts) { js_State *ctx = obj->script_data; if (js_try(ctx)) { fgw_async_error(obj, "mujs script load error:"); fgw_async_error(obj, js_tostring(ctx, -1)); fgw_async_error(obj, "\n"); js_pop(ctx, 1); return -1; } js_ploadfile(ctx, filename); js_call(ctx, -1); js_pop(ctx, 1); js_endtry(ctx); return 0; } static int fgws_mujs_test_parse(const char *filename, FILE *f) { const char *exts[] = {".js", NULL }; return fgw_test_parse_fn(filename, exts); } /* API: engine registration */ static const fgw_eng_t fgw_mujs_eng = { "mujs", fgws_mujs_call_script, fgws_mujs_init, fgws_mujs_load, fgws_mujs_unload, fgws_mujs_reg_func, NULL, fgws_mujs_test_parse, ".js" }; int pplg_check_ver_fungw_mujs(int version_we_need) { return 0; } int pplg_init_fungw_mujs(void) { fgw_eng_reg(&fgw_mujs_eng); return 0; } void pplg_uninit_fungw_mujs(void) { fgw_eng_unreg(fgw_mujs_eng.name); } fungw-1.2.0/libfungwbind/picol/0000755000175100017510000000000014047742763014636 5ustar svnsvnfungw-1.2.0/libfungwbind/picol/Plug.tmpasm0000644000175100017510000000054014036321520016745 0ustar svnsvnput /local/fungw/mod {fungw_picol} put /local/fungw/mod_dir {picol} switch ?libs/script/picol/presents case {true} put /local/fungw/mod_cflags ?libs/script/picol/cflags put /local/fungw/mod_ldflags ?libs/script/picol/ldflags put /local/fungw/mod_src {fungw_picol.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/picol/fungw_picol.c0000644000175100017510000001327314036321520017302 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2017, 2019, 2021 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include #define PICOL_IMPLEMENTATION #include static int fgws_picol_test_parse(const char *filename, FILE *f) { const char *exts[] = {".pcl", NULL}; return fgw_test_parse_fn(filename, exts); } static void fgws_picol_err(fgw_obj_t *obj) { picolVar *v = picolGetVar(obj->script_data, "::errorInfo"); fgw_async_error(obj, v == NULL ? "" : v->val); } /* API: the script is calling an fgw function */ picolResult fgws_picol_call_fgw(picolInterp *interp, int argc, const char *argv[], void *pd) { fgw_obj_t *obj = pd; fgw_arg_t res, *sarg, sarg_static[128]; int n; fgw_func_t *func; fgw_error_t err; func = fgw_func_lookup(obj->parent, argv[0]); if (func == NULL) return 0; /* alloc arguments */ if ((argc+1) > (sizeof(sarg_static) / sizeof(sarg_static[0]))) sarg = malloc((argc+1) * sizeof(fgw_arg_t *)); else sarg = sarg_static; /* set up arguments */ sarg[0].type = FGW_FUNC; sarg[0].val.argv0.func = func; sarg[0].val.argv0.user_call_ctx = obj->script_user_call_ctx; for(n = 1; n < argc; n++) { sarg[n].type = FGW_STR; sarg[n].val.str = argv[n]; } /* Run command */ res.type = FGW_PTR; res.val.ptr_void = NULL; err = func->func(&res, argc, sarg); /* no need to free argv - all static strings */ if (sarg != sarg_static) free(sarg); if (err == 0) { fgw_arg_conv(obj->parent, &res, FGW_STR | FGW_DYN); picolSetResult(obj->script_data, res.val.str); free(res.val.str); } return err ? -1 : 0; } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_picol_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { picolRegisterCmd(obj->script_data, name, fgws_picol_call_fgw, obj); } /* API: fgw calls a picol function */ static fgw_error_t fgws_picol_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; picolInterp *interp = obj->script_data; int n, evr; long len = 0; fgw_error_t ret = FGW_SUCCESS; const char *i, **sarg, *sarg_static[128]; char *cmd, *o; if ((argc+1) > (sizeof(sarg_static) / sizeof(sarg_static[0]))) sarg = malloc((argc+1) * sizeof(char *)); else sarg = sarg_static; sarg[0] = argv[0].val.func->name; for(n = 1; n < argc; n++) { fgw_arg_conv(obj->parent, &argv[n], FGW_STR); sarg[n] = argv[n].val.str; len += strlen(sarg[n])*2 + 3; } cmd = o = malloc(len+4); if (cmd == NULL) { if (sarg != sarg_static) free(sarg); return FGW_ERR_UNKNOWN; } for(n = 0; n < argc; n++) { if (n > 0) *o++ = '"'; for(i = sarg[n]; *i != '\0'; i++) { if ((n > 0) && (*i == '"')) *o++ = '\\'; *o++ = *i; } if (n > 0) *o++ = '"'; *o++ = ' '; } *o = '\0'; fgws_ucc_save(obj); evr = picolEval(interp, cmd); fgws_ucc_restore(obj); free(cmd); if (evr != PICOL_OK) { fgws_picol_err(obj); ret = FGW_ERR_UNKNOWN; } res->type = FGW_STR; res->val.str = interp->result; fgw_argv_free(obj->parent, argc, argv); if (sarg != sarg_static) free(sarg); return ret; } /* API: unload the script */ static int fgws_picol_unload(fgw_obj_t *obj) { picolFreeInterp(obj->script_data); obj->script_data = NULL; return 0; } /* Helper function for the script to register its functions */ picolResult fgws_picol_freg(picolInterp *interp, int argc, const char *argv[], void *pd) { fgw_obj_t *obj = pd; fgw_func_t *func; if (argc != 2) { fgw_async_error(obj, "fgw_func_reg: wrong number of arguments: need 1\n"); return -1; } func = fgw_func_reg(obj, argv[1], fgws_picol_call_script); if (func == NULL) { fgw_async_error(obj, "fgw_func_reg: failed to register function\n"); fgw_async_error(obj, argv[1]); fgw_async_error(obj, "\n"); return -1; } return 0; } /* API: init the interpreter so that functions can be registered */ static int fgws_picol_init(fgw_obj_t *obj, const char *filename, const char *opts) { obj->script_data = picolCreateInterp(); picolRegisterCmd(obj->script_data, "fgw_func_reg", fgws_picol_freg, obj); return 0; } /* API: load a script into an object */ static int fgws_picol_load(fgw_obj_t *obj, const char *filename, const char *opts) { /* Read the file */ if (picolSource(obj->script_data, filename) == PICOL_OK) return 0; fgw_async_error(obj, "fgws_picol_load: failed to eval the file\n"); fgws_picol_err(obj); return -1; } /* API: engine registration */ static const fgw_eng_t fgw_picol_eng = { "picol", fgws_picol_call_script, fgws_picol_init, fgws_picol_load, fgws_picol_unload, fgws_picol_reg_func, NULL, fgws_picol_test_parse, ".pcl" }; int pplg_check_ver_fungw_picol(int version_we_need) { return 0; } int pplg_init_fungw_picol(void) { fgw_eng_reg(&fgw_picol_eng); return 0; } void pplg_uninit_fungw_picol(void) { fgw_eng_unreg(fgw_picol_eng.name); } fungw-1.2.0/libfungwbind/picol/fungw_picol.pup0000644000175100017510000000012614036321520017655 0ustar svnsvn$desc tcl binding engine $state works $script-ext tcl .tcl default buildin autoload 0 fungw-1.2.0/libfungwbind/Plugin.tmpasm0000644000175100017510000000735314032753233016205 0ustar svnsvnappend /local/fungw/summary [~ ~/local/fungw/mod~::e~] put /local/fungw/mod_mak [~~/local/fungw/mod_dir~/~/local/fungw/mod~.mak~] put /local/fungw/mod_mak_bn [~~/local/fungw/mod~.mak~] put /local/fungw/mod_pupfile [~~/local/fungw/mod~.pup~] append /local/fungw/bindings /local/fungw/mod_dir { } uniq /local/fungw/mod_obj /local/fungw/mod_src append /local/fungw/mod_obj { } gsub /local/fungw/mod_obj {.c[ \t\r\n]} {.o } redir [@@/local/fungw/mod_dir@/Makefile@] print [~# Generated by ./configure - DO NOT EDIT # Compile the plugin locally. FUNGWROOT=../../ FUNGW = $(FUNGWROOT)/libfungw PREFIX=~/local/fungw/prefix~ LIBDIR=$(install_root)$(DESTDIR)$(PREFIX)/~/local/fungw/libdirname~ PUPDIR=$(install_root)$(DESTDIR)$(PREFIX)/~/local/fungw/pupdirname~ CFLAGS = -Wall -g -I$(FUNGWROOT) -I../../src_3rd ~?cc/fpic~ ~?/local/fungw/mod_cflags~ LDFLAGS = ~?/local/fungw/mod_ldflags~ SCCBOX = $(FUNGWROOT)/scconfig/sccbox LIBA = ~/local/fungw/mod~.a LIBSO = ~/local/fungw/mod~~/target/sys/ext_dynlib~ LIBSO_X = $(LIBSO).~/local/fungw/ver1~ LIBSO_XY = $(LIBSO).~/local/fungw/ver1~.~/local/fungw/ver2~ LIBSO_XYZ = $(LIBSO).~/local/fungw/ver1~.~/local/fungw/ver2~.~/local/fungw/ver3~ OBJS = ~/local/fungw/mod_obj~ ~] switch /target/cc/soname case {^$} put /local/fungw/mod_soname {} end default put /local/fungw/mod_soname [@@/target/cc/soname@lib$(LIBSO_X)@] end end print [~ all: $(LIBA) $(LIBSO) $(LIBA): $(OBJS) $(SCCBOX) rm -f $(LIBA) @~/host/fstools/ar~ r $(LIBA) $(OBJS) $(LIBSO): $(OBJS) $(CC) $(OBJS) -o $(LIBSO) $(LDFLAGS) ~?/target/cc/ldflags_dynlib~ ~?/local/fungw/mod_soname~ ~/target/cc/so_undefined~ ### Rules for compiling objects ### ~] foreach /local/fungw/c in /local/fungw/mod_src put /local/fungw/o /local/fungw/c sub /local/fungw/o {.c$} {.o} print [~ ~/local/fungw/o~: ~/local/fungw/c~ $(FUNGWROOT)/libfungw/fungw.h $(CC) -c $(CFLAGS) -o ~/local/fungw/o~ ~/local/fungw/c~ ~] end foreach /local/fungw/c in ?/local/fungw/cquote print [~ ~/local/fungw/c~.h: ~/local/fungw/c~ $(FUNGWROOT)/scconfig/cquote -p static -n ~/local/fungw/c~ < ~/local/fungw/c~ > ~/local/fungw/c~.h ~] end print [~ ~?/local/fungw/mod_extra_rules~ clean: $(SCCBOX) rm -f $(LIBA) $(LIBSO) $(OBJS) distclean: clean rm ~?/local/fungw/mod_distclean_files~ Makefile ~/local/fungw/mod_mak_bn~ install_: $(LIBA) $(LIBSO) $(SCCBOX) mkdir $(IOP) -p $(PUPDIR) $(SCCBOX) install $(IOP) $(LIBA) $(LIBDIR)/lib$(LIBA) $(SCCBOX) install $(IOP) $(LIBSO) $(LIBDIR)/lib$(LIBSO_XYZ) $(SCCBOX) install $(IOP) ~/local/fungw/mod_mak_bn~ $(PUPDIR)/~/local/fungw/mod_mak_bn~ $(SCCBOX) install $(IOP) ~/local/fungw/mod_pupfile~ $(PUPDIR)/~/local/fungw/mod_pupfile~ install_link: $(LIBA) $(LIBSO) $(SCCBOX) install $(IOP) --relative $(LIBDIR)/lib$(LIBA) $(PUPDIR)/$(LIBA) $(SCCBOX) install $(IOP) --relative $(LIBDIR)/lib$(LIBSO_XYZ) $(PUPDIR)/$(LIBSO) $(SCCBOX) install $(IOP) --relative $(LIBDIR)/lib$(LIBSO_XYZ) $(LIBDIR)/lib$(LIBSO_XY) $(SCCBOX) install $(IOP) --relative $(LIBDIR)/lib$(LIBSO_XYZ) $(LIBDIR)/lib$(LIBSO_X) install: make install_ IOP="-i" make install_link IOP="-l" linstall: make install_ IOP="-l" make install_link IOP="-l" uninstall: make install_link IOP="-u" make install_ IOP="-u" ~] redir /local/fungw/mod_mak print [~# Generated by ./configure - DO NOT EDIT # Use these flags when linking the plugin without puplug CFLAGS_~/local/fungw/mod~ = ~?/local/fungw/mod_cflags~ LDFLAGS_~/local/fungw/mod~ = ~?/local/fungw/mod_ldflags~ # Use the system-installed version LIBA_~/local/fungw/mod~ = /usr/lib/puplug/~/local/fungw/mod~.a # Link from source tree; user needs to set $(FUNGWBIND) SRCLIBA_~/local/fungw/mod~ = $(FUNGWBIND)/~/local/fungw/mod_dir~/~/local/fungw/mod~.a ~] redir put /local/fungw/mod_distclean_files {} put /local/fungw/mod_extra_rules {} fungw-1.2.0/libfungwbind/mawk/0000755000175100017510000000000014047742763014467 5ustar svnsvnfungw-1.2.0/libfungwbind/mawk/Plug.tmpasm0000644000175100017510000000053013110722271016575 0ustar svnsvnput /local/fungw/mod {fungw_mawk} put /local/fungw/mod_dir {mawk} switch ?libs/script/mawk/presents case {true} put /local/fungw/mod_cflags libs/script/mawk/cflags put /local/fungw/mod_ldflags libs/script/mawk/ldflags put /local/fungw/mod_src {fungw_mawk.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/mawk/fungw_mawk.c0000644000175100017510000002300713757344474017006 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2017, 2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include #define MAX_ARG 256 typedef struct mawk_ctx_s { mawk_state_t *state; mawk_vio_t *vf_stdin; } mawk_ctx_t; /* Extract the fgw object from a mawk state (stored there as a global variable) */ static fgw_obj_t *fgws_mawk_get_obj(mawk_state_t *mst) { return mst->func_userdata; } /* Convert fgw args to void * arg for a mawk call */ static void fgw_mawk_set_cell(fgw_ctx_t *fctx, mawk_ctx_t *mctx, mawk_cell_t *dst, fgw_arg_t *arg) { char tmp[128]; # define FGW_MAWK_SET_DOUBLE(lst, val) dst->type = C_NUM, dst->d.dval = val; return; # define FGW_MAWK_SET_STR(lst, val) dst->type = C_STRING, dst->ptr = mawk_new_STRING(mctx->state, val); return; # define FGW_MAWK_SET_PTR(lst, val) sprintf(tmp, "%p", val); dst->type = C_STRING, dst->ptr = mawk_new_STRING(mctx->state, tmp); return; # define FGW_MAWK_SET_NIL(lst, val) dst->type = C_NOINIT; return; if (FGW_IS_TYPE_CUSTOM(arg->type)) fgw_arg_conv(fctx, arg, FGW_AUTO); /* if fails, it remains custom and will be unhandled */ switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(lst, FGW_MAWK_SET_DOUBLE); ARG_CONV_CASE_LLONG(lst, FGW_MAWK_SET_DOUBLE); ARG_CONV_CASE_DOUBLE(lst, FGW_MAWK_SET_DOUBLE); ARG_CONV_CASE_LDOUBLE(lst, FGW_MAWK_SET_DOUBLE); ARG_CONV_CASE_PTR(lst, FGW_MAWK_SET_PTR); ARG_CONV_CASE_STR(lst, FGW_MAWK_SET_STR); ARG_CONV_CASE_CLASS(lst, FGW_MAWK_SET_NIL); ARG_CONV_CASE_INVALID(lst, FGW_MAWK_SET_NIL); } if (arg->type & FGW_PTR) { FGW_MAWK_SET_PTR(lst, arg->val.ptr_void); } else { FGW_MAWK_SET_NIL(dst, 0) } } /* Read the mawk stack and convert the result to an fgw arg */ static void fgw_mawk_get_cell(mawk_ctx_t *mctx, fgw_arg_t *dst, mawk_cell_t *src) { switch(src->type) { case C_NUM: case C_STRNUM: dst->type = FGW_DOUBLE; dst->val.nat_double = src->d.dval; break; case C_STRING: case C_MBSTRN: dst->type = FGW_STR | FGW_DYN; dst->val.str = fgw_strdup(((mawk_string_t *)(src->ptr))->str); break; case C_NOINIT: default: dst->type = FGW_PTR; dst->val.ptr_void = NULL; break; } } typedef struct { fgw_obj_t *obj; char fname[1]; } fgws_mawk_call_t; /* API: the script is calling an fgw function */ static mawk_cell_t *fgws_mawk_call_fgw(mawk_state_t *context, mawk_cell_t *sp, int argc) { int n, i; fgws_mawk_call_t *cl = context->func_userdata; fgw_arg_t res, *argv, argv_static[16]; mawk_ctx_t *mctx = cl->obj->script_data; fgw_func_t *func = fgw_func_lookup(cl->obj->parent, cl->fname); if (cl == NULL) { /* happens when a function is deregistered */ goto error; } mctx = cl->obj->script_data; func = fgw_func_lookup(cl->obj->parent, cl->fname); if (func == NULL) { error:; res.type = FGW_PTR; res.val.ptr_void = NULL; goto out; } if ((argc + 1) > (sizeof(argv_static) / sizeof(argv_static[0]))) argv = malloc((argc + 1) * sizeof(fgw_arg_t)); else argv = argv_static; argv[0].type = FGW_FUNC; argv[0].val.argv0.func = func; argv[0].val.argv0.user_call_ctx = cl->obj->script_user_call_ctx; for(n = 1, i = argc-1; i >= 0; i--,n++) fgw_mawk_get_cell(mctx, &argv[n], sp-i); /* Run command */ res.type = FGW_PTR; res.val.ptr_void = NULL; if (func->func(&res, argc+1, argv) != FGW_SUCCESS) { res.type = FGW_PTR; res.val.ptr_void = NULL; } /* Free the array */ fgw_argv_free(cl->obj->parent, argc+1, argv); if (argv != argv_static) free(argv); /* restore stack */ out:; fgw_mawk_set_cell(func->obj->parent, mctx, libmawk_cfunc_ret(sp, argc), &res); return sp-argc; } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_mawk_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { mawk_ctx_t *mctx = obj->script_data; fgws_mawk_call_t *cl; void *old_userdata; int nl = strlen(name); old_userdata = mctx->state->func_userdata; cl = malloc(sizeof(fgws_mawk_call_t)+nl); /* the +1 for \0 is the sizeof(cl->fname) */ cl->obj = obj; memcpy(cl->fname, name, nl+1); mctx->state->func_userdata = cl; libmawk_register_function(mctx->state, name, fgws_mawk_call_fgw); mctx->state->func_userdata = old_userdata; } static void fgws_mawk_unreg_func(fgw_obj_t *obj, const char *name) { mawk_ctx_t *mctx = obj->script_data; SYMTAB *sym; if ((mctx == NULL) || (mctx->state == NULL)) return; sym = mawk_find(mctx->state, name, 0); if ((sym != NULL) && (sym->type == ST_C_FUNCTION)) { fgws_mawk_call_t *cl = sym->stval.c_function.func_userdata; free(cl); sym->stval.c_function.func_userdata = NULL; } } /* API: fgw calls a mawk function */ static fgw_error_t fgws_mawk_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; mawk_ctx_t *mctx = obj->script_data; mawk_cell_t arg[MAX_ARG]; mawk_cell_t retc = libmawk_empty_cell; mawk_exec_result_t er; int n; if (argc >= MAX_ARG) return FGW_ERR_ARGC; for(n = 1; n < argc; n++) fgw_mawk_set_cell(obj->parent, mctx, &arg[n-1], &argv[n]); fgws_ucc_save(obj); er = libmawk_call_functionc(mctx->state, argv[0].val.func->name, &retc, argc-1, arg); fgws_ucc_restore(obj); if (er == MAWK_EXER_FUNCRET) { fgw_mawk_get_cell(mctx, res, &retc); libmawk_cell_destroy(mctx->state, &retc); return FGW_SUCCESS; } res->type = FGW_INVALID; return FGW_ERR_UNKNOWN; } /* API: unload the script */ static int fgws_mawk_unload(fgw_obj_t *obj) { mawk_ctx_t *mctx = obj->script_data; if ((mctx != NULL) && (mctx->state != NULL)) libmawk_uninitialize(mctx->state); free(mctx); obj->script_data = NULL; return 0; } /* Helper function for the script to register its functions */ static mawk_cell_t *fgws_mawk_freg(mawk_state_t *mst, mawk_cell_t *sp, int argc) { fgw_obj_t *obj; char name[FGW_ID_LEN]; fgw_func_t *func; obj = fgws_mawk_get_obj(mst); if (argc != 1) { fgw_async_error(obj, "fgw_func_reg: wrong number of arguments: need 1\n"); goto ret; } libmawk_print_cell(mst, libmawk_cfunc_arg(sp, argc, 0), name, sizeof(name)); func = fgw_func_reg(obj, name, fgws_mawk_call_script); if (func == NULL) { fgw_async_error(obj, "fgw_func_reg: failed to register function\n"); fgw_async_error(obj, name); fgw_async_error(obj, "\n"); } ret:; return sp-argc; } /* API: init the interpreter so that functions can be registered */ static int fgws_mawk_init(fgw_obj_t *obj, const char *filename, const char *opts) { mawk_ctx_t *mctx; /* initialize the mawk state */ obj->script_data = mctx = calloc(sizeof(mawk_ctx_t), 1); mctx->state = libmawk_initialize_stage1(); libmawk_initialize_stdio(mctx->state, 0, 1, 1); mctx->state->func_userdata = obj; if (mctx->state == NULL) { fgw_async_error(obj, "fgws_mawk_init: stage1 init failed\n"); free(mctx); obj->script_data = NULL; return -1; } /* set up stdio */ mawk_vio_orig_setup_stdio(mctx->state, 0, 1, 1); /* whether bind to the app's stdio: 0,1,1=stdin,stdout,stderr; let stdout and stderr bind */ mctx->vf_stdin = mawk_vio_fifo_open(mctx->state, NULL, MAWK_VIO_I); /* create a pipe for stdin */ mawk_file_register(mctx->state, "/dev/stdin", F_IN, mctx->vf_stdin); /* register /dev/stdin */ mctx->state->vio_init = mawk_vio_orig_init; /* file operation is handled by the orig vio */ /* register freg before the script is loaded */ libmawk_register_function(mctx->state, "fgw_func_reg", fgws_mawk_freg); return 0; } /* API: load a script into an object */ static int fgws_mawk_load(fgw_obj_t *obj, const char *filename, const char *opts) { mawk_ctx_t *mctx = obj->script_data; /* load the script */ mawk_append_input_file(mctx->state, filename, 0); mctx->state = libmawk_initialize_stage2(mctx->state, 0, NULL); if (mctx->state == NULL) { fgw_async_error(obj, "fgws_mawk_load: stage2 init failed: syntax error in the script?\n"); goto error; } mctx->state = libmawk_initialize_stage3(mctx->state); if (mctx->state == NULL) { fgw_async_error(obj, "fgws_mawk_load: stage3 init failed: BEGIN of the script broke\n"); goto error; } return 0; error:; free(mctx); obj->script_data = NULL; return -1; } static int fgws_mawk_test_parse(const char *filename, FILE *f) { const char *exts[] = {".awk", ".mawk", NULL }; return fgw_test_parse_fn(filename, exts); } /* API: engine registration */ static const fgw_eng_t fgw_mawk_eng = { "mawk", fgws_mawk_call_script, fgws_mawk_init, fgws_mawk_load, fgws_mawk_unload, fgws_mawk_reg_func, fgws_mawk_unreg_func, fgws_mawk_test_parse, ".awk" }; int pplg_check_ver_fungw_mawk(int version_we_need) { return 0; } int pplg_init_fungw_mawk(void) { fgw_eng_reg(&fgw_mawk_eng); return 0; } void pplg_uninit_fungw_mawk(void) { fgw_eng_unreg(fgw_mawk_eng.name); } fungw-1.2.0/libfungwbind/mawk/fungw_mawk.pup0000644000175100017510000000013013757460755017361 0ustar svnsvn$desc mawk binding engine $state works $script-ext mawk .awk default buildin autoload 0 fungw-1.2.0/libfungwbind/python3/0000755000175100017510000000000014047742763015134 5ustar svnsvnfungw-1.2.0/libfungwbind/python3/Plug.tmpasm0000644000175100017510000000055213415315441017253 0ustar svnsvnput /local/fungw/mod {fungw_python3} put /local/fungw/mod_dir {python3} switch ?libs/script/python3/presents case {true} put /local/fungw/mod_cflags libs/script/python3/cflags put /local/fungw/mod_ldflags libs/script/python3/ldflags put /local/fungw/mod_src {fungw_python3.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/python3/fungw_python3.pup0000644000175100017510000000024313757460755020500 0ustar svnsvn$desc python3 binding engine $state works $script-ext python3 .py $script-ext python3 .py3 $script-ext python3 .python default buildin autoload 0 conflict python fungw-1.2.0/libfungwbind/python3/fungw_python3.c0000644000175100017510000003053713757344474020126 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2018,2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include /* Python redefines this blindly */ #undef _POSIX_C_SOURCE #include typedef struct { PyObject *module, *dict; char modname[64]; PyThreadState *interp; fgw_obj_t *obj; } py_ctx_t; typedef struct { char *name; fgw_obj_t *obj; py_ctx_t *ctx; } py_func_t; static void fgws_python_obj2arg(fgw_arg_t *dst, PyObject *src) { PyTypeObject *type = Py_TYPE(src); if (type == &PyUnicode_Type) { dst->type = FGW_STR | FGW_DYN; dst->val.str = fgw_strdup(PyUnicode_AsUTF8(PyObject_Str(src))); } else if (type == &PyFloat_Type) { dst->type = FGW_DOUBLE; dst->val.nat_double = PyFloat_AsDouble(src); } else if (type == &PyBool_Type) { dst->type = FGW_INT; dst->val.nat_int = PyBool_Check(src) ? 1 : 0; } else if (type == &PyLong_Type) { dst->type = FGW_LONG; dst->val.nat_long = PyLong_AsLong(src); } else { fprintf(stderr, "fgws_python_obj2arg: ignoring unknown type %s\n", type->tp_name); dst->type = FGW_PTR; dst->val.ptr_void = NULL; } } PyObject *fgws_python_arg2obj(fgw_ctx_t *fctx, fgw_arg_t *arg) { # define FGW_PY_SET_LONG(lst, val) return PyLong_FromLong(val); # define FGW_PY_SET_LLONG(lst, val) return PyLong_FromLongLong(val); # define FGW_PY_SET_DOUBLE(lst, val) return PyFloat_FromDouble(val); # define FGW_PY_SET_PTR(lst, val) return PyLong_FromVoidPtr(val); # define FGW_PY_SET_NIL(lst, val) return Py_False; /* NOTE: have to use PyBytes here, see below at #PYBYTESBUG */ # define FGW_PY_SET_STR(lst, val) return PyBytes_FromString(val); if (FGW_IS_TYPE_CUSTOM(arg->type)) fgw_arg_conv(fctx, arg, FGW_AUTO); /* if fails, it remains custom and will be unhandled */ switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(NULL, FGW_PY_SET_LONG); ARG_CONV_CASE_LLONG(NULL, FGW_PY_SET_LLONG); ARG_CONV_CASE_DOUBLE(NULL, FGW_PY_SET_DOUBLE); ARG_CONV_CASE_LDOUBLE(NULL, FGW_PY_SET_DOUBLE); ARG_CONV_CASE_PTR(lst, FGW_PY_SET_PTR); ARG_CONV_CASE_STR(NULL, FGW_PY_SET_STR); ARG_CONV_CASE_CLASS(lst, FGW_PY_SET_NIL); ARG_CONV_CASE_INVALID(lst, FGW_PY_SET_NIL); } return Py_None; } /* API: the script is calling an fgw function */ static PyObject *fgws_python_call_fgw(PyObject *self, PyObject *args) { py_func_t *func_ctx; fgw_func_t *func; int argc, n; fgw_arg_t res, *argv, argv_static[16]; fgw_error_t err; PyObject *py_res; func_ctx = PyCapsule_GetPointer(self, NULL); func = fgw_func_lookup(func_ctx->obj->parent, func_ctx->name); if (func == NULL) { fgw_async_error(func_ctx->obj, "fgws_python_call_fgw: function to be called is not found:"); fgw_async_error(func_ctx->obj, func_ctx->name); fgw_async_error(func_ctx->obj, "\n"); return Py_None; } argc = PyTuple_Size(args); if ((argc + 1) > (sizeof(argv_static) / sizeof(argv_static[0]))) argv = malloc((argc + 1) * sizeof(fgw_arg_t)); else argv = argv_static; /* Set the first param */ argv[0].type = FGW_FUNC; argv[0].val.argv0.func = func; argv[0].val.argv0.user_call_ctx = func_ctx->obj->script_user_call_ctx; /* Convert all params to fgw args */ for (n = 0; n < argc; n++) fgws_python_obj2arg(&argv[n+1], PyTuple_GetItem(args, n)); /* Call the target function */ res.type = FGW_PTR; res.val.ptr_void = NULL; err = func->func(&res, argc+1, argv); /* Free the array */ fgw_argv_free(func_ctx->obj->parent, argc, argv); if (argv != argv_static) free(argv); if (err != 0) return 0; py_res = fgws_python_arg2obj(func->obj->parent, &res); if (res.type & FGW_DYN) free(res.val.ptr_void); return py_res; } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_python_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { py_ctx_t *ctx = obj->script_data; py_func_t *func_ctx; PyObject *new, *fc; PyMethodDef *new_func_define; char *new_name = fgw_strdup(name); PyMethodDef met[] = { {NULL, fgws_python_call_fgw, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; PyThreadState_Swap(ctx->interp); met->ml_name = new_name; new_func_define = malloc(sizeof(met)); memcpy(new_func_define, met, sizeof(met)); /* save function call context */ func_ctx = malloc(sizeof(py_func_t)); func_ctx->name = new_name; func_ctx->obj = obj; func_ctx->ctx = ctx; fc = PyCapsule_New(func_ctx, NULL, NULL); new = PyCFunction_New(new_func_define, fc); if ((new == NULL) || PyDict_SetItemString(ctx->dict, new_name, new)) { fgw_async_error(obj, "fgws_python_reg_func: failed to register function:"); fgw_async_error(obj, new_name); fgw_async_error(obj, "\n"); if (PyErr_Occurred()) PyErr_Print(); free(new_name); free(func_ctx); PyThreadState_Swap(NULL); return; } Py_DECREF(new); PyThreadState_Swap(NULL); } /* API: fgw calls a python function */ static fgw_error_t fgws_python_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; py_ctx_t *ctx = obj->script_data; PyObject *pfunc, *pargs, *pv, *pret; const char *func_name = argv[0].val.func->name; int n; fgw_error_t ret = FGW_SUCCESS; PyThreadState_Swap(ctx->interp); pfunc = PyDict_GetItemString(ctx->dict, func_name); if ((pfunc == NULL) || !PyCallable_Check(pfunc)) { fgw_async_error(obj, "Not a callable python object:"); fgw_async_error(obj, func_name); fgw_async_error(obj, "\n"); if (PyErr_Occurred()) PyErr_Print(); PyThreadState_Swap(NULL); return FGW_ERR_NOT_FOUND; } /* Create the args-list */ pargs = PyTuple_New(argc-1); for (n = 1; n < argc; n++ ) { pv = fgws_python_arg2obj(obj->parent, &argv[n]); PyTuple_SetItem(pargs, n-1, pv); } fgws_ucc_save(obj); pret = PyObject_CallObject(pfunc, pargs); fgws_ucc_restore(obj); /* #PYBYTESBUG: Py_DECREF(pv) causes a segfault on PyUnicode on uninit-time */ for(n = 1; n < argc; n++) { pv = PyTuple_GetItem(pargs, n-1); Py_DECREF(pv); } if (pret != NULL) fgws_python_obj2arg(res, pret); else res->type = FGW_INVALID; Py_DECREF(pargs); if (pret != NULL) { Py_DECREF(pret); } if (PyErr_Occurred()) { PyErr_Print(); ret = FGW_ERR_UNKNOWN; } PyThreadState_Swap(NULL); return ret; } static int py_global_inited = 0; static PyThreadState *py_main_interp; static PyGILState_STATE py_main_state; /* API: unload the script */ static int fgws_python_unload(fgw_obj_t *obj) { py_ctx_t *ctx = obj->script_data; if (ctx->interp != NULL) { PyThreadState_Swap(ctx->interp); Py_EndInterpreter(ctx->interp); } PyThreadState_Swap(NULL); free(ctx); py_global_inited--; if (py_global_inited == 0) { PyThreadState_Swap(py_main_interp); PyGILState_Release(py_main_state); Py_Finalize(); } return 0; } /* Helper function for the script to register its functions */ static PyObject *fgws_python_freg(PyObject *self, PyObject *args) { py_func_t *func_ctx = PyCapsule_GetPointer(self, NULL); fgw_obj_t *obj = func_ctx->obj; PyObject *pfunc; const char *name; int argc; fgw_func_t *func; argc = PyTuple_Size(args); if (argc != 1) { fgw_async_error(obj, "fgw_func_reg: wrong number of arguments: need 1\n"); return 0; } name = PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 0))); if (name == NULL) { fgw_async_error(obj, "fgw_func_reg: empty name\n"); return 0; } pfunc = PyDict_GetItemString(func_ctx->ctx->dict, name); if (pfunc == NULL) { fgw_async_error(obj, "fgw_func_reg: function doesn't exist:"); fgw_async_error(obj, name); fgw_async_error(obj, "\n"); if (PyErr_Occurred()) PyErr_Print(); return 0; } func = fgw_func_reg(obj, name, fgws_python_call_script); PyThreadState_Swap(func_ctx->ctx->interp); /* side effect: _reg_func(): has set it to NULL (for making sure no active interpreter is left lying around between calls) */ if (func == NULL) { fgw_async_error(obj, "fgw_python_func_reg: failed to register function: "); fgw_async_error(obj, name); fgw_async_error(obj, "\n"); return 0; } return Py_True; } static int fgws_python_setup_glob(py_ctx_t *ctx) { /* add the python->fgw glue */ { py_func_t *func_ctx; PyObject *new, *fc; PyMethodDef *new_func_define; char *new_name = fgw_strdup("fgw_func_reg"); PyMethodDef met[] = { {"fgw_func_reg", fgws_python_freg, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; met->ml_name = new_name; new_func_define = malloc(sizeof(met)); memcpy(new_func_define, met, sizeof(met)); func_ctx = malloc(sizeof(py_func_t)); func_ctx->name = new_name; func_ctx->obj = ctx->obj; func_ctx->ctx = ctx; fc = PyCapsule_New(func_ctx, NULL, NULL); new = PyCFunction_New(new_func_define, fc); if ((new == NULL) || PyDict_SetItemString(ctx->dict, new_name, new)) { fgw_async_error(ctx->obj, "fgws_python_init: failed to register function: fgw_func_reg\n"); if (PyErr_Occurred()) PyErr_Print(); free(new_name); free(func_ctx); PyThreadState_Swap(NULL); return -1; } Py_DECREF(new); } return 0; } /* API: init the interpreter so that functions can be registered */ static int fgws_python_init(fgw_obj_t *obj, const char *filename, const char *opts) { py_ctx_t *ctx; static long py_global_cnt = 0; ctx = calloc(sizeof(py_ctx_t), 1); if (ctx == NULL) { fgw_async_error(obj, "fgws_python_init: failed to allocate context\n"); return -1; } obj->script_data = ctx; ctx->obj = obj; if (py_global_inited == 0) { Py_Initialize(); py_main_interp = PyThreadState_Get(); /* Need to keep this only for GIL uninit */ py_main_state = PyGILState_Ensure(); } ctx->interp = Py_NewInterpreter(); PyThreadState_Swap(ctx->interp); sprintf(ctx->modname, "_fungw_%ld_", py_global_cnt++); { PyObject *bimod; struct PyModuleDef *python_empty_mod, python_empty_mod_tmp = { PyModuleDef_HEAD_INIT, "foo", "fungw loaded script module", -1, /* sizeof */ NULL, /* methods */ NULL, NULL, NULL, NULL }; python_empty_mod = malloc(sizeof(struct PyModuleDef)); memcpy(python_empty_mod, &python_empty_mod_tmp, sizeof(struct PyModuleDef)); python_empty_mod->m_name = ctx->modname; bimod = PyImport_ImportModule("builtins"); if (bimod == NULL) /* this shouldn't ever fail, but better check */ return -1; ctx->dict = PyDict_New(); PyDict_SetItemString(ctx->dict, "__builtins__", bimod); /* set up builtins for the new module */ fgws_python_setup_glob(ctx); } py_global_inited++; PyThreadState_Swap(NULL); return 0; } /* API: load a script into an object */ static int fgws_python_load(fgw_obj_t *obj, const char *filename, const char *opts) { py_ctx_t *ctx = obj->script_data; FILE *f; PyThreadState_Swap(ctx->interp); f = fopen(filename, "r"); ctx->module = PyRun_File(f, filename, Py_file_input, ctx->dict, ctx->dict); fclose(f); if (ctx->module == NULL) { fgw_async_error(obj, "Failed to load python script:"); fgw_async_error(obj, filename); fgw_async_error(obj, "\n"); if (PyErr_Occurred()) PyErr_Print(); PyThreadState_Swap(NULL); return -1; } PyThreadState_Swap(NULL); return 0; } static int fgws_python_test_parse(const char *filename, FILE *f) { const char *exts[] = {".py", ".py3", ".python", NULL }; return fgw_test_parse_fn(filename, exts); } /* API: engine registration */ static const fgw_eng_t fgw_python_eng = { "python", fgws_python_call_script, fgws_python_init, fgws_python_load, fgws_python_unload, fgws_python_reg_func, NULL, fgws_python_test_parse, ".py" }; int pplg_check_ver_fungw_python(int version_we_need) { return 0; } int pplg_init_fungw_python(void) { fgw_eng_reg(&fgw_python_eng); return 0; } void pplg_uninit_fungw_python(void) { fgw_eng_unreg(fgw_python_eng.name); } fungw-1.2.0/libfungwbind/mruby/0000755000175100017510000000000014047742763014666 5ustar svnsvnfungw-1.2.0/libfungwbind/mruby/fungw_mruby.pup0000644000175100017510000000021113757657675017770 0ustar svnsvn$desc mruby binding engine $state works $script-ext mruby .rb $script-ext mruby .mruby $lang-alias mruby ruby default buildin autoload 0 fungw-1.2.0/libfungwbind/mruby/Plug.tmpasm0000644000175100017510000000053613300234127017001 0ustar svnsvnput /local/fungw/mod {fungw_mruby} put /local/fungw/mod_dir {mruby} switch ?libs/script/mruby/presents case {true} put /local/fungw/mod_cflags libs/script/mruby/cflags put /local/fungw/mod_ldflags libs/script/mruby/ldflags put /local/fungw/mod_src {fungw_mruby.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/mruby/fungw_mruby.c0000644000175100017510000002116213757344474017404 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2018, 2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include #include #include #include typedef struct { mrb_state *mrb; mrb_value script; } mruby_ctx_t; static void fgws_mruby_val2arg(mruby_ctx_t *ctx, fgw_arg_t *dst, mrb_value src) { const char *s; switch(mrb_type(src)) { case MRB_TT_FALSE: dst->type = FGW_INT; dst->val.nat_int = 0; break; case MRB_TT_TRUE: dst->type = FGW_INT; dst->val.nat_int = 1; break; case MRB_TT_FIXNUM: dst->type = FGW_LONG; dst->val.nat_long = mrb_fixnum(src); break; case MRB_TT_FLOAT: dst->type = FGW_DOUBLE; dst->val.nat_double = mrb_float(src); break; case MRB_TT_CPTR: dst->type = FGW_PTR; dst->val.ptr_void = mrb_cptr(src); break; case MRB_TT_STRING: dst->type = FGW_STR | FGW_DYN; s = mrb_string_value_ptr(ctx->mrb, src); if (*s == '"') { /* mruby returns quoted string for some reason */ dst->val.str = fgw_strdup(s+1); dst->val.str[strlen(dst->val.str)-1] = '\0'; } else dst->val.str = fgw_strdup(s); break; default: dst->type = FGW_PTR; dst->val.ptr_void = NULL; break; } } static void fgws_mruby_arg2val(fgw_ctx_t *fctx, mruby_ctx_t *ctx, mrb_value *dst, fgw_arg_t *arg) { # define FGW_RB_SET_LONG(lst, val) *dst = mrb_fixnum_value(val); return; # define FGW_RB_SET_DOUBLE(lst, val) *dst = mrb_float_value(ctx->mrb, val); return; # define FGW_RB_SET_PTR(lst, val) *dst = mrb_cptr_value(ctx->mrb, val); return; # define FGW_RB_SET_STR(lst, val) *dst = mrb_str_new_cstr(ctx->mrb, val); return; # define FGW_RB_SET_NIL(lst, val) *dst = mrb_nil_value(); return; if (FGW_IS_TYPE_CUSTOM(arg->type)) fgw_arg_conv(fctx, arg, FGW_AUTO); /* if fails, it remains custom and will be unhandled */ switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(NULL, FGW_RB_SET_LONG); ARG_CONV_CASE_LLONG(NULL, FGW_RB_SET_DOUBLE); ARG_CONV_CASE_DOUBLE(NULL, FGW_RB_SET_DOUBLE); ARG_CONV_CASE_LDOUBLE(NULL, FGW_RB_SET_DOUBLE); ARG_CONV_CASE_PTR(NULL, FGW_RB_SET_PTR); ARG_CONV_CASE_STR(NULL, FGW_RB_SET_STR); ARG_CONV_CASE_CLASS(NULL, FGW_RB_SET_NIL); ARG_CONV_CASE_INVALID(NULL, FGW_RB_SET_NIL); } *dst = mrb_nil_value(); } /* API: the script is calling an fgw function */ static mrb_value fgws_mruby_call_fgw(mrb_state *mrb, mrb_value self) { fgw_obj_t *obj = mrb->ud; mruby_ctx_t *ctx = obj->script_data; const char *func_name = mrb_sym2name(mrb, mrb_obj_to_sym(mrb, mrb_funcall(mrb, self, "__method__", 0))); fgw_arg_t res, *argv, argv_static[16]; fgw_func_t *func; int n, argc; mrb_value *rargv, rres; fgw_error_t err; /* Find our proc based on the function-name */ func = fgw_func_lookup(obj->parent, func_name); if (func == NULL) { fgw_async_error(obj, "fgws_mruby_call_fgw: function to be called is not found:"); fgw_async_error(obj, func_name); fgw_async_error(obj, "\n"); return mrb_nil_value(); } mrb_get_args(mrb, "*", &rargv, &argc); if ((argc + 1) > (sizeof(argv_static) / sizeof(argv_static[0]))) argv = malloc((argc + 1) * sizeof(fgw_arg_t)); else argv = argv_static; /* Set the first param */ argv[0].type = FGW_FUNC; argv[0].val.argv0.func = func; argv[0].val.argv0.user_call_ctx = obj->script_user_call_ctx; /* Convert all params to a string */ for (n = 0; n < argc; n++) fgws_mruby_val2arg(ctx, &argv[n+1], mrb_inspect(mrb, rargv[n])); /* Call the target function */ res.type = FGW_PTR; res.val.ptr_void = NULL; err = func->func(&res, argc+1, argv); /* Free the array */ fgw_argv_free(obj->parent, argc+1, argv); if (argv != argv_static) free(argv); if (err != 0) return mrb_nil_value(); fgws_mruby_arg2val(func->obj->parent, ctx, &rres, &res); if (res.type & FGW_DYN) free(res.val.ptr_void); return rres; } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_mruby_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { mruby_ctx_t *ctx = obj->script_data; mrb_define_method(ctx->mrb, ctx->mrb->object_class, name, fgws_mruby_call_fgw, MRB_ARGS_ANY()); } /* API: fgw calls a mruby function */ static fgw_error_t fgws_mruby_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; mruby_ctx_t *ctx = obj->script_data; const char *func_name = argv[0].val.func->name; mrb_value *rargv, rres; int n; mrb_sym rfun = mrb_intern_cstr(ctx->mrb, func_name); rargv = malloc(sizeof(mrb_value) * argc); for(n = 1; n < argc; n++) fgws_mruby_arg2val(obj->parent, ctx, &rargv[n-1], &argv[n]); fgws_ucc_save(obj); rres = mrb_funcall_argv(ctx->mrb, ctx->script, rfun, argc-1, rargv); fgws_ucc_restore(obj); free(rargv); fgws_mruby_val2arg(ctx, res, rres); return 0; } /* API: unload the script */ static int fgws_mruby_unload(fgw_obj_t *obj) { mruby_ctx_t *ctx = obj->script_data; mrb_close(ctx->mrb); free(ctx); return 0; } /* Helper function for the script to register its functions */ static mrb_value fgws_mruby_func_reg(mrb_state *mrb, mrb_value self) { fgw_obj_t *obj = mrb->ud; mruby_ctx_t *ctx = obj->script_data; mrb_value *rargv; int argc; const char *s; char *func_name; fgw_func_t *func; mrb_get_args(mrb, "*", &rargv, &argc); if (argc != 1) { fgw_async_error(obj, "fgws_mruby_func_reg: wrong number of arguments: need 1\n"); return mrb_false_value(); } if (mrb_type(rargv[0]) != MRB_TT_STRING) { fgw_async_error(obj, "fgws_mruby_func_reg: wrong type of arguments: must be string\n"); return mrb_false_value(); } s = mrb_string_value_ptr(ctx->mrb, rargv[0]); if (*s == '"') { /* mruby returns quoted string for some reason */ func_name = fgw_strdup(s+1); func_name[strlen(func_name)-1] = '\0'; } else func_name = (char *)s; func = fgw_func_reg(obj, func_name, fgws_mruby_call_script); if (func == NULL) { fgw_async_error(obj, "fgw_mruby_func_reg: failed to register function: "); fgw_async_error(obj, func_name); fgw_async_error(obj, "\n"); if (func_name != s) free(func_name); return mrb_false_value(); } if (func_name != s) free(func_name); return mrb_true_value(); } /* API: init the interpreter so that functions can be registered */ static int fgws_mruby_init(fgw_obj_t *obj, const char *filename, const char *opts) { mruby_ctx_t *ctx = calloc(sizeof(mruby_ctx_t), 1); ctx->mrb = mrb_open(); ctx->mrb->ud = obj; obj->script_data = ctx; mrb_define_method(ctx->mrb, ctx->mrb->object_class, "fgw_func_reg", fgws_mruby_func_reg, MRB_ARGS_ANY()); return 0; } /* API: load a script into an object */ static int fgws_mruby_load(fgw_obj_t *obj, const char *filename, const char *opts) { mruby_ctx_t *ctx = obj->script_data; FILE *fp; /* Load the file, and execute once */ fp = fopen(filename, "r"); if (fp == NULL) { fgw_async_error(obj, "fgws_mruby_load: filed to open file for reading: "); fgw_async_error(obj, filename); fgw_async_error(obj, "\n"); return -1; } ctx->script = mrb_load_file(ctx->mrb, fp); fclose(fp); #warning TODO: /* if (ruby_errinfo != Qnil) error(1, "[MRuby] Error (%s:%d): %s", ruby_sourcefile, ruby_sourceline, RSTRING(rb_obj_as_string(ruby_errinfo))->ptr);*/ return 0; } static int fgws_mruby_test_parse(const char *filename, FILE *f) { const char *exts[] = {".rb", ".mruby", NULL }; return fgw_test_parse_fn(filename, exts); } /* API: engine registration */ static const fgw_eng_t fgw_mruby_eng = { "mruby", fgws_mruby_call_script, fgws_mruby_init, fgws_mruby_load, fgws_mruby_unload, fgws_mruby_reg_func, NULL, fgws_mruby_test_parse, ".rb" }; int pplg_check_ver_fungw_mruby(int version_we_need) { return 0; } int pplg_init_fungw_mruby(void) { fgw_eng_reg(&fgw_mruby_eng); return 0; } void pplg_uninit_fungw_mruby(void) { fgw_eng_unreg(fgw_mruby_eng.name); } fungw-1.2.0/libfungwbind/c/0000755000175100017510000000000014047742763013752 5ustar svnsvnfungw-1.2.0/libfungwbind/c/Plug.tmpasm0000644000175100017510000000043714032753726016103 0ustar svnsvnput /local/fungw/mod {fungw_c} put /local/fungw/mod_dir {c} switch ?libs/script/c/presents case {true} put /local/fungw/mod_cflags {} put /local/fungw/mod_ldflags {} put /local/fungw/mod_src {fungw_c.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/c/fungw_c.pup0000644000175100017510000000012214034215026016102 0ustar svnsvn$desc helper lib for engines written in C $state works default buildin autoload 0 fungw-1.2.0/libfungwbind/c/fungw_c.c0000644000175100017510000000333614032753233015537 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2017 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ /* The "C engine": helpers for functions implemented in C */ #include #include fgw_error_t fgws_c_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_error_t rv; fgw_func_t *fnc = argv[0].val.func; rv = fnc->func(res, argc, argv); fgw_argv_free(fnc->obj->parent, argc, argv); return rv; } fgw_eng_t fgw_c_eng = { "c", fgws_c_call_script, NULL, NULL, NULL, NULL, NULL, NULL, /* these shall be overwritten by the host app */ ".so" }; /* Dummy; we are not registering a real engine here, it's just a lib so engines can be developed. Still, we need these so puplug can load it. */ int pplg_check_ver_fungw_c(int version_we_need) { return 0; } int pplg_init_fungw_c(void) { return 0; } void pplg_uninit_fungw_c(void) { } fungw-1.2.0/libfungwbind/c/fungw_c.h0000644000175100017510000000024513367075150015545 0ustar svnsvnfgw_error_t fgws_c_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv); extern fgw_eng_t fgw_c_eng; /* the host app needs to overwrite at least the load field */ fungw-1.2.0/libfungwbind/welltype/0000755000175100017510000000000014047742763015375 5ustar svnsvnfungw-1.2.0/libfungwbind/welltype/Plug.tmpasm0000644000175100017510000000056014032275663017522 0ustar svnsvnput /local/fungw/mod {fungw_welltype} put /local/fungw/mod_dir {welltype} switch ?libs/script/welltype/presents case {true} put /local/fungw/mod_cflags libs/script/welltype/cflags put /local/fungw/mod_ldflags libs/script/welltype/ldflags put /local/fungw/mod_src {fungw_welltype.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/welltype/fungw_welltype.c0000644000175100017510000001630514032473547020614 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2017, 2019, 2021 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include #include #include /* Convert fgw type to wt type and push */ static void fgw_wt_push(fgw_ctx_t *fctx, wt_State *lst, fgw_arg_t *arg) { # define FGW_wt_PUSH_LONG(lst, val) wt_pushinteger(lst, val); return; # define FGW_wt_PUSH_DOUBLE(lst, val) wt_pushnumber(lst, val); return; # define FGW_wt_PUSH_PTR(lst, val) wt_pushlightuserdata(lst, val); return; # define FGW_wt_PUSH_STR(lst, val) wt_pushstring(lst, val); return; # define FGW_wt_PUSH_NIL(lst, val) wt_pushnil(lst); return; if (FGW_IS_TYPE_CUSTOM(arg->type)) fgw_arg_conv(fctx, arg, FGW_AUTO); switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(lst, FGW_wt_PUSH_LONG); ARG_CONV_CASE_LLONG(lst, FGW_wt_PUSH_DOUBLE); ARG_CONV_CASE_DOUBLE(lst, FGW_wt_PUSH_DOUBLE); ARG_CONV_CASE_LDOUBLE(lst, FGW_wt_PUSH_DOUBLE); ARG_CONV_CASE_PTR(lst, FGW_wt_PUSH_PTR); ARG_CONV_CASE_STR(lst, FGW_wt_PUSH_STR); ARG_CONV_CASE_CLASS(lst, FGW_wt_PUSH_NIL); ARG_CONV_CASE_INVALID(lst, FGW_wt_PUSH_NIL); } if (arg->type & FGW_PTR) { FGW_wt_PUSH_PTR(lst, arg->val.ptr_void); } else { FGW_wt_PUSH_NIL(lst, 0); } } /* Read the wt stack and convert the result to an fgw arg */ static void fgw_wt_toarg(wt_State *lst, fgw_arg_t *dst, int n) { switch(wt_type(lst, n)) { case wt_TNUMBER: dst->type = FGW_DOUBLE; dst->val.nat_double = wt_tonumber(lst, n); break; case wt_TBOOLEAN: dst->type = FGW_INT; dst->val.nat_int = !!wt_toboolean(lst, n); break; case wt_TSTRING: dst->type = FGW_STR | FGW_DYN; dst->val.str = fgw_strdup(wt_tostring(lst, n)); break; case wt_TLIGHTUSERDATA: dst->type = FGW_PTR; dst->val.ptr_void = wt_touserdata(lst, n); break; case wt_TNIL: default: dst->type = FGW_PTR; dst->val.ptr_void = NULL; break; } } /* API: the script is calling an fgw function */ static int fgws_wt_call_fgw(wt_State *lst) { fgw_obj_t *obj; int argc, n; fgw_arg_t res, *argv, argv_static[16]; fgw_func_t *func; fgw_error_t err; wt_Debug ar; /* Get the current function-name */ wt_getstack(lst, 0, &ar); wt_getinfo(lst, "n", &ar); obj = fgws_wt_get_obj(lst); func = fgw_func_lookup(obj->parent, ar.name); if (func == NULL) return 0; argc = wt_gettop(lst); if ((argc + 1) > (sizeof(argv_static) / sizeof(argv_static[0]))) argv = malloc((argc + 1) * sizeof(fgw_arg_t)); else argv = argv_static; argv[0].type = FGW_FUNC; argv[0].val.argv0.func = func; argv[0].val.argv0.user_call_ctx = obj->script_user_call_ctx; for (n = 1; n < argc; n++) fgw_wt_toarg(lst, &argv[n], n); /* Run command */ res.type = FGW_PTR; res.val.ptr_void = NULL; err = func->func(&res, argc, argv); /* Free the array */ fgw_argv_free(obj->parent, argc, argv); if (argv != argv_static) free(argv); if (err != 0) return 0; fgw_wt_push(obj->parent, lst, &res); if (res.type & FGW_DYN) free(res.val.ptr_void); return 1; } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_wt_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { wt_State *lst = obj->script_data; wt_register(lst, name, fgws_wt_call_fgw); } /* API: fgw calls a wt function */ static fgw_error_t fgws_wt_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; wt_State *lst = obj->script_data; int i; wt_getglobal(lst, argv[0].val.func->name); for (i = 1; i < argc; i++) fgw_wt_push(obj->parent, lst, &argv[i]); fgws_ucc_save(obj); wt_call(lst, argc-1, 1); fgws_ucc_restore(obj); fgw_wt_toarg(lst, res, 1); wt_pop(lst, 1); return FGW_SUCCESS; } /* API: unload the script */ static int fgws_wt_unload(fgw_obj_t *obj) { if (obj->script_data != NULL) wt_close(obj->script_data); obj->script_data = NULL; return 0; } /* Helper function for the script to register its functions */ static int fgws_wt_freg(wt_State *lst) { fgw_obj_t *obj; const char *name; int argc; fgw_func_t *func; obj = fgws_wt_get_obj(lst); argc = wt_gettop(lst); if (argc != 2) { fgw_async_error(obj, "fgw_func_reg: wrong number of arguments: need 2\n"); return 0; } name = wt_tostring(lst, 1); if (name == NULL) { fgw_async_error(obj, "fgw_func_reg: empty name\n"); return 0; } func = fgw_func_reg(obj, name, fgws_wt_call_script); if (func == NULL) { fgw_async_error(obj, "fgw_func_reg: failed to register function\n"); fgw_async_error(obj, name); fgw_async_error(obj, "\n"); return 0; } return 1; } /* API: init the interpreter so that functions can be registered */ static int fgws_wt_init(fgw_obj_t *obj, const char *filename, const char *opts) { wt_State *lst; /* initialize the interpreter */ obj->script_data = lst = wtL_newstate(); if (lst == NULL) { fgw_async_error(obj, "fgws_wt_init: failed to set up the interpreter\n"); return -1; } /* Load default libs */ #if wt_VERSION_NUM < 500 wtopen_base(lst); wtopen_io(lst); wtopen_string(lst); wtopen_math(lst); #else wtL_openlibs(lst); #endif /* add the wt->fgw glue */ wt_register(lst, "fgw_func_reg", fgws_wt_freg); wt_pushlightuserdata(lst, obj); wt_setglobal(lst, wt_OBJ_NAME); return 0; } /* API: load a script into an object */ static int fgws_wt_load(fgw_obj_t *obj, const char *filename, const char *opts) { wt_State *lst = obj->script_data; int res; /* Load the file */ res = wtL_loadfile(lst, filename); if (res != 0) { fgw_async_error(obj, "fgws_wt_load: failed to load the script\n"); wt_close(obj->script_data); obj->script_data = NULL; return -1; } /* Run the main part */ if (wt_pcall(lst, 0, 0, 0) != 0) { fgw_async_error(obj, "fgws_wt_load: failed to execute the script\n"); wt_close(obj->script_data); obj->script_data = NULL; return -2; } return 0; } static int fgws_wt_test_parse(const char *filename, FILE *f) { const char *exts[] = {".wt", NULL }; return fgw_test_parse_fn(filename, exts); } /* API: engine registration */ static const fgw_eng_t fgw_wt_eng = { "wt", fgws_wt_call_script, fgws_wt_init, fgws_wt_load, fgws_wt_unload, fgws_wt_reg_func, NULL, fgws_wt_test_parse, ".wt" }; int pplg_check_ver_fungw_wt(int version_we_need) { return 0; } int pplg_init_fungw_wt(void) { fgw_eng_reg(&fgw_wt_eng); return 0; } void pplg_uninit_fungw_wt(void) { fgw_eng_unreg(fgw_wt_eng.name); } fungw-1.2.0/libfungwbind/duktape/0000755000175100017510000000000014047742763015165 5ustar svnsvnfungw-1.2.0/libfungwbind/duktape/Plug.tmpasm0000644000175100017510000000054713300234127017302 0ustar svnsvnput /local/fungw/mod {fungw_duktape} put /local/fungw/mod_dir {duktape} switch ?libs/script/duktape/presents case {true} put /local/fungw/mod_cflags libs/script/duktape/cflags put /local/fungw/mod_ldflags libs/script/duktape/ldflags put /local/fungw/mod_src {fungw_duktape.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/duktape/fungw_duktape.pup0000644000175100017510000000023613757657675020575 0ustar svnsvn$desc duktape/javascript binding engine $state works $script-ext duktape .js $lang-alias duktape javascript $lang-alias duktape js default buildin autoload 0 fungw-1.2.0/libfungwbind/duktape/fungw_duktape.c0000644000175100017510000002145513757344474020207 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2018, 2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include #include #define FGW_CTX2OBJ_NAME "__fungw_ctx_to_obj__" static fgw_obj_t *fgws_duk_ctx2obj(duk_context *ctx) { fgw_obj_t *res; duk_get_global_string(ctx, FGW_CTX2OBJ_NAME); res = duk_to_pointer(ctx, -1); duk_pop(ctx); return res; } static duk_ret_t fgws_duk_print(duk_context *ctx) { duk_push_string(ctx, " "); duk_insert(ctx, 0); duk_join(ctx, duk_get_top(ctx) - 1); printf("%s\n", duk_safe_to_string(ctx, -1)); return 0; } static void fgws_duk_push_arg(fgw_ctx_t *fctx, duk_context *ctx, fgw_arg_t *arg) { # define FGW_DUK_PUSH_LONG(lst, val) duk_push_int(ctx, val); return; # define FGW_DUK_PUSH_DOUBLE(lst, val) duk_push_number(ctx, val); return; # define FGW_DUK_PUSH_PTR(lst, val) duk_push_pointer(ctx, val); return; # define FGW_DUK_PUSH_STR(lst, val) duk_push_string(ctx, val); return; # define FGW_DUK_PUSH_NIL(lst, val) duk_push_null(ctx); return; if (FGW_IS_TYPE_CUSTOM(arg->type)) fgw_arg_conv(fctx, arg, FGW_AUTO); /* if fails, it remains custom and will be unhandled */ switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(NULL, FGW_DUK_PUSH_LONG); ARG_CONV_CASE_LLONG(NULL, FGW_DUK_PUSH_DOUBLE); ARG_CONV_CASE_DOUBLE(NULL, FGW_DUK_PUSH_DOUBLE); ARG_CONV_CASE_LDOUBLE(NULL, FGW_DUK_PUSH_DOUBLE); ARG_CONV_CASE_PTR(NULL, FGW_DUK_PUSH_PTR); ARG_CONV_CASE_STR(NULL, FGW_DUK_PUSH_STR); ARG_CONV_CASE_CLASS(NULL, FGW_DUK_PUSH_NIL); ARG_CONV_CASE_INVALID(NULL, FGW_DUK_PUSH_NIL); } duk_push_null(ctx); } static void fgws_duk_js2arg(duk_context *ctx, fgw_arg_t *dst, int src_idx) { int type = duk_get_type(ctx, src_idx); switch(type) { case DUK_TYPE_BOOLEAN: dst->type = FGW_INT; dst->val.nat_int = duk_to_boolean(ctx, src_idx); break; case DUK_TYPE_NUMBER: dst->type = FGW_DOUBLE; dst->val.nat_double = duk_to_number(ctx, src_idx); break; case DUK_TYPE_STRING: dst->type = FGW_STR | FGW_DYN; dst->val.str = fgw_strdup(duk_to_string(ctx, src_idx)); break; case DUK_TYPE_BUFFER: dst->type = FGW_STR | FGW_DYN; dst->val.str = fgw_strdup(duk_buffer_to_string(ctx, src_idx)); break; case DUK_TYPE_POINTER: dst->type = FGW_PTR; dst->val.ptr_void = duk_to_pointer(ctx, src_idx); break; case DUK_TYPE_UNDEFINED: case DUK_TYPE_LIGHTFUNC: case DUK_TYPE_OBJECT: /* can't convert these */ fprintf(stderr, "fgws_duk_js2arg: ignoring unconvertable type %d\n", type); case DUK_TYPE_NONE: case DUK_TYPE_NULL: dst->type = FGW_PTR; dst->val.ptr_void = NULL; break; } } /* API: the script is calling an fgw function */ static duk_ret_t fgws_duk_call_fgw(duk_context *ctx) { fgw_func_t *f; int n, argc; fgw_arg_t res, *argv, argv_static[16]; fgw_error_t err; fgw_obj_t *obj = fgws_duk_ctx2obj(ctx); /* figure the fgw function to call */ duk_push_current_function(ctx); duk_get_prop_string(ctx, -1, "fgw_func"); f = duk_to_pointer(ctx, -1); duk_pop_2(ctx); argc = duk_get_top(ctx); if ((argc + 1) > (sizeof(argv_static) / sizeof(argv_static[0]))) argv = malloc((argc + 1) * sizeof(fgw_arg_t)); else argv = argv_static; /* Set the first param */ argv[0].type = FGW_FUNC; argv[0].val.argv0.func = f; argv[0].val.argv0.user_call_ctx = obj->script_user_call_ctx; for(n = 1; n <= argc; n++) fgws_duk_js2arg(ctx, &argv[n], -n); /* Call the target function */ res.type = FGW_PTR; res.val.ptr_void = NULL; err = f->func(&res, argc+1, argv); for(n = 1; n <= argc; n++) fgw_arg_free(obj->parent, &argv[n]); /* Free the array */ fgw_argv_free(f->obj->parent, argc, argv); if (argv != argv_static) free(argv); if (err != 0) { /* return DUK_RET_ERROR; but this aborts */ return 0; } if ((res.type == FGW_PTR) && (res.val.ptr_void == NULL)) return 0; fgws_duk_push_arg(f->obj->parent, ctx, &res); if (res.type & FGW_DYN) free(res.val.ptr_void); return 1; } static int fgws_duk_freg_in_script(duk_context *ctx, const char *name, duk_ret_t (*f)(duk_context *ctx), void *fgw_func) { duk_push_c_function(ctx, f, DUK_VARARGS); duk_push_pointer(ctx, fgw_func); duk_put_prop_string(ctx, -2, "fgw_func"); duk_put_global_string(ctx, name); return 0; } /* API: fgw calls a duk function */ static fgw_error_t fgws_duk_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; duk_context *ctx = obj->script_data; int n; duk_get_global_string(ctx, argv[0].val.func->name); for(n = 1; n < argc; n++) fgws_duk_push_arg(obj->parent, ctx, &argv[n]); fgws_ucc_save(obj); duk_call(ctx, argc-1); fgws_ucc_restore(obj); fgws_duk_js2arg(ctx, res, -1); duk_pop(ctx); /* result */ return 0; } /* Helper function for the script to register its functions */ static int fgws_duk_freg_in_fungw(duk_context *ctx) { const char *fn; int argc = duk_get_top(ctx); fgw_obj_t *obj = fgws_duk_ctx2obj(ctx); fgw_func_t *func; if (argc != 1) { fprintf(stderr, "fgw_func_reg: called with wrong number of arguments (must be 1)\n"); goto error; } fn = duk_get_string(ctx, -1); if (duk_get_global_string(ctx, fn) == 0) { fgw_async_error(obj, "fgw_func_reg: function does not exist:"); fgw_async_error(obj, fn); fgw_async_error(obj, "\n"); goto error; } if (!duk_is_function(ctx, -1)) { fgw_async_error(obj, "fgw_func_reg: not a function: "); fgw_async_error(obj, fn); fgw_async_error(obj, "\n"); goto error; } func = fgw_func_reg(obj, fn, fgws_duk_call_script); if (func == NULL) { fgw_async_error(obj, "fgw_func_reg: failed to register function\n"); fgw_async_error(obj, fn); fgw_async_error(obj, "\n"); return 0; } duk_push_true(ctx); return 1; error:; return 0; } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_duk_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { fgws_duk_freg_in_script(obj->script_data, name, fgws_duk_call_fgw, f); } /* API: unload the script */ static int fgws_duk_unload(fgw_obj_t *obj) { duk_context *ctx = obj->script_data; duk_destroy_heap(ctx); return 0; } /* API: init the interpreter so that functions can be registered */ static int fgws_duk_init(fgw_obj_t *obj, const char *filename, const char *opts) { duk_context *ctx = duk_create_heap_default(); if (ctx == NULL) return -1; obj->script_data = ctx; /* add the duk->fgw glue */ fgws_duk_freg_in_script(ctx, "print", fgws_duk_print, NULL); fgws_duk_freg_in_script(ctx, "fgw_func_reg", fgws_duk_freg_in_fungw, NULL); duk_push_pointer(ctx, obj); duk_put_global_string(ctx, FGW_CTX2OBJ_NAME); return 0; } /* API: load a script into an object */ static int fgws_duk_load(fgw_obj_t *obj, const char *filename, const char *opts) { duk_context *ctx = obj->script_data; FILE *f; char buf[4096]; size_t ret; duk_push_string(ctx, filename); f = fopen(filename, "rb"); if (f == NULL) { fprintf(stderr, "failed to open '%s' for read\n", filename); return -1; } for(;;) { if (ferror(f)) { fclose(f); fprintf(stderr, "ferror when reading file '%s'\n", filename); return -1; } if (feof(f)) break; ret = fread(buf, 1, sizeof(buf), f); if (ret == 0) { break; } duk_push_lstring(ctx, (const char *)buf, ret); } duk_concat(ctx, duk_get_top(ctx) - 1); /* -1 for filename */ duk_insert(ctx, -2); duk_compile(ctx, 0); duk_call(ctx, 0); duk_pop(ctx); /* result */ return 0; } static int fgws_duk_test_parse(const char *filename, FILE *f) { const char *exts[] = {".js", NULL }; return fgw_test_parse_fn(filename, exts); } /* API: engine registration */ static const fgw_eng_t fgw_duk_eng = { "duktape", fgws_duk_call_script, fgws_duk_init, fgws_duk_load, fgws_duk_unload, fgws_duk_reg_func, NULL, fgws_duk_test_parse, ".js" }; int pplg_check_ver_fungw_duktape(int version_we_need) { return 0; } int pplg_init_fungw_duktape(void) { fgw_eng_reg(&fgw_duk_eng); return 0; } void pplg_uninit_fungw_duktape(void) { fgw_eng_unreg(fgw_duk_eng.name); } fungw-1.2.0/libfungwbind/funlisp/0000755000175100017510000000000014047742763015210 5ustar svnsvnfungw-1.2.0/libfungwbind/funlisp/Plug.tmpasm0000644000175100017510000000055213335544723017337 0ustar svnsvnput /local/fungw/mod {fungw_funlisp} put /local/fungw/mod_dir {funlisp} switch ?libs/script/funlisp/presents case {true} put /local/fungw/mod_cflags libs/script/funlisp/cflags put /local/fungw/mod_ldflags libs/script/funlisp/ldflags put /local/fungw/mod_src {fungw_funlisp.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/funlisp/fungw_funlisp.pup0000644000175100017510000000017213757460755020631 0ustar svnsvn$desc funlisp binding engine $state works $script-ext funlisp .fl $script-ext funlisp .funlisp default buildin autoload 0 fungw-1.2.0/libfungwbind/funlisp/fungw_funlisp.c0000644000175100017510000002273013757344474020252 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2018, 2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include #include typedef struct fgws_funlist_fnc_s fgws_funlist_fnc_t; struct fgws_funlist_fnc_s { fgw_obj_t *obj; fgws_funlist_fnc_t *next; char func_name[]; }; typedef struct { lisp_runtime *rt; lisp_scope *scp; fgws_funlist_fnc_t *func_ctxs; } funlisp_ctx_t; /* run the garbage collector to free all temporary objects */ static void fgws_funlisp_gc(funlisp_ctx_t *ctx) { lisp_mark(ctx->rt, (lisp_value*)ctx->scp); lisp_sweep(ctx->rt); } static void fgws_funlisp_val2arg(funlisp_ctx_t *ctx, fgw_arg_t *dst, lisp_value *src) { if (lisp_is(src, type_integer)) { dst->type = FGW_INT; dst->val.nat_int = lisp_integer_get((lisp_integer *)src); } else if (lisp_is(src, type_string)) { dst->type = FGW_STR; dst->val.str = lisp_string_get((lisp_string *)src); } else { dst->type = FGW_PTR; dst->val.ptr_void = NULL; } } static lisp_value *fgws_funlisp_arg2val(fgw_ctx_t *fctx, funlisp_ctx_t *ctx, fgw_arg_t *arg) { # define FGW_RB_SET_LONG(lst, val) return (lisp_value *)lisp_integer_new(ctx->rt, val); # define FGW_RB_SET_DOUBLE(lst, val) return (lisp_value *)lisp_integer_new(ctx->rt, fungw_round(val)); # define FGW_RB_SET_PTR(lst, val) return NULL; # define FGW_RB_SET_STR(lst, val) return (lisp_value *)lisp_string_new(ctx->rt, val, LS_CPY | LS_OWN); # define FGW_RB_SET_NIL(lst, val) return lisp_nil_new(ctx->rt); if (FGW_IS_TYPE_CUSTOM(arg->type)) fgw_arg_conv(fctx, arg, FGW_AUTO); /* if fails, it remains custom and will be unhandled */ switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(NULL, FGW_RB_SET_LONG); ARG_CONV_CASE_LLONG(NULL, FGW_RB_SET_DOUBLE); ARG_CONV_CASE_DOUBLE(NULL, FGW_RB_SET_DOUBLE); ARG_CONV_CASE_LDOUBLE(NULL, FGW_RB_SET_DOUBLE); ARG_CONV_CASE_PTR(NULL, FGW_RB_SET_PTR); ARG_CONV_CASE_STR(NULL, FGW_RB_SET_STR); ARG_CONV_CASE_CLASS(NULL, FGW_RB_SET_NIL); ARG_CONV_CASE_INVALID(NULL, FGW_RB_SET_NIL); } return lisp_nil_new(ctx->rt); } /* API: the script is calling an fgw function */ static lisp_value *fgws_funlisp_call_fgw(lisp_runtime *rt, lisp_scope *scope, lisp_list *arglist, void *user) { fgws_funlist_fnc_t *fctx = user; fgw_obj_t *obj = fctx->obj; funlisp_ctx_t *ctx = obj->script_data; fgw_arg_t res, *argv, argv_static[16]; fgw_func_t *func; int n, argc; lisp_value *lres; fgw_error_t err; lisp_list *a; /* Find our proc based on the function-name */ func = fgw_func_lookup(obj->parent, fctx->func_name); if (func == NULL) { fgw_async_error(obj, "fgws_funlisp_call_fgw: function to be called is not found:"); fgw_async_error(obj, fctx->func_name); fgw_async_error(obj, "\n"); return NULL; } a = arglist; if (a == NULL) { fgw_async_error(obj, "fgws_funlisp_call_fgw: failed to evaluate arguments for function call:"); fgw_async_error(obj, fctx->func_name); fgw_async_error(obj, "\n"); return NULL; } argc = lisp_list_length((lisp_list *)a); if ((argc + 1) > (sizeof(argv_static) / sizeof(argv_static[0]))) argv = malloc((argc + 1) * sizeof(fgw_arg_t)); else argv = argv_static; /* Set the first param */ argv[0].type = FGW_FUNC; argv[0].val.argv0.func = func; argv[0].val.argv0.user_call_ctx = obj->script_user_call_ctx; /* Convert all params to a string */ for (n = 0; n < argc; n++) { fgws_funlisp_val2arg(ctx, &argv[n+1], lisp_list_get_left(a)); a = (lisp_list *)lisp_list_get_right(a); } /* Call the target function */ res.type = FGW_PTR; res.val.ptr_void = NULL; err = func->func(&res, argc+1, argv); /* Free the array */ fgw_argv_free(obj->parent, argc, argv); if (argv != argv_static) free(argv); if (err != 0) return NULL; lres = fgws_funlisp_arg2val(func->obj->parent, ctx, &res); if (res.type & FGW_DYN) free(res.val.ptr_void); return lres; } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_funlisp_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { fgws_funlist_fnc_t *fctx; funlisp_ctx_t *ctx = obj->script_data; int fnlen = strlen(name); fctx = malloc(sizeof(fgws_funlist_fnc_t) + fnlen + 1); fctx->obj = obj; memcpy(fctx->func_name, name, fnlen + 1); fctx->next = ctx->func_ctxs; ctx->func_ctxs = fctx; lisp_scope_add_builtin(ctx->rt, ctx->scp, fctx->func_name, fgws_funlisp_call_fgw, fctx, 1); } /* API: fgw calls a funlisp function */ static fgw_error_t fgws_funlisp_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; funlisp_ctx_t *ctx = obj->script_data; char *func_name = argv[0].val.func->name; lisp_value *rres; int n; lisp_value *function = lisp_scope_lookup_string(ctx->rt, ctx->scp, func_name); lisp_list *largs, *tail; if (function == NULL) return FGW_ERR_NOT_FOUND; largs = tail = (lisp_list *)lisp_nil_new(ctx->rt); for(n = 1; n < argc; n++) lisp_list_append(ctx->rt, &largs, &tail, fgws_funlisp_arg2val(obj->parent, ctx, &argv[n])); fgws_ucc_save(obj); rres = lisp_call(ctx->rt, ctx->scp, function, largs); fgws_ucc_restore(obj); if (rres == NULL) { res->type = FGW_PTR; res->val.ptr_void = NULL; } else fgws_funlisp_val2arg(ctx, res, rres); fgws_funlisp_gc(ctx); return 0; } /* API: unload the script */ static int fgws_funlisp_unload(fgw_obj_t *obj) { funlisp_ctx_t *ctx = obj->script_data; fgws_funlist_fnc_t *fctx, *fnext; lisp_runtime_free(ctx->rt); for(fctx = ctx->func_ctxs; fctx != NULL; fctx = fnext) { fnext = fctx->next; free(fctx); } free(ctx); return 0; } /* Helper function for the script to register its functions */ static lisp_value *fgws_funlisp_func_reg(lisp_runtime *rt, lisp_scope *scope, lisp_list *arglist, void *user) { fgw_obj_t *obj = user; funlisp_ctx_t *ctx = obj->script_data; const char *func_name; fgw_func_t *func; lisp_value *fnv; if (!lisp_get_args(rt, arglist, "S", &fnv) || (!lisp_is(fnv, type_string))) { fgw_async_error(obj, "fgws_funlisp_func_reg: wrong arguments: need one string (function name)\n"); return NULL; } func_name = lisp_string_get((lisp_string *)fnv); func = fgw_func_reg(obj, func_name, fgws_funlisp_call_script); if (func == NULL) { fgw_async_error(obj, "fgw_funlisp_func_reg: failed to register function: "); fgw_async_error(obj, func_name); fgw_async_error(obj, "\n"); return NULL; } return (lisp_value *)lisp_integer_new(ctx->rt, 1); } /* API: init the interpreter so that functions can be registered */ static int fgws_funlisp_init(fgw_obj_t *obj, const char *filename, const char *opts) { funlisp_ctx_t *ctx = calloc(sizeof(funlisp_ctx_t), 1); ctx->rt = lisp_runtime_new(); lisp_enable_symcache(ctx->rt); lisp_enable_strcache(ctx->rt); ctx->scp = lisp_new_default_scope(ctx->rt); obj->script_data = ctx; lisp_scope_add_builtin(ctx->rt, ctx->scp, "fgw_func_reg", fgws_funlisp_func_reg, obj, 1); return 0; } /* API: load a script into an object */ static int fgws_funlisp_load(fgw_obj_t *obj, const char *filename, const char *opts) { funlisp_ctx_t *ctx = obj->script_data; FILE *fp; lisp_value *result; /* Load the file, and execute once */ fp = fopen(filename, "r"); if (fp == NULL) { fgw_async_error(obj, "fgws_funlisp_load: filed to open file for reading: "); fgw_async_error(obj, filename); fgw_async_error(obj, "\n"); return -1; } lisp_load_file(ctx->rt, ctx->scp, fp); fclose(fp); if (lisp_get_error(ctx->rt)) { #warning TODO: error line /* char tmp[128]; sprintf(tmp, "fgws_funlisp_load: filed in lisp_load_file() around line %d: ", ctx->rt->error_line) fgw_async_error(obj, tmp);*/ fgw_async_error(obj, "fgws_funlisp_load: filed in lisp_load_file(): "); fgw_async_error(obj, lisp_get_error(ctx->rt)); fgw_async_error(obj, "\n"); return -1; } result = lisp_run_main_if_exists(ctx->rt, ctx->scp, 0, NULL); if (!result) { #warning TODO: error line fgw_async_error(obj, "fgws_funlisp_load: filed to run main: "); fgw_async_error(obj, lisp_get_error(ctx->rt)); fgw_async_error(obj, "\n"); return -1; } fgws_funlisp_gc(ctx); return 0; } static int fgws_funlisp_test_parse(const char *filename, FILE *f) { const char *exts[] = {".fl", ".funlisp", NULL }; return fgw_test_parse_fn(filename, exts); } /* API: engine registration */ static const fgw_eng_t fgw_funlisp_eng = { "funlisp", fgws_funlisp_call_script, fgws_funlisp_init, fgws_funlisp_load, fgws_funlisp_unload, fgws_funlisp_reg_func, NULL, fgws_funlisp_test_parse, ".fl" }; int pplg_check_ver_fungw_funlisp(int version_we_need) { return 0; } int pplg_init_fungw_funlisp(void) { fgw_eng_reg(&fgw_funlisp_eng); return 0; } void pplg_uninit_fungw_funlisp(void) { fgw_eng_unreg(fgw_funlisp_eng.name); } fungw-1.2.0/libfungwbind/Disable.tmpasm0000644000175100017510000000045213561531473016311 0ustar svnsvnappend /local/fungw/summary [~ ~/local/fungw/mod~::d~] put /local/fungw/mod_mak [~~/local/fungw/mod_dir~/~/local/fungw/mod~.mak~] print [~ # binding not configured all: clean: distclean: install: linstall: uninstall: ~] redir /local/fungw/mod_mak print [~ # binding not configured ~] fungw-1.2.0/libfungwbind/tcl/0000755000175100017510000000000014047742763014312 5ustar svnsvnfungw-1.2.0/libfungwbind/tcl/Plug.tmpasm0000644000175100017510000000052213110722271016421 0ustar svnsvnput /local/fungw/mod {fungw_tcl} put /local/fungw/mod_dir {tcl} switch ?libs/script/tcl/presents case {true} put /local/fungw/mod_cflags libs/script/tcl/cflags put /local/fungw/mod_ldflags libs/script/tcl/ldflags put /local/fungw/mod_src {fungw_tcl.c} include {Plugin.tmpasm} end default include {Disable.tmpasm} end end fungw-1.2.0/libfungwbind/tcl/fungw_tcl.pup0000644000175100017510000000012613757460755017034 0ustar svnsvn$desc tcl binding engine $state works $script-ext tcl .tcl default buildin autoload 0 fungw-1.2.0/libfungwbind/tcl/fungw_tcl.c0000644000175100017510000001277013757344474016461 0ustar svnsvn/* fungw - language-agnostic function gateway Copyright (C) 2017, 2019 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/fungw Version control: svn://repo.hu/fungw/trunk */ #include #include #include #include #include static int fgws_tcl_test_parse(const char *filename, FILE *f) { const char *exts[] = {".tcl", NULL}; return fgw_test_parse_fn(filename, exts); } static void fgws_tcl_err(fgw_obj_t *obj) { Tcl_Obj *res; res = Tcl_GetObjResult(obj->script_data); fgw_async_error(obj, res->bytes); } /* API: the script is calling an fgw function */ static int fgws_tcl_call_fgw(ClientData dat, Tcl_Interp *interp, int argc, char *argv[]) { fgw_obj_t *obj = dat; fgw_arg_t res, *sarg, sarg_static[128]; int n; fgw_func_t *func; fgw_error_t err; func = fgw_func_lookup(obj->parent, argv[0]); if (func == NULL) return 0; /* alloc arguments */ if ((argc+1) > (sizeof(sarg_static) / sizeof(sarg_static[0]))) sarg = malloc((argc+1) * sizeof(fgw_arg_t *)); else sarg = sarg_static; /* set up arguments */ sarg[0].type = FGW_FUNC; sarg[0].val.argv0.func = func; sarg[0].val.argv0.user_call_ctx = obj->script_user_call_ctx; for(n = 1; n < argc; n++) { sarg[n].type = FGW_STR; sarg[n].val.str = argv[n]; } /* Run command */ res.type = FGW_PTR; res.val.ptr_void = NULL; err = func->func(&res, argc, sarg); /* no need to free argv - all static strings */ if (sarg != sarg_static) free(sarg); Tcl_ResetResult(obj->script_data); if (err == 0) { fgw_arg_conv(obj->parent, &res, FGW_STR | FGW_DYN); Tcl_AppendResult(obj->script_data, res.val.str, NULL); free(res.val.str); } return err ? -1 : 0; } /* API: register an fgw function in the script, make the function visible/callable */ static void fgws_tcl_reg_func(fgw_obj_t *obj, const char *name, fgw_func_t *f) { Tcl_CreateCommand(obj->script_data, name, (Tcl_CmdProc *)fgws_tcl_call_fgw, (ClientData)obj, (Tcl_CmdDeleteProc *)NULL); } /* API: fgw calls a tcl function */ static fgw_error_t fgws_tcl_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_obj_t *obj = argv[0].val.func->obj; int n, evr; fgw_error_t ret = FGW_SUCCESS; char *s; const char **sarg, *sarg_static[128]; if ((argc+1) > (sizeof(sarg_static) / sizeof(sarg_static[0]))) sarg = malloc((argc+1) * sizeof(char *)); else sarg = sarg_static; sarg[0] = argv[0].val.func->name; for(n = 1; n < argc; n++) { fgw_arg_conv(obj->parent, &argv[n], FGW_STR); sarg[n] = argv[n].val.str; } s = Tcl_Merge(argc, sarg); fgws_ucc_save(obj); evr = Tcl_Eval(obj->script_data, s); fgws_ucc_restore(obj); if (evr != TCL_OK) { fgws_tcl_err(obj); ret = FGW_ERR_UNKNOWN; } Tcl_Free(s); res->type = FGW_STR; res->val.str = (char *)Tcl_GetStringResult(obj->script_data); fgw_argv_free(obj->parent, argc, argv); if (sarg != sarg_static) free(sarg); return ret; } /* API: unload the script */ static int fgws_tcl_unload(fgw_obj_t *obj) { Tcl_DeleteInterp(obj->script_data); return 0; } /* Helper function for the script to register its functions */ static int fgws_tcl_freg(ClientData dat, Tcl_Interp *interp, int argc, char *argv[]) { fgw_obj_t *obj = dat; fgw_func_t *func; if (argc != 2) { fgw_async_error(obj, "fgw_func_reg: wrong number of arguments: need 1\n"); return -1; } func = fgw_func_reg(obj, argv[1], fgws_tcl_call_script); if (func == NULL) { fgw_async_error(obj, "fgw_func_reg: failed to register function\n"); fgw_async_error(obj, argv[1]); fgw_async_error(obj, "\n"); return -1; } return 0; } /* API: init the interpreter so that functions can be registered */ static int fgws_tcl_init(fgw_obj_t *obj, const char *filename, const char *opts) { Tcl_FindExecutable(""); obj->script_data = Tcl_CreateInterp(); if (Tcl_Init(obj->script_data) == TCL_ERROR) { fgw_async_error(obj, "fgws_tcl_init: failed to create an interpreter\n"); return -1; } Tcl_CreateCommand(obj->script_data, "fgw_func_reg", (Tcl_CmdProc *)fgws_tcl_freg, (ClientData)obj, (Tcl_CmdDeleteProc *)NULL); return 0; } /* API: load a script into an object */ static int fgws_tcl_load(fgw_obj_t *obj, const char *filename, const char *opts) { /* Read the file */ if (Tcl_EvalFile(obj->script_data, filename) == TCL_OK) return 0; fgw_async_error(obj, "fgws_tcl_load: failed to eval the file\n"); fgws_tcl_err(obj); return -1; } /* API: engine registration */ static const fgw_eng_t fgw_tcl_eng = { "tcl", fgws_tcl_call_script, fgws_tcl_init, fgws_tcl_load, fgws_tcl_unload, fgws_tcl_reg_func, NULL, fgws_tcl_test_parse, ".tcl" }; int pplg_check_ver_fungw_tcl(int version_we_need) { return 0; } int pplg_init_fungw_tcl(void) { fgw_eng_reg(&fgw_tcl_eng); return 0; } void pplg_uninit_fungw_tcl(void) { fgw_eng_unreg(fgw_tcl_eng.name); } fungw-1.2.0/INSTALL0000644000175100017510000000157013561534656012112 0ustar svnsvn0. Requirements - C compiler (that supports C89) - make (does not have to be GNU) - optional: scripting language libs, see doc/lang.html 1. configure Run ./configure You may want to specify the prefix, e.g. --prefix=/usr ./configure will automatically detect and enable scripting with libs found on the system. There is no --with or --without arguments. If a language is not enabled, the lib is not found or is not suitable (check the version) 2. compile Run make 3. test At the moment there's no automated testing. The most common thing to test is scripts. For that, before installation: cd example SCRIPT=hello.fawk ./test_script The script name determines the language used. All script output shall be the same, except for the 'Set up' line that differs in language and file name. 4. installation Run make install For packaging, DESTDIR is supported. fungw-1.2.0/COPYING0000644000175100017510000006364213105510756012111 0ustar svnsvn GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! fungw-1.2.0/src_3rd/0000755000175100017510000000000014047742763012415 5ustar svnsvnfungw-1.2.0/src_3rd/genht/0000755000175100017510000000000014047742763013522 5ustar svnsvnfungw-1.2.0/src_3rd/genht/htip.c0000644000175100017510000000020612752011310014602 0ustar svnsvn#include "htip.h" #define HT(x) htip_ ## x #include "ht.c" int htip_keyeq(htip_key_t a, htip_key_t b) { return a == b; } #undef HT fungw-1.2.0/src_3rd/genht/AUTHORS0000644000175100017510000000033013365754613014565 0ustar svnsvnOriginal author, hash table code: Szabolcs Nagy Contribution on the build system, maintenance: Tibor 'Igor2' Palinkas Project page: http://repo.hu/projects/genht Contact: http://repo.hu/projects/genht/contact.html fungw-1.2.0/src_3rd/genht/htss.c0000644000175100017510000000010511436423231014624 0ustar svnsvn#include "htss.h" #define HT(x) htss_ ## x #include "ht.c" #undef HT fungw-1.2.0/src_3rd/genht/htpp.c0000644000175100017510000000010512674664564014643 0ustar svnsvn#include "htpp.h" #define HT(x) htpp_ ## x #include "ht.c" #undef HT fungw-1.2.0/src_3rd/genht/version.h.in0000644000175100017510000000100713156407367015761 0ustar svnsvn#ifndef GENHT_VERSION_H #define GENHT_VERSION_H /* API version: MAJOR changes only on incompatible API change */ #define GENHT_APIVER_MAJOR _VER_MAJOR_ /* API version: MAJOR changes only on compatible API change (additions) */ #define GENHT_APIVER_MINOR _VER_MINOR_ /* Code version as numbers */ #define GENHT_VER_MAJOR GENHT_APIVER_MAJOR #define GENHT_VER_MINOR GENHT_APIVER_MINOR #define GENHT_VER_PATCH _VER_PATCH_ /* Code version as string */ #define GENHT_VER_STR "_VER_MAJOR_._VER_MINOR_._VER_PATCH_" #endif fungw-1.2.0/src_3rd/genht/htpi.h0000644000175100017510000000033112767241764014636 0ustar svnsvn#ifndef GENHT_HTPI_H #define GENHT_HTPI_H #define HT_HAS_CONST_KEY typedef void *htpi_key_t; typedef const void *htpi_const_key_t; typedef int htpi_value_t; #define HT(x) htpi_ ## x #include "ht.h" #undef HT #endif fungw-1.2.0/src_3rd/genht/mainsi.ref0000644000175100017510000000004213767041637015474 0ustar svnsvna 1 qw 4 asdf -3 v 5 x 7 y 8 df 6 fungw-1.2.0/src_3rd/genht/ht.h0000644000175100017510000000631713567452141014307 0ustar svnsvn/* open addressing hash table */ /* max size is 1 << 31 */ /* an entry pointer is valid until the next insertion or resize */ /* typedef void *HT(key_t); typedef void *HT(value_t); Plus optionally, for key const correctness: typedef void *HT(const key_t); #define HT_HAS_CONST_KEY */ #ifndef HT_HAS_CONST_KEY typedef HT(key_t) HT(const_key_t); #else # undef HT_HAS_CONST_KEY #endif typedef struct { int flag; unsigned int hash; HT(key_t) key; HT(value_t) value; } HT(entry_t); typedef struct { unsigned int mask; unsigned int fill; unsigned int used; HT(entry_t) *table; unsigned int (*keyhash)(HT(const_key_t)); int (*keyeq)(HT(const_key_t), HT(const_key_t)); #ifdef GENHT_USER_FIELDS GENHT_USER_FIELDS #endif } HT(t); /* allocates a new hash table, but the function may return null-pointer */ HT(t) *HT(alloc)(unsigned int (*keyhash)(HT(const_key_t)), int (*keyeq)(HT(const_key_t), HT(const_key_t))); /* returns 0 on success */ int HT(init)(HT(t) *ht, unsigned int (*keyhash)(HT(const_key_t)), int (*keyeq)(HT(const_key_t), HT(const_key_t))); void HT(free)(HT(t) *ht); void HT(uninit)(HT(t) *ht); void HT(clear)(HT(t) *ht); HT(t) *HT(copy)(const HT(t) *ht); /* new size is 2^n >= hint, returns 0 on success */ int HT(resize)(HT(t) *ht, unsigned int hint); /* ht[key] is used */ int HT(has)(HT(t) *ht, HT(const_key_t) key); /* value of ht[key] or 0 if key is not used */ HT(value_t) HT(get)(HT(t) *ht, HT(const_key_t) key); /* entry of ht[key] or NULL if key is not used */ HT(entry_t) *HT(getentry)(HT(t) *ht, HT(const_key_t) key); /* ht[key] = value */ void HT(set)(HT(t) *ht, HT(key_t) key, HT(value_t) value); /* if key is used then return ht[key] else ht[key] = value and return NULL */ /* (the value of the returned used entry can be modified) */ HT(entry_t) *HT(insert)(HT(t) *ht, HT(key_t) key, HT(value_t) value); /* delete key and return ht[key] or 0 if key is not used */ HT(value_t) HT(pop)(HT(t) *ht, HT(const_key_t) key); /* delete key and return ht[key] or NULL if key is not used */ /* (the returned deleted entry can be used to free key,value resources) */ HT(entry_t) *HT(popentry)(HT(t) *ht, HT(const_key_t) key); /* delete entry (useful for destructive iteration) */ void HT(delentry)(HT(t) *ht, HT(entry_t) *entry); /* User application can override malloc/realloc/free by defining these macros: */ #ifndef genht_malloc #define genht_malloc(ht, size) malloc(size) #endif #ifndef genht_calloc #define genht_calloc(ht, size1, size2) calloc(size1, size2) #endif #ifndef genht_realloc #define genht_realloc(ht, ptr, size) realloc(ptr, size) #endif #ifndef genht_free #define genht_free(ht, ptr) free(ptr) #endif #ifdef GENHT_WANT_INLINE # define GENHT_STATIC static # define GENHT_INLINE inline # include "ht_inlines.h" #else /* helper functions */ unsigned int HT(length)(const HT(t) *ht); unsigned int HT(fill)(const HT(t) *ht); unsigned int HT(size)(const HT(t) *ht); /* for any entry exactly one returns true */ int HT(isused)(const HT(entry_t) *entry); int HT(isempty)(const HT(entry_t) *entry); int HT(isdeleted)(const HT(entry_t) *entry); /* first used (useful for iteration) */ HT(entry_t) *HT(first)(const HT(t) *ht); /* next used (useful for iteration) */ HT(entry_t) *HT(next)(const HT(t) *ht, HT(entry_t) *entry); #endif fungw-1.2.0/src_3rd/genht/htsi.c0000644000175100017510000000010511436423764014625 0ustar svnsvn#include "htsi.h" #define HT(x) htsi_ ## x #include "ht.c" #undef HT fungw-1.2.0/src_3rd/genht/siphash24.c0000644000175100017510000000375312561641630015471 0ustar svnsvn/* based on SipHash reference C implementation (CC0) */ #include #include #define cROUNDS 2 #define dROUNDS 4 #define ROTL(x,b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) ) #define U8TO64_LE(p) \ (((uint64_t)((p)[0]) ) | \ ((uint64_t)((p)[1]) << 8) | \ ((uint64_t)((p)[2]) << 16) | \ ((uint64_t)((p)[3]) << 24) | \ ((uint64_t)((p)[4]) << 32) | \ ((uint64_t)((p)[5]) << 40) | \ ((uint64_t)((p)[6]) << 48) | \ ((uint64_t)((p)[7]) << 56)) #define SIPROUND \ do { \ v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \ v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \ v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \ v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \ } while(0) uint64_t siphash(const uint8_t *in, size_t len, const uint8_t *k) { /* "somepseudorandomlygeneratedbytes" */ uint64_t v0 = 0x736f6d6570736575ULL; uint64_t v1 = 0x646f72616e646f6dULL; uint64_t v2 = 0x6c7967656e657261ULL; uint64_t v3 = 0x7465646279746573ULL; uint64_t b; uint64_t k0 = U8TO64_LE( k ); uint64_t k1 = U8TO64_LE( k + 8 ); uint64_t m; int i; const uint8_t *end = in + len - len%8; const int left = len%8; b = (uint64_t)len << 56; v3 ^= k1; v2 ^= k0; v1 ^= k1; v0 ^= k0; for ( ; in != end; in += 8 ) { m = U8TO64_LE( in ); v3 ^= m; for( i=0; ikey); free(htent->value); }); */ #define genht_uninit_deep(type, ht, uninit_code) \ do { \ type ## _entry_t *htent; \ for(htent = type ## _first(ht); htent != NULL; htent = type ## _next(ht, htent)) { uninit_code; } \ type ## _uninit(ht); \ } while(0) #endif fungw-1.2.0/src_3rd/genht/htpi.c0000644000175100017510000000010512674664564014634 0ustar svnsvn#include "htpi.h" #define HT(x) htpi_ ## x #include "ht.c" #undef HT fungw-1.2.0/src_3rd/genht/ht.c0000644000175100017510000001327113567452141014277 0ustar svnsvn#include #ifdef GENHT_WANT_INLINE # define GENHT_INLINE inline # define GENHT_STATIC static #else /* make sure inline and static are empty so all calls become linkable functions */ # define GENHT_INLINE # define GENHT_STATIC # include "ht_inlines.h" #endif #ifndef HT_INVALID_VALUE #define HT_INVALID_VALUE 0 #endif #define HT_MINSIZE 8 #define HT_MAXSIZE (1U << 31) #define JUMP(i, j) i += j++ #define JUMP_FIRST(i, j) j = 1, i += j++ /* generic functions, useful if ht_entry_t changes */ static GENHT_INLINE void setused(HT(entry_t) *entry) { entry->flag = 1; } static GENHT_INLINE void setdeleted(HT(entry_t) *entry) { entry->flag = -1; } static GENHT_INLINE unsigned int entryhash(const HT(entry_t) *entry) { return entry->hash; } int HT(init)(HT(t) *ht, unsigned int (*keyhash)(HT(const_key_t)), int (*keyeq)(HT(const_key_t), HT(const_key_t))) { ht->mask = HT_MINSIZE - 1; ht->fill = 0; ht->used = 0; ht->table = genht_calloc(ht, ht->mask + 1, sizeof(HT(entry_t))); if (!ht->table) return -1; ht->keyhash = keyhash; ht->keyeq = keyeq; return 0; } void HT(uninit)(HT(t) *ht) { genht_free(ht, ht->table); ht->table = NULL; } HT(t) *HT(alloc)(unsigned int (*keyhash)(HT(const_key_t)), int (*keyeq)(HT(const_key_t), HT(const_key_t))) { HT(t) *ht = genht_malloc(NULL, sizeof(HT(t))); if (!ht) return 0; HT(init)(ht, keyhash, keyeq); return ht; } void HT(clear)(HT(t) *ht) { HT(uninit)(ht); HT(init)(ht, ht->keyhash, ht->keyeq); } void HT(free)(HT(t) *ht) { HT(uninit)(ht); genht_free(NULL, ht); } /* one lookup function to rule them all */ static HT(entry_t) *lookup(HT(t) *ht, HT(const_key_t) key, unsigned int hash) { unsigned int mask = ht->mask; unsigned int i = hash; unsigned int j; HT(entry_t) *table = ht->table; HT(entry_t) *entry = table + (i & mask); HT(entry_t) *free_entry; /* first deleted entry for insert */ if (HT(isempty)(entry)) return entry; else if (HT(isdeleted)(entry)) free_entry = entry; else if (entryhash(entry) == hash && ht->keyeq(entry->key, key)) return entry; else free_entry = NULL; for (JUMP_FIRST(i, j); ; JUMP(i, j)) { entry = table + (i & mask); if (HT(isempty)(entry)) return (free_entry == NULL) ? entry : free_entry; else if (HT(isdeleted)(entry)) { if (free_entry == NULL) free_entry = entry; } else if (entryhash(entry) == hash && ht->keyeq(entry->key, key)) return entry; } } /* for copy and resize: no deleted entries in ht, the lookedup key is not in ht */ static HT(entry_t) *cleanlookup(HT(t) *ht, unsigned int hash) { unsigned int mask = ht->mask; unsigned int i = hash; unsigned int j; HT(entry_t) *table = ht->table; HT(entry_t) *entry = table + (i & mask); if (HT(isempty)(entry)) return entry; for (JUMP_FIRST(i, j); ; JUMP(i, j)) { entry = table + (i & mask); if (HT(isempty)(entry)) return entry; } } HT(t) *HT(copy)(const HT(t) *ht) { HT(t) *newht; HT(entry_t) *entry; unsigned int used = ht->used; newht = genht_malloc(NULL, sizeof(HT(t))); if (!newht) return 0; *newht = *ht; newht->fill = used; newht->table = genht_calloc(ht, newht->mask + 1, sizeof(HT(entry_t))); if (!newht->table) { genht_free(NULL, newht); return 0; } for (entry = ht->table; used > 0; entry++) if (HT(isused)(entry)) { used--; *cleanlookup(newht, entryhash(entry)) = *entry; } return newht; } int HT(resize)(HT(t) *ht, unsigned int hint) { unsigned int newsize; unsigned int used = ht->used; HT(entry_t) *oldtable = ht->table; HT(entry_t) *entry; if (hint < used << 1) hint = used << 1; if (hint > HT_MAXSIZE) hint = HT_MAXSIZE; for (newsize = HT_MINSIZE; newsize < hint; newsize <<= 1); ht->table = genht_calloc(ht, newsize, sizeof(HT(entry_t))); if (!ht->table) { ht->table = oldtable; return -1; } ht->mask = newsize - 1; ht->fill = ht->used; for (entry = oldtable; used > 0; entry++) if (HT(isused)(entry)) { used--; *cleanlookup(ht, entryhash(entry)) = *entry; } genht_free(ht, oldtable); return 0; } int HT(has)(HT(t) *ht, HT(const_key_t) key) { HT(entry_t) *entry = lookup(ht, key, ht->keyhash(key)); return HT(isused)(entry); } HT(value_t) HT(get)(HT(t) *ht, HT(const_key_t) key) { HT(entry_t) *entry = lookup(ht, key, ht->keyhash(key)); return HT(isused)(entry) ? entry->value : HT_INVALID_VALUE; } HT(entry_t) *HT(getentry)(HT(t) *ht, HT(const_key_t) key) { HT(entry_t) *entry = lookup(ht, key, ht->keyhash(key)); return HT(isused)(entry) ? entry : NULL; } /* fill threshold = 3/4 */ static GENHT_INLINE void checkfill(HT(t) *ht) { if (ht->fill > ht->mask - (ht->mask >> 2) || ht->fill > ht->used << 2) HT(resize)(ht, ht->used << (ht->used > 1 << 16 ? 1 : 2)); } HT(entry_t) *HT(insert)(HT(t) *ht, HT(key_t) key, HT(value_t) value) { unsigned int hash = ht->keyhash(key); HT(entry_t) *entry = lookup(ht, key, hash); if (HT(isused)(entry)) return entry; if (HT(isempty)(entry)) ht->fill++; ht->used++; entry->hash = hash; entry->key = key; entry->value = value; setused(entry); checkfill(ht); return NULL; } void HT(set)(HT(t) *ht, HT(key_t) key, HT(value_t) value) { HT(entry_t) *entry = HT(insert)(ht, key, value); if (entry) entry->value = value; } HT(value_t) HT(pop)(HT(t) *ht, HT(const_key_t) key) { HT(entry_t) *entry = lookup(ht, key, ht->keyhash(key)); HT(value_t) v; if (!HT(isused)(entry)) return HT_INVALID_VALUE; ht->used--; v = entry->value; setdeleted(entry); return v; } HT(entry_t) *HT(popentry)(HT(t) *ht, HT(const_key_t) key) { HT(entry_t) *entry = lookup(ht, key, ht->keyhash(key)); if (HT(isused)(entry)) { ht->used--; setdeleted(entry); return entry; } return NULL; } void HT(delentry)(HT(t) *ht, HT(entry_t) *entry) { if (!HT(isused)(entry)) return; ht->used--; setdeleted(entry); } #undef HT_INVALID_VALUE fungw-1.2.0/src_3rd/genht/htsi.h0000644000175100017510000000033112775757472014650 0ustar svnsvn#ifndef GENHT_HTSI_H #define GENHT_HTSI_H #define HT_HAS_CONST_KEY typedef char *htsi_key_t; typedef const char *htsi_const_key_t; typedef int htsi_value_t; #define HT(x) htsi_ ## x #include "ht.h" #undef HT #endif fungw-1.2.0/src_3rd/genht/htip.h0000644000175100017510000000031412752010623014615 0ustar svnsvn#ifndef GENHT_HTIP_H #define GENHT_HTIP_H typedef long int htip_key_t; typedef void *htip_value_t; #define HT(x) htip_ ## x #include "ht.h" #undef HT int htip_keyeq(htip_key_t a, htip_key_t b); #endif fungw-1.2.0/src_3rd/genht/LICENSE0000644000175100017510000000034613124401260014505 0ustar svnsvnPublic Domain. genht was written by Szabolcs Nagy in 2010. The whole project, including all source files are placed in the Public Domain by the author. There is absolutely no restriction on use or distribution of these files. fungw-1.2.0/src_3rd/genht/mainsi.c0000644000175100017510000000252711436424405015141 0ustar svnsvn#include #include #include #include #include "htsi.h" static unsigned int keyhash(char *key) { unsigned char *p = (unsigned char *)key; unsigned int hash = 0; while (*p) hash += (hash << 2) + *p++; return hash; } static int keyeq(char *a, char *b) { char *pa = (char *)a; char *pb = (char *)b; for (; *pa == *pb; pa++, pb++) if (*pa == '\0') return 1; return 0; } int main() { htsi_t *ht; htsi_entry_t *e; ht = htsi_alloc(keyhash, keyeq); htsi_set(ht, "a", 1); htsi_set(ht, "b", 2); htsi_set(ht, "asdf", -3); htsi_set(ht, "qw", 4); htsi_set(ht, "v", 5); htsi_set(ht, "df", 6); htsi_set(ht, "x", 7); if (!htsi_has(ht, "a")) puts("ERR: has a"); if (htsi_has(ht, "1")) puts("ERR: has 1"); if (htsi_insert(ht, "y", 8)) puts("ERR: insert y"); if (htsi_insert(ht, "x", 9)->value != 7) puts("ERR: insert x"); if (htsi_pop(ht, "b") != 2 || htsi_getentry(ht, "b")) puts("ERR: pop b"); if (htsi_popentry(ht, "b")) puts("ERR: pope b"); if (htsi_popentry(ht, "c")) puts("ERR: pope c"); for (e = htsi_first(ht); e; e = htsi_next(ht, e)) { if (htsi_get(ht, e->key) != e->value) printf("ERR %s %d\n", e->key, e->value); printf("%s %d\n", e->key, e->value); } htsi_clear(ht); for (e = htsi_first(ht); e; e = htsi_next(ht, e)) puts("ERR: clear"); htsi_free(ht); return 0; } fungw-1.2.0/src_3rd/genht/htss.h0000644000175100017510000000033312767241764014655 0ustar svnsvn#ifndef GENHT_HTSS_H #define GENHT_HTSS_H #define HT_HAS_CONST_KEY typedef char *htss_key_t; typedef const char *htss_const_key_t; typedef char *htss_value_t; #define HT(x) htss_ ## x #include "ht.h" #undef HT #endif fungw-1.2.0/src_3rd/genht/siphash24.h0000644000175100017510000000026312561641630015467 0ustar svnsvn#include #include /* hash (in,len) using 16byte secret k so collision attacks are hard */ uint64_t siphash(const uint8_t *in, size_t len, const uint8_t *k); fungw-1.2.0/src_3rd/genht/htpp.h0000644000175100017510000000033312767241764014647 0ustar svnsvn#ifndef GENHT_HTPP_H #define GENHT_HTPP_H #define HT_HAS_CONST_KEY typedef void *htpp_key_t; typedef const void *htpp_const_key_t; typedef void *htpp_value_t; #define HT(x) htpp_ ## x #include "ht.h" #undef HT #endif fungw-1.2.0/src_3rd/genht/hash.c0000644000175100017510000000657413514013363014606 0ustar svnsvn#include #include /* assumes sizeof(unsigned)==4 */ #define rot(x, k) (((x)<<(k)) ^ ((x)>>(32-(k)))) /* mix 3 32 bit values reversibly */ #define mix(a, b, c) { \ a -= c; a ^= rot(c, 4); c += b; \ b -= a; b ^= rot(a, 6); a += c; \ c -= b; c ^= rot(b, 8); b += a; \ a -= c; a ^= rot(c, 16); c += b; \ b -= a; b ^= rot(a, 19); a += c; \ c -= b; c ^= rot(b, 4); b += a; \ } /* final mixing of 3 32-bit values (a, b, c) into c */ #define final(a, b, c) { \ c ^= b; c -= rot(b, 14); \ a ^= c; a -= rot(c, 11); \ b ^= a; b -= rot(a, 25); \ c ^= b; c -= rot(b, 16); \ a ^= c; a -= rot(c, 4); \ b ^= a; b -= rot(a, 14); \ c ^= b; c -= rot(b, 24); \ } #define SEED 0x9e3779b9 int genht_strcasecmp(const char *s1, const char *s2) { for(; (*s1 != '\0') && (*s2 != '\0') && ((*s1 == *s2) || (tolower(*s1) == tolower(*s2))); s1++, s2++); return tolower(*s1) - tolower(*s2); } /* not for strings: does unaligned access and reads past the end of key */ /* bob jenkins: lookup 3 */ unsigned jenhash(const void *key, unsigned len) { unsigned a, b, c; const unsigned *k = (const unsigned *)key; a = b = c = SEED; while (len > 12) { a += *k++; b += *k++; c += *k++; mix(a, b, c) len -= 12; } switch (len) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=k[1]&0xffffff; a+=k[0]; break; case 6 : b+=k[1]&0xffff; a+=k[0]; break; case 5 : b+=k[1]&0xff; a+=k[0]; break; case 4 : a+=k[0]; break; case 3 : a+=k[0]&0xffffff; break; case 2 : a+=k[0]&0xffff; break; case 1 : a+=k[0]&0xff; break; } final(a, b, c) return c; } unsigned jenhash32(unsigned k) { unsigned a, b, c; a = b = c = SEED; a += k; final(a, b, c) return c; } /* austin appleby: murmur 2 */ unsigned murmurhash(const void *key, unsigned len) { enum { m = 0x5bd1e995, r = 24 }; unsigned h = SEED ^ len; const unsigned char *data = (const unsigned char *)key; while(len >= 4) { unsigned k = *(unsigned *)data; k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; data += 4; len -= 4; } switch(len) { case 3: h ^= data[2] << 16; case 2: h ^= data[1] << 8; case 1: h ^= data[0]; h *= m; } h ^= h >> 13; h *= m; h ^= h >> 15; return h; } unsigned murmurhash32(unsigned k) { return murmurhash(&k, 4); } /* simple hash for aligned pointers */ unsigned ptrhash(const void *k) { const unsigned long n = (const unsigned long)k; return (n >> 2) ^ (n >> 12); } /* simple string hash */ unsigned strhash(const char *key) { const unsigned char *p = (const unsigned char *)key; unsigned h = SEED; while (*p) h += (h << 2) + *p++; return h; } /* simple string hash, case-insensitive */ unsigned strhash_case(const char *key) { const unsigned char *p = (const unsigned char *)key; unsigned h = SEED; while (*p) h += (h << 2) + tolower(*p++); return h; } /* case sensitive string keyeq */ int strkeyeq(const char *a, const char *b) { return !strcmp(a, b); } /* case insensitive string keyeq */ int strkeyeq_case(const char *a, const char *b) { return !genht_strcasecmp(a, b); } /* pointer match for htp*_t */ int ptrkeyeq(const void *a, const void *b) { return a == b; } unsigned longhash(long int l) { return l; } int longkeyeq(long a, long b) { return a == b; } fungw-1.2.0/src_3rd/genht/ver_edit.c0000644000175100017510000000107413156407367015467 0ustar svnsvn#include #include #define replace(pattern, value) \ do { \ char *next, *s = strstr(line, pattern); \ if (s != NULL) { \ next = s + strlen(pattern); \ strcpy(s, value); \ s += strlen(value); \ memmove(s, next, strlen(next)+1); \ } \ } while(0) int main(int argc, char *argv[]) { char *major = argv[1], *minor = argv[2], *patch = argv[3], line[256];; while(fgets(line, sizeof(line), stdin) != NULL) { replace("_VER_MAJOR_", major); replace("_VER_MINOR_", minor); replace("_VER_PATCH_", patch); printf("%s", line); } return 0; } fungw-1.2.0/src_3rd/genht/ht_inlines.h0000644000175100017510000000204513156373353016023 0ustar svnsvn/* helper functions */ GENHT_STATIC GENHT_INLINE unsigned int HT(length)(const HT(t) *ht) {return ht->used;} GENHT_STATIC GENHT_INLINE unsigned int HT(fill)(const HT(t) *ht) {return ht->fill;} GENHT_STATIC GENHT_INLINE unsigned int HT(size)(const HT(t) *ht) {return ht->mask + 1;} /* for any entry exactly one returns true */ GENHT_STATIC GENHT_INLINE int HT(isused)(const HT(entry_t) *entry) {return entry->flag > 0;} GENHT_STATIC GENHT_INLINE int HT(isempty)(const HT(entry_t) *entry) {return entry->flag == 0;} GENHT_STATIC GENHT_INLINE int HT(isdeleted)(const HT(entry_t) *entry) {return entry->flag < 0;} /* first used (useful for iteration) */ GENHT_STATIC GENHT_INLINE HT(entry_t) *HT(first)(const HT(t) *ht) { HT(entry_t) *entry = 0; if (ht->used) for (entry = ht->table; !HT(isused)(entry); entry++); return entry; } /* next used (useful for iteration) */ GENHT_STATIC GENHT_INLINE HT(entry_t) *HT(next)(const HT(t) *ht, HT(entry_t) *entry) { while (++entry != ht->table + ht->mask + 1) if (HT(isused)(entry)) return entry; return 0; } fungw-1.2.0/src_3rd/genht/htsp.h0000644000175100017510000000033312767241764014652 0ustar svnsvn#ifndef GENHT_HTSP_H #define GENHT_HTSP_H #define HT_HAS_CONST_KEY typedef char *htsp_key_t; typedef const char *htsp_const_key_t; typedef void *htsp_value_t; #define HT(x) htsp_ ## x #include "ht.h" #undef HT #endif fungw-1.2.0/src_3rd/genht/Makefile0000644000175100017510000000430714044504577015162 0ustar svnsvn# use -DGENHT_WANT_INLINE to turn half of the API into static inline functions PREFIX=/usr INCDIR=$(install_root)$(DESTDIR)$(PREFIX)/include/genht LIBDIR=$(install_root)$(DESTDIR)$(PREFIX)/lib HOSTCC = $(CC) VER_MAJOR=1 VER_MINOR=1 VER_PATCH=1 VER=$(VER_MAJOR).$(VER_MINOR).$(VER_PATCH) CFLAGS=-g $(GENHT_CFLAGS) LDFLAGS=$(GENHT_LDFLAGS) BIN=mainsi OBJS=htss.o htsp.o htsi.o htip.o htpp.o htpi.o hash.o siphash24.o LIBSO=libgenht.so.$(VER) LIBSO1=libgenht.so.$(VER_MAJOR) LIBA=libgenht.a all: version.h $(BIN) $(OBJS) $(LIBA) $(LIBSO) clean: -rm $(BIN) $(OBJS) $(LIBA) $(LIBSO) genht_std.so genht_std.a version.h ver_edit .SUFFIXES: .SUFFIXES: .o .c .c.o: $(CC) -o $@ -c -fPIC $< $(CFLAGS) mainsi: mainsi.o htsi.o $(CC) -o $@ mainsi.o htsi.o $(LDFLAGS) $(LIBSO): $(OBJS) $(CC) $(LDFLAGS) -shared -dynamic -rdynamic -o $@ $(OBJS) $(LIBA): $(OBJS) ar rvu $@ $(OBJS) -ar s $@ -ranlib $@ # for compatibility genht_std.so: $(LIBSO) ln -s $(LIBSO) genht_std.so # for compatibility genht_std.a: $(LIBA) ln -s $(LIBA) genht_std.a version.h: version.h.in ver_edit ./ver_edit $(VER_MAJOR) $(VER_MINOR) $(VER_PATCH) < version.h.in > version.h ver_edit: ver_edit.c $(HOSTCC) -o $@ $(CFLAGS) ver_edit.c install_: mkdir -p $(INCDIR) $(LIBDIR) $(CP) `pwd`/hash.h $(INCDIR)/hash.h $(CP) `pwd`/siphash24.h $(INCDIR)/siphash24.h $(CP) `pwd`/ht.h $(INCDIR)/ht.h $(CP) `pwd`/ht.c $(INCDIR)/ht.c $(CP) `pwd`/version.h $(INCDIR)/version.h $(CP) `pwd`/ht_inlines.h $(INCDIR)/ht_inlines.h $(CP) `pwd`/ht_utils.h $(INCDIR)/ht_utils.h $(CP) `pwd`/htip.h $(INCDIR)/htip.h $(CP) `pwd`/htpi.h $(INCDIR)/htpi.h $(CP) `pwd`/htsi.h $(INCDIR)/htsi.h $(CP) `pwd`/htsp.h $(INCDIR)/htsp.h $(CP) `pwd`/htss.h $(INCDIR)/htss.h $(CP) `pwd`/htpp.h $(INCDIR)/htpp.h $(CP) `pwd`/$(LIBSO) $(LIBDIR)/$(LIBSO) $(CP) `pwd`/$(LIBA) $(LIBDIR)/$(LIBA) uninstall: rm -rf $(INCDIR) rm $(LIBDIR)/$(LIBSO) $(LIBDIR)/$(LIBA) $(LIBDIR)/$(LIBSO1) install: make install_ CP="cp" -@rm $(LIBDIR)/$(LIBSO1) 2>/dev/null ln -s $(LIBSO) $(LIBDIR)/$(LIBSO1) linstall: make install_ CP="ln -s" -@rm $(LIBDIR)/$(LIBSO1) 2>/dev/null ln -s $(LIBSO) $(LIBDIR)/$(LIBSO1) test: mainsi @./mainsi > mainsi.out @diff mainsi.ref mainsi.out @echo "*** QC pass ***" fungw-1.2.0/src_3rd/genht/hash.h0000644000175100017510000000174513514013700014601 0ustar svnsvn/* assumes sizeof(unsigned)==4 */ #define GENHT_HAS_STRCASECMP 1 /* Portable (c89) version of strcasecmp */ int genht_strcasecmp(const char *s1, const char *s2); /* not for strings: does unaligned access and reads past the end of key */ /* bob jenkins: lookup 3 */ unsigned jenhash(const void *key, unsigned len); unsigned jenhash32(unsigned k); /* austin appleby: murmur 2 */ unsigned murmurhash(const void *key, unsigned len); unsigned murmurhash32(unsigned k); /* simple hash for aligned pointers */ unsigned ptrhash(const void *k); /* simple string hash - case sensitive and case-insensitive */ unsigned strhash(const char *k); unsigned strhash_case(const char *key); /* string keyeq functions - case sensitive and case-insensitive */ int strkeyeq(const char *a, const char *b); int strkeyeq_case(const char *a, const char *b); /* pointer match for htp*_t */ int ptrkeyeq(const void *a, const void *b); /* long (int) */ unsigned longhash(long int l); int longkeyeq(long a, long b); fungw-1.2.0/doc/0000755000175100017510000000000014047742763011623 5ustar svnsvnfungw-1.2.0/doc/lang_perl.html0000644000175100017510000000343414034212775014447 0ustar svnsvn

fungw language bindings - perl

datasheet

tested on Debian (libperl 5.22, 5.24, 5.26), gentoo (libperl 5.26)
known bugs/limitations none
API stability moderate (prefer other languages)
configuration requirements Must enable ithreads

Details

API hacks

Required major hacks because the perl API doesn't support storing caller context. It is also impossible to register a perl variable for this purpose between init and script load. The current solution is using an unused field of the perl interpreter struct, which may not be the most stable solution long term.

configuration

On Debian, ithread is enabled by default by the packaging. If you compile perl from source, you probably need to enable it by hand. Readme.irix suggests running Configure with -Duseithreads.

Karl's comments on enabling ithreads on gentoo:

Doing the things below (in gentoo) makes that so:

  the magic, add ithreads support to perl:
# echo dev-lang/perl ithreads > /etc/portage/package.use/perl

  rebuilding perl (-aqv is not nessesary):
# emerge -aqv perl
[ebuild   R   ] dev-lang/perl-5.26.9999  USE="berkdb gdbm ithreads* -debug -doc" 
...

Here comes a warning that you must rebuild all things perl (not perl
itself which was rebuild above), which is done with the perl-cleaner
thing below.

  I got what was requested:
$ perl -V | grep PERL_IMPLICIT_CONTEXT 
    PERL_IMPLICIT_CONTEXT

  cleaning upp the mess:
# perl-cleaner --modules ; perl-cleaner --force --libperl
[ rebuilding 100+ packages...]

It seems that "ithreads" is per default off.

fungw-1.2.0/doc/model.html0000644000175100017510000002151514032232717013600 0ustar svnsvn

Fungw - data model

contexts, objects, functions

The most important basic type is function (fgw_func_t). A function has a return value and a flexible number of arguments. The return value and arguments are all of fgw_arg_t, containing a type and a value.

Functions are registered by name in a the hash table of the fungw context. Functions are provided by uniquely named objects - a list of available objects is also registered in the fungw context.

A fungw context is a collection of states that describes a full, self contained configuration and state of a fungw instance. It can be taken as an instance of a plugin system. An application can maintain multiple independent fungw contexts, but the typical case is that an application creates only one. Fungw contexts can be created and discarded any time.

An object is a group of related functions and opaque internal states. Fungw is not object oriented in the modern sense: each object is an independent entity in a flat list of functions and there's no inheritance or other complex relationship between objects. Each object has a name that is unique within the fungw context. If the object has internal states, these states can be accessed only through function calls the object provides.

In the example drawn above, the application keeps two independent fungw contexts: context1 (green, right) and context2 (green, left). Each context hosts two objects (purple). Context's obj1 is a fawk script that provides 3 functions (foo, bar and baz) and has some internal states (fawk global variables). The other object in context1 is implemented in lua. Context2 has a similar setup. Context2

Both context1 and context2 loaded hello.lua, but these are two separate instances, because they are in two separate objects. Although the object names match (obj2), the objects are in two separate contexts. Thus the two instances of hello.lua will not share any lua code or variables (but are loaded in memory as duplicates).

The application may call function fungw foo() within a context. If it is called in context1, it's obj1, foo.awk's foo(). If it's called in context2m it's obj1, bar.lua's foo(). Once a function is called within a context, it may do further calls, but those calls will always stay within the same context.

engines

Objects are really instances of engines. An engine is the actual implementation that is instantiated into an object. It can be instantiated into multiple independent objects in the same or in multiple fungw contexts. That means the code of an engine is shared by all objects created from it, but data (states, variables) is unique to the object.

An engine is a library that implements code that can be used to create object. The implementation should be thread safe and multi-instance, storing all internal state in a per object storage allocated when the object is initialized. The engine implements and registers all functions the object will expose to fungw.

For example a buddy allocator library is implemented as an engine. The application, within an already initialized fungw context, may create multiple objects that will be each an independent instance of the buddy allocator, each controlling its own allocations, not knowing about other instances.

Another typical example is script engines. The implementation for loading and executing lua scripts is an engine. When the application needs to load three different lua scripts, the engine is called three times to register three objects. Each object represents the full internal state of one of the scripts, including the script code parsed, global variables and functions registered. Different scripts may register different functions, so although the engine was the same for all three objects, the actual API (the functions exposed) may differ in each object.

On the above pictured example, two engines are used: fungw_fawk and fungw_lua. These each implement their own (fawk or lua) scripting. That means the engine has the code for the interpreter for the given language, but no script. A script is loaded by creating a new object: the object's code is provided by the engine, but data (the script) by the script file. In the above example the lua engine is loaded only once, although it creates 3 independent objects running separate lua scripts.

function names

The function hash is a hash table in a fungw context that lists all functions currently registered in the context, with enough low level information that fungw can perform a call to the function.

Each public function is normally registered in the function hash by two names: the globally unique object_name.function_name and the non-unique short function_name. Since function names must be unique within the hash, short name collisions need to be resolved.

Fungw applies a first-come-first-served method for selecting the short name: the first object that tries to register the given short name will succeed, subsequent requests with the same name are denied. When short name can not be registered object_name.function_name is still registered, so the function is accessible.

When a function is unregistered, the long name is removed from the hash. If the short name in the hash corresponds to this function, it is also removed and a substitution (another function with the same short name) is searched and if found, inserted. If there are multiple candidates (registered in different objects) for the substitutions, one is picked randomly.

This mechanism is designed so that callers do not need to know the name of the object that provides a common function. Multiple objects can provide the same function using the same name. When the caller uses a short name of a function, the caller assumes the different implementations are compatible.

On the above pictured example, when a single-call to baz() in context1 is done, only one of obj1's baz() or obj2's baz() is called. Which one of the two is picked depends on the order of objects created. Such call is useful if different objects implement the same functionality using the same name so it doesn't matter which one is called - "call any available implementation, just don't do it twice". When a specific implementation is required, e.g. the application needs to one implemented in hello.lua, obj2.baz() should be called. If both needs to be called ("call all implementations, in random order" - typical for delivering events), fgw_*call_all() should be used.

function calls

A function has:

  • two names
  • zero or more input arguments
  • a return value
  • a success indicator

All arguments are input arguments because it would not be feasible in every supported scripting language to use output arguments. The return value is always present, but can be empty (a void pointer with value of 0).

The number of arguments is not fixed. The caller may call the function with any amount of arguments. The callee gets an argc/argv[] pair and can decide if the call is valid, optionally looking at the number and type of arguments.

Any function call may succeed or fail. Optionally the caller gets an indication of this in the success indicator (accessible in C). When the call fails, the return value is set to empty.

argv[0] contains a pointer to the fungw function being called and optionally the user_call_ctx.

function call stack (threads and async)

Every call stack (or chain) should originate in the host application. Which means it's generally the host application that is running first and the first fungw call is always initiated by the application. In other words, the main loop and handling external events (user input, network events, I/O in general, timers, or anything async) should be implemented by the application, not by fungw engines (e.g. scripts).

Especially any threading or async event handling done by the application, keeping in mind that fungw objects have non-thread-local internal states. If the application has threads, it is the application's responsibility to make sure parallel instances of the same object is never being executed. If an engine, e.g. a script language, implements threading or async event handling, that should not be used with fungw.

custom types

The application may extend the list of types supported by registering new, custom types. fungw-1.2.0/doc/lang_python3.html0000644000175100017510000000130214034212775015101 0ustar svnsvn

fungw language bindings - python3

datasheet

tested on Debian (python 3.7.3)
known bugs/limitations steals signal handlers and strings are PyBytes
API stability low
configuration requirements n/a
Python3 is not recommended for scripting.

Details

Python3 removed PyString but memory management breaks with PyUnicode so fungw has to use PyBytes which is probably inconvenient.

Python3 takes over signal handling which is usually incompatible with the application it is running in. fungw-1.2.0/doc/src/0000755000175100017510000000000014047742763012412 5ustar svnsvnfungw-1.2.0/doc/src/model.dot0000644000175100017510000000432314032252242014202 0ustar svnsvndigraph model { ranksep=0.01; nodesep=0.01; subgraph cluster_0 { label = "application code"; bgcolor = "#FFDDDD" subgraph cluster_1 { label = "fgw_ctx_t *context1"; bgcolor = "#DDFFDD" subgraph cluster_1a { bgcolor = "#DDDDFF" label = "fgw_obj_t *obj1\n(e.g. foo.awk,\nengine: fungw_fawk)" funca1 [label="awk function: foo()"] funca2 [label="awk function: bar()"] funca3 [label="awk function: baz()"] statea1 [label="internal states\n(e.g. global variables\nof the script)"] } subgraph cluster_1b { bgcolor = "#DDDDFF" label = "fgw_obj_t *obj2\n(e.g. hello.lua,\nengine: fungw_lua)" funcb1 [label="lua function: hello_world()"] funcb2 [label="lua function: baz()"] stateb1 [label="internal states\n(e.g. global variables\nof the script)"] } subgraph cluster_1c { bgcolor = "#AAAAAA" label = "context function name hash" global1 [label="obj1.foo(), obj1.bar(), obj1.baz(), obj2.baz(),\nobj2.hello_world()\nfoo(), bar(), baz(), hello_world()\n"] } statea1 -> global1 [style=invis] stateb1 -> global1 [style=invis] } subgraph cluster_2 { label = "fgw_ctx_t *context2"; bgcolor = "#DDFFDD" subgraph cluster_2a { bgcolor = "#DDDDFF" label = "fgw_obj_t *obj1\n(e.g. bar.lua,\nengine: fungw_lua)" funcc2 [label="lua function: foo()"] statec2 [label="internal states\n(e.g. global variables\nof the script)"] } subgraph cluster_2b { bgcolor = "#DDDDFF" label = "fgw_obj_t *obj2\n(e.g. hello.lua,\nengine: fungw_lua)" funcd1 [label="lua function: hello_world()"] funcd2 [label="lua function: baz()"] stated1 [label="internal states\n(e.g. global variables\nof the script)"] } subgraph cluster_1c { bgcolor = "#AAAAAA" label = "context function name hash" global2 [label="obj1.foo(), obj2.baz(), obj2.hello_world()\nfoo(), baz(), hello_world()"] } statec2 -> global2 [style=invis] stated1 -> global2 [style=invis] } } funca1->funca2 [style=invis] funca2->funca3 [style=invis] funca3->statea1 [style=invis] funcb1->funcb2 [style=invis] funcb2->stateb1 [style=invis] funcc2->statec2 [style=invis] funcd1->funcd2 [style=invis] funcd2->stated1 [style=invis] } fungw-1.2.0/doc/lang_lua.html0000644000175100017510000000070713323555241014264 0ustar svnsvn

fungw language bindings - lua

datasheet

tested on Debian (liblua 5.1.5)
known bugs/limitations insufficient number of arguments in a C->lua call causes lua to abort in an uncontrollable way
API stability good
configuration requirements n/a

Details

fungw-1.2.0/doc/Makefile.in0000644000175100017510000000142414034225152013651 0ustar svnsvnprint [~# Generated by ./configure, do not edit SCCBOX=../scconfig/sccbox PREFIX=~/local/fungw/prefix~ DOCDIR=$(install_root)$(DESTDIR)$(PREFIX)/share/doc/fungw/ DOCS= \ TODO \ custom_types.txt \ index.html \ lang.html \ lang_estutter.html \ lang_funlisp.html \ lang_howto_support.html \ lang_lua.html \ lang_perl.html \ lang_python.html \ lang_sh.html \ model.html all: clean: distclean: rm Makefile # Universal install rule (can uninstall as well) install_: $(MKDR) $(DOCDIR) $(INST) -d $(DOCS) $(DOCDIR) install: make install_ "INST=$(SCCBOX) install -i" "MKDR=$(SCCBOX) mkdir -p -i" linstall: make install_ "INST=$(SCCBOX) install -l -f -a" "MKDR=$(SCCBOX) mkdir -p -i" uninstall: make install_ "INST=$(SCCBOX) install -u -f" "MKDR=$(SCCBOX) mkdir -u" ~] fungw-1.2.0/doc/custom_types.txt0000644000175100017510000000163614034212775015117 0ustar svnsvn**TODO: document this ** The implementation must respect the FGW_DYN type flag; in a multicall, the caller will make non-dynamic copies that shall never be free'd, even if the custom type stores the payload as a separate allocation. In that case the non-dynamic copies will exist only as long as the dynamic parent copy exists (the caller guarantees this) and they will all point to the same allocation. This also means a conversion to a different type shall not change the memory behind the allocation. When a custom type is requested to convert to FGW_AUTO, it needs to be converted to any base (non-custom) type. It is strongly recommended to pick a type from which the custom argument can be converted back, for full round trips. This feature is typically used by script bindings to convert incoming (e.g. function return) values to something the given script language can store. Safest bets are FGW_LONG and FGW_STR. fungw-1.2.0/doc/lang_sh.html0000644000175100017510000000153413304175024014110 0ustar svnsvn

fungw language bindings - sh using cli

datasheet

tested on Debian (bash, dash)
known bugs/limitations data can not contain newlines; C function calls must be wrapped in a call to fgw; all data passed as string
API stability stable but inconvenient due to the limitations of the method
configuration requirements nothing special

Details

Function calls from the script to C must be wrapped in calls to local shell function fgw. For example, assuming there's a function called atoi, instead of atoi 4 the script needs to call fgw atoi 4.

Because of the text protocol of the cli, all data passed between the script and the C program are converted to string. fungw-1.2.0/doc/TODO0000644000175100017510000000103314046416016012274 0ustar svnsvn- BUG: event mode call-all doesn't work well; test this with fawk: if a script registers foo() then another script defines foo(), it may collide with the already defined one. Is this fawk-specific? fgwirc, script.c, script_clean_hooks() - need to install the header from the c "binding" to make it useful for plugins - look for code TODOs - BUG: doc install: DOCDIR missing /librnd - make test should run automated tests - BUG: duktape: syntax error in the script causes assert - check for memleaks (all languages) fungw-1.2.0/doc/releasing.txt0000644000175100017510000000026413760130702014320 0ustar svnsvnfungw release procedure ~~~~~~~~~~~~~~~~~~~~~~~ 1. write the Changelog and Release_notes 2. update version in trunk/libfungw/scconfig_hooks.h "fungw_ver" global vars 3. svn tag fungw-1.2.0/doc/lang_estutter.html0000644000175100017510000000146614046417634015373 0ustar svnsvn

fungw language bindings - estutter

datasheet

tested on Debian ( estutter 1.0.0)
known bugs/limitations none
API stability stable (we also have full control over the estutter project so fungw support is likely to remain stable for long)
configuration requirements nothing special

Details

estutter is a medium complexity lisp-like interpreter implemented in about 9k sloc. The language is inspired by and supports elements of common lisp and elements of scheme. Despite of its small size, it features a lot of advanced concepts, like OOP, CLOS, customizable I/O streams, SETF. fungw-1.2.0/doc/lang_howto_support.html0000644000175100017510000000557313302212046016433 0ustar svnsvn

How to get your favorite language supported by fungw

requirements

First find an implementation, preferably a reusable lib, and check if it meets the requirements:

  • must be an interpreted language (it can feature its own VM, most languages do, but it shouldn't try to create a native host system executable)
  • must feature a free/open source lib implementation with reasonable dependencies
  • must offer a C API (having a C++ API is not enough)
  • must support loading and executing multiple independent scripts in parallel
  • must support loading and unloading scripts any time
  • must support function calls between the host application and the script in both direction
  • the API must be strong enough to support storing context pointers (see below)

option 1: request

Request language support by sending a mail to: fungw (at) igor2.repo.hu

Please include the name of the scripting language, the name of the interpreter library you think is suitable for the job, the URL for the lib.

If you are a programmer, please consider option 2 for faster inclusion.

option 2: craft an example

To help the process, you should write an example program. The example code must be placed in the public domain so code can be copied into fungw without affecting the license. The example should include:

  1. a test script which implements exactly the same as any hello.* script under trunk/example
  2. a C program, that demonstrates:
    1. how to init the lib and a script context
    2. how to store and retrieve a C pointer in the script context
    3. how register C functions (with multiple arguments and a return value) in the script context, before loading a script
    4. how the C function figures the function name that's being called (dispatched functions)
    5. how the C function figures the script context the call came from
    6. how to load and parse a script; how to execute the main part of a script
    7. how to call a function of the script by name, with multiple arguments, acquiring the return value
    8. how to uninit the lib and the script context

When writing the example C program, please try to be as simple as possible:

  • prefer to use C89 (with gcc, compile with -std=c89 -pedantic)
  • do not demonstrate any feature not requested above - fungw will not use anything more, so extras packed in the example are only noise that has to be ignored in the fungw implementation
  • the example program doesn't need to know anything about fungw, only about the script language lib; it should be just a simple, single-source-file, standalone executable
  • the example program doesn't need to be able to load and run the example script requested above, but a script that it can run must be supplied.
fungw-1.2.0/doc/packaging.txt0000644000175100017510000000610113561527440014277 0ustar svnsvnThis file contains recommendations for distro packagers. Please try to follow them as closely as possible so that fungw is packaged the same way on all distros. This will ease the life of users and developers who try to support users. Before packaging, please make sure you read lang.html - especially library versions. Some bindings will work only with a specific version of the scripting lib. 1. For binary distros This applies if your packages are going to deliver precompiled binaries. There should be a core package, called libfungw. It should contain the files installed from trunk/libfungw. To get a list: cd trunk ./configure --prefix=/usr make cd libfungw make install DESTDIR=/tmp/foo # then explore /tmp/foo libfungw should not depend on anything (but libc). There should be a separate package for each binding, called libfungw-BINDINGNAME, e.g. libfungw-lua. These packages should contain the .so files _and_ the puplug/* files installed by the package. (The above method should work in libfungwbind/*). libfgunw-c does not need a package. NOTE: the puplug/* files are essential: most applications will dynamic link these .so files using dlopen() through puplug - it will be the rare case that the .so files are directly linked with -l. If the puplug/* files are not in place, the package will be unusable for most apps. A binding package should depend on libfungw (same version!) and in most cases on 3rd party script lib (e.g. liblua). Please do not create bundled packages of selected bindings, rather stick to one package per directory. Optionally a metapackage should be created, called fungw, which installs: libfungw libfungw-fawk libfungw-cli These are the packages that won't have external dependencies so would be cheap to install. Together they will provide at least 4 scripting languages. 2. For source distros This applies if your packages are NOT going to deliver precompiled binaries, only sources and users of the package will always compile. The core is always compiled. Bindings can be disabled or enabled one by one. Please let the user decide which bindings to use. Each binding depends only on libfungw core and its own 3rd party scripting lib (except for fawk and cli which won't have any external dependencies). Please do NOT make any scripting language mandatory: fungw works perfectly fine without python or perl or lua or whichever language is the popular one this week. Many users will prefer to use something less popular because they happen to know that language. Let the user pick which ones they are going to use, don't force them to install dependencies for a language they would never use. NOTE: ./configure is much smarter than the usual autotools stuff: it can automatically figure which languages are available on the users system and will enable bindings accordingly. This doesn't need any distro side help or scripting, it just happens any time the user runs ./configure. No need to pass configure parameters to explicitly enable or disable languages, it's enough to just install the right libs. It's probably a good idea to use --prefix=/usr for ./configure fungw-1.2.0/doc/lang_python.html0000644000175100017510000000111713325030756015021 0ustar svnsvn

fungw language bindings - python

datasheet

tested on Debian (python 2.6)
known bugs/limitations will fail with anything higher than 2.6
API stability low
configuration requirements n/a

Details

Python 2.6 produces a lot of invalid memory handling even on a simple init/uninit sequence, when debugged with valgrind. 2.7 crashes on the same sequence. It's not recommended to use python. fungw-1.2.0/doc/lang_picol.html0000644000175100017510000000140014036460670014603 0ustar svnsvn

fungw language bindings - picol

datasheet

tested on Devuan
known bugs/limitations function name lookups are slow
API stability good
configuration requirements n/a

Details

Picol implements linear search for function name lookup. In a typical setup the host application and other scripts and plugins will register a lot of functions within the picol context which will make calling a proc (or fungw registered function) real slow having to go through a long list with linear search for each call. This gets worse when function calls are made from within a loop. fungw-1.2.0/doc/lang.html0000644000175100017510000002425314046417634013433 0ustar svnsvn

fungw language support

language fungw plugin state 3rd party software dependency binding doc, comments
C c works C89 and newer n/a
fawk fawk works none (an AWK dialect)
BASIC fawk (fbas) works none (a modern BASIC dialect)
pascal fawk (fpas) works none (a pascal script dialect)
awk mawk works libmawk (svn r1206) license warning: libmawk is GPL
lua lua works liblua (5.1) available
tcl tcl works libtcl (8.5) n/a
picol (tcl) picol works picol (0.6.0) available
javascript duktape works libduktape (2.0.2) n/a
javascript mujs works mujs (1.1.1) n/a
ruby mruby works libmruby (1.0.0) n/a
perl perl works libperl (5.24.1) available
lisp/scheme estutter works estutter (1.0.0) available
POSIX shell cli works bash (4.3) or dash (0.5.7) available
lisp funlisp works funlisp (1.1.0) available
python python works libpython (2.6) available
python3 python3 BUGGY libpython (3.7.3) available

Note: a version number in the dependency column is only the version of the package fungw last worked with - other versions may work as well. Exception is when the version number is bold, in which case other versions will NOT work.

libs/languages that could be supported but probably won't be

language last evaluated reason
scheme/guile 2018-05 guile is a major pain to configure and compile; the API is not friendly either; use estutter instead
sigscheme 2018-05 use estutter instead
chibi-scheme 2021-04 probably usable, but we have enough lisp/scheme variants for now (use estutter instead)
chicken-scheme2021-04 probably usable, but we have enough lisp/scheme variants for now (use estutter instead)
janet 2021-04 probably usable, but we have enough lisp/scheme variants for now (use estutter instead)
s7 2021-04 probably usable, but we have enough lisp/scheme variants for now (use estutter instead)
Jim (tcl) 2021-04 probably usable, but we have enough TCL variants for now (use stock tcl or picol)
jerryscript 2021-04 probably usable, but we have enough JavaScript variants for now (use mujs or duktape instead)
jsi/jsish 2021-04 probably usable, but we have enough JavaScript variants for now (use mujs or duktape instead)
espruino 2021-04 probably usable, but we have enough JavaScript variants for now (use mujs or duktape instead)
njs 2021-04 probably usable, but we have enough JavaScript variants for now (use mujs or duktape instead)
quickjs 2021-04 probably usable, but we have enough JavaScript variants for now (use mujs or duktape instead)

libs/languages that can not be supported

Some of these could be supported through the cli engine but they can not be supported natively for various reasons, described below.

language last evaluated reason
php 2018-05 could not figure how to set up multiple instances of php script with php7.0 (see work/php); probably could be done with PH7, but that doesn't come with an installable lib
erlang 2018-05 does not have a C embeddable lib
go 2018-05 not interpreted, compiled
haskell 2018-05 not interpreted, compiled (would need FFI)
Clojure 2018-05 depends on JVM?
Scala 2018-05 depends on JVM
Groovy 2018-05 depends on JVM?
Rust 2018-05 not interpreted, compiled
F# 2018-05 not interpreted, compiled
Boo 2018-05 does not have a C embeddable lib
rep (lisp) 2018-05 does not support multiple instances
ecl (lisp) 2018-05 does not support multiple instances
gambc (lisp) 2018-05 does not support multiple instances
elk (lisp) 2018-05 does not support multiple instances
scm (lisp) 2018-05 does not support multiple instances
scheme9 2018-05 does not support multiple instances
ixion 2018-05 C++
rexx 2018-05 rexxsaa.h does not offer multiple script contexts, that would be mt.h (included from rexx.h) with tsd_t; but it seems rexx.h is usually not installed (from .deb or .rpm)
pike7.8 2019-02 stand alone interpreter, does not seem to have an API for embedding
red 2021-04 official download page broken: can not download source release tarballs
rebol 2021-04 bootstrap fail: requires rebol binary to build from source; no binary available in Debian; no proper source release tarballs, just an abandoned github project
PascalScript 2021-04 bootstrap fail: remobject's PascalScript: no source release tarballs, only a github project; not clear how to compile outside of delphi (no Makefile or shell script or anything with e.g. fpc commands, no documentation)
ici 4.1.0 2021-04 bootstrap fail: fails to compile (posix thread problems, non-standard vararg copy)
dart 2021-04 bootstrap fail: no proper source release tarball; there are development snapshots from git, containing a 300 megabytes sdk
Kotlin 2021-04 depends on JVM?
Pony 2021-04 doesn't look like a scripting language with an API for embedding
Matlab 2021-04 octave is the free implementation; liboctave: the API is largely C++; example for embedding: embedded.cc; no C API for parse or execute
Atlas 2021-04 not reentrant (can run only one script at a time)
pawn 2021-04 does not compile a reusable lib, does not have an installation process
umka 2021-04 API too weak, no email address to the author
hashlink 2021-04 dependencies are too extensive, no email address to the author
Io 2021-04 overcomplicated build process; cmake fails to create Makefile using the last release (2019.05.22-alpha)
my_basic 2021-04 no release tarballs available
lily 2021-04 API too weak (for script->C function dispatching); no email contact information, dead IRC channel
never-lang 2021-04 API too weak (no dynamic registered C function callback), no contact information, insufficient docs/examples on embedding
pforth 2021-04 no release tarballs, no contact email
ring 2021-04 just too huge: the source release tarball is 1.2 gigabytes - bigger than the typical operating system image fungw is developed on
sgscript 2021-04 abandoned project; no make install (ctx in function: sgs_DFunc; ctx in script: *OutputFunc, MsgFunc, ScriptFSFunc)
picoc 2021-04 does not compile into a library out of the box - probably could be easily modified, tho (no answer from the author)
wren 2021-04 script->C function call: can't figure which function is called (no answer from the author) [fork?]
gravity 2021-04 script->C function call: can't figure which function is called (no answer from the author) [fork?]

libs/languages that requires some upstream support

These potentially could be supported, but something fails, installation, or some detail of the API. I've contacted the author to see if the problem can be resolved (or the language moved to the unsupported category).

(none at the moment)

Special languages: embedded interpreters

Libfawk provides an extremely small scripting language VM+compiler implementation with support for multiple languages. It is so small that the whole code is a single C file that is simply embedded in the fungw plugin. Libfawk is also pure C89, without any external dependency.

That means the fungw fawk plugin has no external dependency, because it contains both the scripting language code and the fungw binding. Since fungw can also be compiled without any external dependency, this means there is at least one scripting language plugin that will always work, everywhere, wherever fungw can be installed, providing multiple languages. fungw-1.2.0/doc/model.png0000644000175100017510000042745014032252242013423 0ustar svnsvn‰PNG  IHDRF¢5#¼bKGDÿÿÿ ½§“ IDATxœìÝwtTU×Çñß$B“ž‰ˆä‘ŽT R"i¡ƒŠ€EC@E‚R EŠ‚Ò‚ ¡*U¤÷Ðð¡!=„d2ïy i“Æðý¬åZÎsÏÙçŠÃÙ{îœk0™L&°J6€¼C°b+F°b+F°b+F°b+F°b+F°b+F°bv÷sòäÉ“µgϞ܊À=š5k¦aÆåøüû*îÙ³G{wîTÓúõï§éØ{äÈ}÷q_@IjZ¿¾–OŸ~ßH­Ç!÷Ý{VŒ `Å(VŒ `Å(à[Ð!<0(ä“$£Q8 Q“&é×;ÌÇWmÚ¤*-Zèôùóy:~~“—fèù>}T«mÛ‚àA Ÿ8vLóþYãgÎÔÕóñbŽŽ*_¦Œìísu¼kׯ§zWãä§7{öTrr²ŒÉÉ Àƒ @>iV¿¾ÞõòJs¼í3ÏèP` «R%ׯº©—‡ Ëóqò›­­­*W¬XÐa>ªùØcz£gÏ ÇY±q£¶íÝ+{{üóO5¬][£‡ ‘}Ñ¢:zê”jÅÆ:¾~½Þ7N«~ûMÕ«VÕRUÏânÂÌæÕØw­Þ¼Yë¶mSé%wû¶®……¥Ãd2iv@€‚NŸÖá“'U²xqÍ;V5ªU³ø¿?€5£ ©÷СªP¶¬F"Iª÷ÒKò7N 'MÒ•+5kÉEÇÆêëモ÷ë¯ëOO½=j”Z÷ï¯ó[·ZÔ¦RùòiÆ ÓÉsç´y×. ìÛ×||áÊ•Z¿}»O™"Ÿ9So%×GU«fÍ2·_çÎZºv­Nüù§æŽ/I:}þ|ºãL?_Ëׯ×ö€±³Ó͈5ëÖM;Ô¶Å‹U±\9=uJÿ½rE#'NÔ°7ÞÐþýÕ´[7úúk|óM†×4«9d5¶Á`Ð’À@ùÿø£¶/Y"{{ݸuKµÚ¶•Ý?ËX¿Ù³U©|y};nœŒF£êxxè¹Þ½uaÛ699:Þß +ÀO€$ Õ­UËüú©'žÐ±3g$I_~ð)IúiݺTm’M&ÿwGeíš5e2™4ÒÏ/U› —/kæ¢E9ŽÀšð`ðлûÕùEëÖÕcÇtòÜ9…Þ¸¡cgΨBÙ²’Rî\ :}ÚüðÏgÌ—§§×­kî+«6Q11’¤Øøxó9×®_—ôÏkÍ4PûçŸ×ªM›Ôúå—Õ½}{]úûo…GFê»/¿Ô¾£G³Œ×¥|yݸuK‡NœPtl¬ש“fœ2¥JÉoäH 3F[vïVëæÍ%Iþ?ü /OO½Ð´©$)2:Z’RýŒùúÍ›Šû×î•Õ$Y4ö M›ê‡+Ôð©§äåé©“çÎiçÁƒ WÀš5êÔºµž®SGKu;!A]ÝÝ£_~ýUKýý-ø¯`ýl?ýôÓOszòòåË¥ÄDõôðÈÅòWåŠzã†6íÜ©}AAò|ñEµjÞ\k¶lÑåà`õôðÐÆ;tâìY-ZTß/_®_ÿøCU*UÒ×l. ®Ù²%Ó6ûƒ‚ô™¿¿Î_º¤ë7n¨J¥Jºríš&Ι£Kÿ­Ð7ôXåÊr­ZU]ÝÝu+2R{Öö}ûäú裚øÑGr°··(^×GÕÚ­[¸e‹šÕ¯¯›éŽÓ¨vmÕssÓÔùóµ?(H{QÙGÑ„?”Á`Жݻõõwß)"*J1qqj\·®~Ù¸Qs–.Utl¬ ƒžmÔÈü¤ßËl’²[’º¾ø¢®]¿®ï~úI³–,‘s±bªT®œê<ù¤šÕ¯¯Õª©{ûöú;4TÛ÷îÕ¯;vÈÑÁA3ÆŽ5ndËׯ—ŠUÏž=s܇Á”Õo[2Ñ£G)6V˧OÏq‚>>Z´j•âOº¯6@vô2D*V,åF¼b@ÀŠQ°@Ll¬“’2}0ˆ%m€üF ß.^¬M;wÊh4ê­Q£´óàÁµ OÈÂ;ýúé~ýî» P¸°b+F°b+F°b+F°b+F°b+F°b+F°b+F°bvù5вuëòk(üOó† U¹bÅLÛ\ ÑîC‡ò)"ÜÕ³C‡|'ß €½†ͯ¡ð??ùûg¹°Ü}èk5€`u@IòÿÉ_zæÏÄv®×lµ¿`ºG‘àßÖ-[§¡½òï Xö¬@ÀŠQ¬@ÀŠQ¬@ÀŠQ “ɤySæiÖ„YjU£•†÷.£ÑXÐa=0:Öï¨Ã»tù&pI :7ê¬:%êȳ‰§¶¯ß^Ð!`ÕX«ÝŸ‡m­&IQQšì;Y_}üUA‡  @Ó>›¦¿Îþ¥ ÔÄù­¤Ä¤‚+Sׯ]/оÏ?«¡7R^$ÇbŽŠ‰ŠÑæÀÍyWfòêzÜÛï¼)ó´jÑ*uíßU=^ï¡?Oü©7_zS»6ïÊ“ñkµœôý°®Õ$iËš-ò}ÛW3¾˜¡¸˜¸<Àƒ‡ -š¹H•«U–$5z¦‘æΑ½ƒ}G•±È[‘öò°í{íÒµj÷T;íÞ²[u×UÙ eÕ¿M}6ô3™L¦<‰-#yu=îí7.&N[×nÕ÷ë¾×«ï½ªÑSGkáæ…2 šûÕÜ\¤`­–ý¾ƵÚ]­;¶Öø¹ãs}<6 €ÀC.áv‚n^¿)ƒÁPСX$>.^ïõ~OWþºR }ÿb¸Vî_©‡N(<,\˾_¦O¦}¢mç·åëµÌ«ë‘^¿G÷Õ‡>L5¿úÍêË­¾›.ž¿˜«ã€¬ÕrÖ÷øVû·¢öEsu<> €ÀCì—‘ÏIÒúåëå3ÀG³ýf›ß?qè„F½5JÞ}½ÕµqWÌ1)e¿™¿îÐvO¨fÑšÚºv«n'Èg€\ ®jS³ömß'I ¾¬nM»ip÷ÁÙŠmûúí3hŒ>{ï3uoÖ]Kç.•$ý¶ò7?}^á7Âå3ÀGs¿ž«ÓA§Õ·e_¹\Õ¯U?]¿v]ó¦ÌS-‡Zš5a–Å?‘I¯ïŒÜ½¡ßVþ¦ g.¨b劊‹‰ÓêE«uìÀ1‹æqׯõéOõåˆ/õZû×4Ùw²î$Ü‘$:zJ>˜ –Õ[*.6N¿ù±–m¨®»š{Ål2™´dÖ~g´<›xÊËÝKÏ]”$‹®Wzý6oÝ\už®“æZ/YÜ|WÈ=¬ÕRc­–ùZ 2c0ÝÇýÏ=zôbcµ|úô¬ru•ÿOþêгCN‡"nF¨aÙ†öù0 õÏÂ/ør°^t{QNlPåj•5Âk„V.X©§>¥§Ÿ}Z¾S|åÝ×[WlÔ‘[Gäè䨤Ä$µtm©ê5«kÁ¦æ¾Þíù®>œð¡ªT¯bQL+®ÔöõÛ5eñÙØØhæø™š4j’mY¤f­ši@ÇúóÄŸúý¿¿ÿ3𵪽œœôÛéß4iÔ$Õ¬]SûuÎÖõH¯ïôŒz{”.¿¤Ù«fk„×Mø~‚¾Ÿô½ÎX¨C7ÉÆÆ&ËyÌŸ:_ë—¯WÀöÙ±SÄÍukÖM\*hñ¶ÅºzCÃû׮ͻÔp½<èeݹsGÝšv“{Ww}ðM†1Ïš0Kå+•—§—§ŒF£<êx(2™öI¶®1€ü±nÙ: í5T¦ Y¯¿z "+¦åË—çx<î®Ó¨ä#%Íwv ò$IêóVùNñ•$õÐ[‰wµiÕ&I’];µëÖNûwìWä­HI)?[1/(ÃÃÂ5öݱ1~„lllÌc¾èù¢ÊU*—áy¥)%ŸI>ºxî¢üÇúë⹋Ù^PfǸoÇiÑ–E*V¼˜‚öéäá“öù0¸~@666YÎãæõ›šì;Y}ö•]»”9”)¥A>ƒ´ï÷}Z½xµÊU,g¾ãÎ{¬·w{\nõÜTçé::qèD†±…‡jþÔùêÒ¿‹$ÉÖÖVí»·WXH˜¶®Ùš«×kóêÍr«ç¦n¯vËö¹ çX«e޵¤fWÐ(œBÿU|\¼ùuõšÕUªL)_ 6kÒ²‰ªÍ?m:µIÕö~®×çï®ÑSG«l…²9Žä k5Ë=¬k5ø7 €ÒÕ¹_gŸ;^#^¡É£'ë‹a_蛀oÔ°EÃTí<½<•œœ¬5k´pÆB½òî+òôòÔÁµg땯T^Žë\ÂY ·tþÔù4ïÝÝp9#q±q \¨Îý:káô…:tÚâqs[Vó¨üXÊÏu.ÿu9Ýó]ŸtÍñØN ¹¢«!iÞ  7ÿûý\¯…3ʽ‹»?×8Çq€œc­v¬}­÷¢<ä2úÖ3)1IÏ]Ôº u6n˜üæù©m—¶iÚU®VYMZ6Ñ‚i äàè  .äÞÕ]NÎNz¿ßûÙ~0ÄÝ}T&ùNRrr²ùø‰C'´mÝ6I)?鈉Msî”ÑSôÆð74jò(+^LcÉö·Óõ]YÍ£A³r.álޓ箫!Š‹Oóíovb®Y»¦L&“üFú¥jwùÂe-š¹Èü:«ë•ѵ\(G‡4vmÞeqÌÀ2¬ÕRc­–õZ ÒÀÀCîÚÕk’¤Ûq·SŸí7[ûß/·zn*_©¼œœTªL©Tû¤ÜÕýµîñÊÍü%eÏG'GyôðPÐþ ÕnT;[ñ4hÞ@Ï·^›VmÒË­_Vûîíõ÷¥¿©/¿ûR’TÞ¥¼nݸ¥‡N(6:Vu×ÑÙãguíÊ5=ÓöIÒûãÞרwÇ*`v€úìkñøéõíè䘭9X:‘~#5fÐíÞ²[Í[7—$ýàÿƒ<½<Õô…¦’¤èÈhI’1Éhîûæõ›©öü¹7æ†-ªÎÓu¸$P ·äÞÕ]1Q1úõ—_å¿Ô_’ttßÑ,¯Wz×bßö}Z0mº½ÚM³$¥$&gŽÑãn«E›Ù¾V c¬ÕRc­–ùZí‹K‰ËøO\n¶Ÿ~úé§9=yùòåRb¢zzxdÙv¬¿¿Ú÷h¯'þóDN‡ËN>©¹_ÍÕŸ'þTÈÕ=Rî¹Tu‘½ƒ½b¢b´dÖ­ X£?®ÐÒ¹KõÃ7?hÃÏÔ¶K[9w6÷S½fuÅDǤúÙåQ=öÄcz²Î“َ˽«»"oEêðžÃÚ·}Ÿu}TMüHö)›"»TuÑÖµ[µ%p‹ê7«¯ˆ›òîë­†Íê÷gd0tñÜEýú˯ڹi§J>RRu×µhì{û®U·V¶ã·tµÕ–[=7ÍŸ:_Aûƒtdï=Rö}8áC íÞ²[ß}ý¢"¢§ºëjã/µtÎRÅFÇÊ`0¨Ñ³ôþ_ª˜Ý깩]·v ý;T{·ïÕŽ_wÈÁÑAcgŒUÙ eµgë‹®×½×"ñN¢^÷x]W/^ÕÖµ[Íÿl[·MgŸÕW?|•­ŸÈ{þcýÕ£}{ýç‰Ì×_'ÏÓÏ6è½Oß˧ÈX‚µZZ¬Õ2^«Ý½;7íÔl¿Ù:sìŒ"Â#äàè ò.åU¬x±_+¹ïÜÉsÚðó}ú^Öë¯åë×KE‹ªgÏž9Ï`Êéî­’zôè!ÅÆjùôéYäê*ÿŸüÕ¡g‡œ m^½Y‰‰‰jѦ…ÂîkW¯éì±³2™LþÅð‚Wƒ«~ò÷WÏ™¯¿–­[§^C‡ê‚éB>Eà~±V€Ûºeë4´×P™.d½þê1dˆT¬XÊx9ÄO€¤qæØzZ¡‡jÆç3ÔÒ£¥\k¹*.&NG÷ÕÎM;õÁ—ä¨ßƒas9Òcl€ÜÄZ ]¤Ñ©_']ºpI g,ÔäÑ“åäì¤n5Ôï~òâ+ PPX«²‹ €4lmmå=Ö[Þc½/G †‚ b­È> €2åèäXÐ! ¬Õ–àÞpÀŠQ¬@ÀŠQ¬@ÀŠQ¬@ÀŠQ¬@ÀŠQ¬@ÀŠQ¬@ÀŠQ¬˜]Ap¯uËÖih¯¡Ü·öÝÛkúòérˆü@Nøÿä¯=;t©ºà]þþ?tcóçO)èKÈOXjèÐ^Bº m°C‡ž䨆 Ë%ÅtÈä§,UX €ìX1 €€£X1 €€£«]Ð!äkš <¬)³¦¹ ÷PD*&“IóæMѬYÔªU Þ_F£± ÃÊÐÂ…3ԫ׳òôlši»ààËzöÙGµjÕ¢<©cÇú:|xw¶Ï»ß¹.QçÎT§N yz6Ñöíë³ÜrÊûWP9¥$EEEhòd_}õÕÇÙ…@¤2mÚgú믳8ð#Mœ8_ÑÑ‘JJJ,è°2Ô·ïÛŠŽŽ”É”œi»"EŠªL™ò*VÌ9Ûc\¿~-Ë6gÏסÿ{e£c1ÅÄDióæ@‹Ç¹Ÿ¹Ì›7E«V-R×®ýÕ£ÇëúóÏzóÍ—´k×f‹Ç€ûEN™ÖƒSJÒ–-käëû¶fÌøBqq1‹@¤²hÑLU®\M’Ô¨Ñ3š3'PööT&lmíT¡ÂÿeÙ®\¹ŠZµê€Ú¶í’­þ##oiذ—³l·víRµk÷”vïÞ¢ºu«lÙ êß¿>ûl¨L&“Ecåt.qq1Úºu­¾ÿ~^}õ==U n–Á`Ðܹ_Y46ärÊÔ„œò®Ö­;jüø¹…@˜%$ÜÖÍ›×e0 :”B!>>Nï½×[W®ü•eÛáÿÐÊ•ûuâÄ!…‡‡iÙ²ïõÉ'Ó´mÛù<¿žGîÓ‡NH5NýúÍäæV_/žÏÓ±à.rÊÔ”œòߊµÏ·±¿(B’ôË/?ÊÇg€$iýúåòñ Ù³ýÌïŸ8qH£F½%oï¾êÚµ±fËhL’$íØñ«žxÂN5kÕÖ­k•p[>>äêjP›65µoßvI)û tëÖTƒwÏVl7®Ð§ŸÑ—_ŽÐk¯µ×äɾºs'!M»}û¶ëµ×Ú©AƒGôê«/š?dÆ$ýþû ö²&LøÀâqûm¥ÎŸ?­ððòñ ¹s¿Î°í¡úí·•ºpáŒ*V¬¬¸¸­^½HÇŽÈó¹4oÞZuê<¦âÅKš¿y€¼DN™Öƒ’Sâá`WРpðôôR«V/iÕªErwïªÁƒG™ß ¾¬>}ž×† 'T¹r5á%_ßZºt®ž~úYùúN‘‡GOmܸBÍšµ’½½ƒ>ûl¦vìøU..UÕ¤IKI’‹KU¹¸TÕ‡N°8®ùó§jýúå Ø.;»"Šˆ¸©nÝšéàÁZ¼x›ù››7ôråB½öš·Î;¥¯¾úX½z=«-[ÎéÆP]»vE«W/Vß¾-»sç~Z»v©þüóD–·AO™2F—.×ìÙ«4b„—&Lø^ß?I¯¿î¡C‡nÊÆÆ&_çb4uöìqé—aÈ-ä”i=È9%¬w"K LWÉ’˜ï&4ÈG’Ô§Ï[òõ"IêÝ{€ïhÓ¦U’$;»"j×®›öïß¡ÈÈ[’Rn7ªR¥ºEãÞ¼y]“'ûªoß²³+"I*UªŒ òѾ}¿kõêÅæ¶öööš0á{=÷\;½ñÆ0y{Uhh°–-ûNUª<¦^½˜ûÈ ãÆ}«E‹¶¨X±â Ú§“'kذÏuàÀuÙØØäû\6o^-7·zêÖíÕ<›3X‚œ2k…-§„õ¡ˆ,…†þ­øø8óëêÕkªT©2 ¾b>Ö¤IKU©ò˜V®\h>væÌ1IÚ°a¹¤”[•Ûµëfñ¸GŽìU\\¬\\ª¦:ÞªÕK’¤½{·™9;—HÕ¦k×W$IÇ’$ ÙÚÚZ>VÏ?ßÎÜÆ`0ÈÓÓK»vmRXXˆÜ©ºu«yóÖZµj‘¤”kw÷®5” IDAT|I’žêxéÒeåèè¤ÐÐà Ï­PÁEŽJHˆ·x¼Ü2r¤_šåüœË矿¯Ñ£§ªlÙ ÙŒr9eötN ëÄ€ÈRçÎýtûv¼FŒxEÝ»¿®ÐпõÍ7jذEªvžž^ò÷«5k´_>>“´wï6 Þ_{ölUùò•äààhñ¸•+?&Iº|9ý'&¹º>™éùƒAO<ñ”Åãå¥üšËÂ…3äîÞE?—ý  SÞ?kš wÂÌd2¥{<))Q/žÓºuA6lœüüæ©mÛ.iÚU®\MMš´Ô‚Óäàਠ\äîÞUNNÎzÿý~êÞýµlÅÓ A39;—0ïqWHÈUÅÇÇ©M›Nž{õêE%&&Êãg¶Æ¼—bccî«)æ¸DŽiþÛìÚµ9瀅È)ÓzrJX7 €0»víª$éöí¸TÇgÏöÓþý¿kçÎMÚ·o»Ž?¨+Wþ›nÝ»¿¦+Wþ+/¯¡’$GG'yxôPéÒeU»v£lÅSªTé§C‡v™o—¤~ð—§§—š6}A’dkk«ÈÈ[Š‹‹•”ò—Îôéã4tè'Y~ ’•òå]tëÖ 8qHûömOµoEašËöíëµ`Á4%%%* `¶fkÉ’Y3f.\8“£˜ ;È)ÓzPrÊ»îžg4s' /~ IÒÉ“‡5wî×’¤5kT£Æô T¼xIÕªUO³gO”·wßTçÔ¨ñýøãoªPÁÅ|Ì㇎?(7·zæc¯¼ò®þüóDŽâêÛw Ê—¯¤9s&jÓ¦U*Q¢´Ê•«¨‘#ýÌm>øàKÍœ9^ƒw“‹Ë£rppTË–ÙÚ6#ýú½£­[×ÊÛ»¯†ÿBŽŽN9î+¯ærìØ Ü]·oÇëÈ‘½©Þ+ZÔ^{öd¼ärÊô=9å];wnÒÊ• $I;vüª¥Kç¨U«Ž*_¾RŽcFáA’¤ÿü§¦N]¢©S—¤y/9Ù(?¿yjÑ¢ÂÃæk×®êìÙcZ´h††ÿÂÜÖÞÞAcÆ|“ê|7·z©>¼³«M›ÎjÓ¦s†ï׬Y[ß|e?ÉÉÉ*]ºl¶Æ~òÉ:ÚµëJÖ -”s©Sçi<™³o‘ 7S¦ïAÈ)ïzæ™¶zæ™¶š4iagáAF™:sæ˜ÆŒ¬Ý»Snå.Q¢”ùqäÏ>ënþv »5*—e?¿yjݺcŽú¿Wtt¤ï¨jÕê2~nºw.PX‘S’S¢p ˆL>}T¡¡kÆŒÏÕ²¥‡\]k)..FGîÓΛôÁ_æ¨ßƒÃr9Ò´‚ƒ/ë³ÏÞ“Ï׺};^%J”RûöÝómüÜ”Ù\ °"§,È)A™êÔ©Ÿ.]º … ghòäÑrrrVnê×ïùúN‘Má}ŽŒ³s ݺuC}ú´ÔóÏ·Ó‚›T¬Xñ‚+G¬i.ä”…ƒ5Í9C™²µµ•·÷Xy{U||œe0 :,‹”(QJ?ýôGA‡‘+¬i.ä”…ƒ5Í9C»Ÿ§nä”@Á)¼÷ÚŠ‹‹)è(L&“N:ª;w :"‘müñ›¶lYSÐahõêÅêß¿Zµª‘ã>ŒÆ$8ð‡&M¥;~ÍðXnöHQXòË»L&“æÍ›¢Y³&¨U«>¼¿ŒFcA‡e±ÀÀ%záWuìX_QQ  €È–Å‹¿ÕåË Å£Ì_z©·ŒF£’’’rÜDZcôóÏó5sæx…„\ÍðXnö(\ùå]Ó¦}¦¿þ:«?Òĉ󩤤ĂËb:õU»v<ÝiQ„Ŷm[§={¶ª_¿w :I)›ÉVªTù¾ú¨_¿™¼¼ÞÍòXnö»Â–_ÞµhÑLU®\M’Ô¨Ñ3š3'PööT6•.]¦ C@!ÄC@`‘ØØh}ôÑZ¼xkA‡’ëŠ)jѱÜìV…5¿LH¸­›7¯?0O)²ƒ ,²té\-j¯ÇwKuÜd2) `¶NŸÒÉ“‡U¼xI;CÕªe¾/߉‡0[±±1ºté¼zö|C={¾![Ûœý‘ ‘¯ï@8°C•+?¦É“éñÇkÝWŒéÙ¸q…öîÝ&{{ýùçIÕ®ÝPC†ŒVÑ¢ö÷±fM€Fz[Å‹—Ô®]W©ô×7ß|ªºuëçŸ÷H’nÜÕäɾrq©ªààË ¿¡ ¾S©R|›àÁUóË_~ùQ»vm–$­_¿\—.×£>®·ß)ɲ\0'ùbfyߎ¿êÍ7;È`°Ñ·ßþ¢-ÚhìØwõÓOßé±ÇžÐ_ÌV“&-|Yï¾ÛS+VÖŒ?§cË–58°‹ZµzIݺ½*w÷®_X €°È† ?«^½&iŽÏží§òå+iܸoe4åáQG½{?§mÛ.døˆ÷ààËêÓçymØpB•+WÓˆ^òõ¨¥Kçêé§Ÿ•¯ï”lÅ–¯Ù³ý4r¤Ÿï¨{÷fòóûPsç®ÉqŒé™?ªÖ¯_®€€í²³+¢ˆˆ›êÖ­™ܩŋ·Yü-QÇŽ}´bÅ:wî”$©xñ’2d´Ö¯_žªÝС½U¶l 2Z’ôÒKõ4nœ·&MZhqÌPØÆüÒÓÓK­Z½¤U«Éݽ«e~Ï’\0§ùbfyßsϽ(žÚ¸q…š5k%{{}öÙLíØñ«\\ªªI“–’$—ªrq©ª?œî‡ïÖ¸q³Ô»÷‹®¬{"KÉÉÉ:~ü`š;ÏBCƒ5þTuéÒ_RÊž|íÛwWXXˆ¶nÍø)N LWÉ’˜÷U4ÈG’Ô§Ï[Ù.þ¥Œk§?þJÕ«×TÍšµU§Nc8qè¾b¼×Í›×5y²¯úö(;»"’¤R¥ÊhÐ íÛ÷»V¯^œ­˜ÒþååäT,ÕkƒÁ Zµêš_?ñÄS:sæX¶Æ€Â¤°ç—÷²$¼Ÿ|1«¼¯wïJL¼£M›VI’ì슨]»nÚ¿‡"#oIJùé²ÑhT•*ÕSõœœ¬¯¾úX 4§øîDÖ¢¢n)))Q%K–NuüðáÝJJJ”¯ïÛ©Ž÷êõ¦ìí3ì/4ôoÅÇÇ™_W¯^S¥J•Qpð•ÅggW$Õ­ÝU«Vב#{î+Æ{9²Wqq±rq©šêx«V/I’öîݦ.]^ÎQü¹»FBÂm­^½XAAûe2™ru ÈO…=¿¼—%¹ ³s‰ç‹Yå}Mš´T•*iåÊ…êÔ©¯$éÌ™c2“´aÃrõîý–6n\¡víº¥éû“O«\¹Š…ê)Ë(8‘%’ÑhLuüÂ…Órt,¦ñãçf«¿–-=¸D»woQóæ­¡øøX=ÿ|»\‰÷ß·Vç4Æ{_’$EF†§:^ºtY9::)44ø¾úOÑhÔœ9uüøAyy U½zMtäÈÞ\ò˃–_Z’ ÞO¾˜UÞg0äéé¥éÓÇ),,D—.Wݺekk«U«™ €S¦¤½ËÐÑÑIK—ÎU×®ýU¿~³_X €ÈRñâ%eoï ¨¨ˆTÇœrU!!WU±båTï…‡‡é‘GÊ¥Û_çÎýtûv¼FŒxEÝ»¿®ÐпõÍ7jذE®ÇžÓïU¹òc’¤Ë—ÿJ÷}W×'ï/Ð{$''ë7ü >¤÷Þ룵kªD‰RYOV‹=‘%ƒÁ  šëúõÔßZÔ¬Y[&“I~~#S¿|ù‚-ši~œœœêý¤¤D]¼xNëÖiذqòó›§¶m»¤÷ÞórÂÒ³Ò A39;—0ï»pWHÈUÅÇÇ©M›Næc–Ämgg§¸¸˜TßzÅÆÆ˜Ï Ú¯?þøÍ¼©«$%&&ò`´Âœ_¦—oY’ æ4_´4ï«\¹šš4i© ¦ÉÁÁQ*¸Èݽ«œœœõþûýÔ½ûkéÎÇÞÞA“&-Ôõë×äãÀ; €°H§N}uøðîTDÏ<ÓVuê<­ÀÀ%4¨›V­Z¤E‹fjÔ¨·Õ¿ÿ`IÒ¼ySÔ¨QY?Ú|ÞìÙ~Ú¿ÿwíܹIûöm×ñãuåÊS—Þyé¹u릢¢"”˜xÇ|ìæÍëºs'AññqÅ%IŠ5÷qï±R¥ÊhäH?:´K»wo1·ûáyzz©iÓ2Œ;½þkÖ¬­¨¨Íšõ¥þûß?5cÆçºs'AýuV§N1ÿŒù—_~Ôٳǵ|ù<;wR7n„êÌ™cºq#TcÆ RÏžÏèÒ¥ó™^#(L k~yíÚUIÒíÛÿì)hI.˜Ó|Ñ’¼ï®îÝ_Ó•+ÿ•—×PI)?ïõðè¡Ò¥ËªvíF©æ—’{Irs«'oï±Ú°ág}ûí—æ6ä“ €°ˆ§ç+*UªŒŽM½ÁüùÕ¹s?<¸S_|1LGîÓ×_/PéÒe%¥ÜéæèXL66ÿüQ«U«žÎ;%oï¾êÛ÷uéò´Z¶¬®víž2ïÞy÷Z¹r¡ŽÝ+£1I&|¨˜˜(ýüó|<¸S&“I_í£¤¤ÄLc Ú¯iÓÆI’V¬øQÛ·¯O÷˜$õí;P³f­Ôœ95v컚2eŒÊ•«¨‰ç›cº7îŒúzí5oµnÝQ³fù郼ôÜsíÔ°a µnÝQ×®]U½zMÔ·ï@:uT£F½¥ÿû¿GõÑGeoï éÓÇÉÉÉYÁÁ—uäÈýô? ðà(ŒùåÉ“‡5gÎDIÒš5 \¢èèHI–å‚9É-Éûîòðè!/¯¡rs«g>öÊ+ïêí·Sß1¸Dk×.•$M›6N/žSÓ¦/ÈÖÖV_í£?~S—.'Ÿ|L÷ñ›Â=zH±±Z>}zÖ¹ºÊÿ'uèÙ!Óvë–­ÓÐ^Cuá?u,lŽ?¨éÓÇiöìÕ÷ÕÏæÍ«•˜˜¨-Ú(<ôQð† é!‹ÕôåY>@^q5¸ê'õìùúkÙºuê5t¨.˜.äSdð` ?Í;ä—|2o¸º²Uÿ2]ÈzýÕcÈ©X1-_¾<ÇqñX¬víFêÔ©¯æÍ›¢×_?G}œ9sLcÆ ÖîÝ)·V—(QJÕªÕ$=û¬»V®\kñZ»ØØhmÙ²FC†øt(-ä—‹|òáÃO€‘-:ôRÿÑæÍ9:ÿôé£ ý[3f|®“'ëöíx…‡‡iëÖµš:õõìùf.Gl½Îœ9¦÷ßÿLÎÎ% :È6òË‚C>ùðá@d۳ϺçøÜNúéÒ¥ Z¸p†&O-''gÕ¨á¦~ýÞ‘¯ï”L÷d@j¹õX{((ä—ƒ|òáCùÊÖÖVÞÞcåí=Vññqrpp4?ùK‘_–£ˆãèèTÐ!¬ù%9ÅL&“N:ª;w :Xˆ rͦM«Ô¢E?ºÀb0™Lš7oŠfÍš V­jhøðþ2On0“tàÀš4i”vìø5×ú=uêˆæÏŸ*“É”k}@fÈóV~Ì-0p‰^xÁU;ÖWTTDºmÈ7  €È5ŽŽÅT¦LyÙÛ;X Ó¦}¦¿þ:«?Òĉ󩤤ċ'7;v@?ÿ<_3gŽWHÈÕ\ë×Í­¾jÕª+?¿‘¹Ö'd†¼1oåÇÜ:uê«víºgÚ†|³ð¡ˆ\óÌ3mxHUªLÓ¦}¦+Wþʳ1 0°æ¼±0Î|³ð ø0™LZ²d–F~GžžMäå宋ÏI’N:ª >PË–Õ«?~S –U×®ÓüzâÄ!õ–¼½ûªkׯ ˜-£1I’yKË–}¯W^i«M›Ve«ïÌⓤC‡v©E‹*úý÷ Îñ—_~”ÏIÒúõËåã3@³gûiÍšÕ©SB-ZT‘$EGGjúôqªQÃVÝ»7ËÕk°cǯzâ ;Õ¬YT[·®UBÂmùø ««AmÚÔÔ¾}Û%IÁÁ—Õ­[S œù-Ó™±d^’tãF¨||húôqòñ »*"âfª¾œœŠ©víFš1ã‹ÇàÁFÞø`çÍMÊûl¦*Uª"—ªjÒ¤¥$ÉÅ¥ª\\ªê£&æø¿©%󒤡C{+&&ZC†ŒÖøñsuõê5nœwšþ4h¦WXÍÆ·²‡¼ñÁÎ3š›”y^˜[ñ>¼[ãÆÍÒìÙ«åîÞ5Õ{ä›…@+¬ùó§ªK—þ’$[[[µoß]aa!ÚºuÊ•«¨:už–$y{Õã»ÉÍ­žêÔyZ'N2÷³`Át•,ùˆy/Aƒ|$I}ú¼%_ß)züñZjÓ¦sª±-é;«ø$©eK;­Núæè888¥9æäT,[qZr $©wïJL¼cþ6ËήˆÚµë¦ýûw˜ÿ"JH¸-£Ñ¨*Uªçh>–ÎK’ ƒjÕªk~ýÄOéÌ™ciÎ+S¦‚¢£#uþü©ûŠ Àƒ‡¼ÑzóF)ë¼ð~âINNÖW_}¬ š«wïéŽO¾Y8ØtÈ[‡ïVRR¢|}ßNu¼W¯7eoï(I²±±•$ÙÚþóÇ¡bÅʺté¼ùuhèßæoV$©zõš*UªŒ‚ƒ¯˜ÙÙ¥ýã”Uß–Ä—r¾­…3ΙܺMš´T•*iåÊ…æ¿xΜ9&£1I6,WïÞoiãÆj×®[žÎç®Å‹·JJù°^½z±‚‚ö§ûö%JIJ¹5¼fÍÚù€Â¼Ñ2jÞ˜U^x?ñ|òÉ`•+WQ­[wÌp|òÍ •»pá´‹iüø¹÷ÕOË– \¢Ý»·¨yóÖŠŠŠP||¬ž¾]¡ˆ/?Xr ƒ<=½4}ú8………èÒ¥óª[·±lmmµjÕ"óç”)‹ó%f£Ñ¨9s&êøñƒòòªzõšèÈ‘½iÚÙØ¤Ü œœœœ/q(<ÈsOaÌ³Ê ï'GG'-]:W]»öWýúÍÒ¼/‘o­œƒƒ“BB®*$äª*V¬œê½ðð0=òH9‹úéܹŸnߎ׈¯¨{÷×ú·¾ù&@ ¶(ñåK¯§§—üýÇjÍší—Ï$íÝ»MÇ÷מ=[U¾|%988f0JîINNÖox¨L™òš9s…$iÙ²ïÒm.)å¶vòÆÜSØòFKóœÆ3|ø:}:Hï½×Gk×5ßí÷oä›…{Z¹š5kËd2ÉÏodªã—/_ТE3-î'))Q/žÓºuA6lœüüæ©mÛ.ùŸ%ߤ÷ÓV)å󸸘TŽÆÆÆdûÛK¯AåÊÕÔ¤IK-X0MŽªPÁEîî]åää¬÷ßï§îÝ_ËÖ¸Éj^AAûõÇ¿™7m•¤ÄÄÄt¯Ó­[7äì\"ÍCDX?òFëÈÓ››¥yaNã±·wФI uýú5óSˆïE¾Y8p •{æ™¶ªSçi.QBÂm¹»wULL”~ýõùû/•”ò´$IæÇ’KÒÍ›×Sí[0{¶Ÿöïÿ]nnõT¾|%999«T©2ªRå1s›ëׯIJù殬ú¶$¾]»6kРnš0á{µoŸñ#Я]»*Iº};.Õñš5kkÆŸ5kÖ—òðè©õë—éÎ]»vE§N‘›[ý\»wuïþšFŒxE3gþ")å¶h Ú¯Úµe8‡ŒÄÄDI’âãc-ž—Á`”ò8øºuëØ±:wî¤nÜÕ™3ÇT¶l•-[ARÊžíÚuËó=3>äÖ‘7¦7·ìä…Ù‰'..Ö|-ÜÜêÉÛ{¬¾úêc}ûí—zçSµ%ß,¸ÐÊ ÍŸ¿Q;÷ÓÁƒ;õÅÃtôè>}ýõ•.]V»wo1?5iÊ”1 ÓÊ• ´_11Qò÷+£Ñ¨ZµêéܹSòö}_P—.O«eËêj×î)…†kÏž­Z°`š$iÙ²ïµsç&‹úNNNÎ4>)e#W'§b*R¤H†ó}œœœœ%I·oÇëðáÝ9Š Àƒ¼ñÁÏ3š›¥yavâ \¢µkS ¯Ó¦ÓÅ‹çÔ´é ²µµÕ×_ûèãß4?…|³ð0˜2ºÿÕ=zôbcµ|úô¬ru•ÿOþêгC¦íÖ-[§¡½†êÂ…‡…<°yój%&&ªE‹6 Sxx˜®]»ª³gÉd2iøð/ :ÄRý~üþû%%%>TÆry£õæ9E¾Y¸PD–NŸ>ªÐп5cÆçjÙÒC®®µ££G÷içÎMúàƒ/ :Ä<—W×ààÁ°¬å‘Ó§ƒ©?œP`1°äÖ™7æùfáCYêÔ©Ÿ.]º … ghòäÑrrrVnê×ïùúN‘õo%i× V­ºªU«nA‡À XcΔ]\ƒo>‘%[[[y{•·÷XÅÇÇÉÁÁÑü$¡‡×2FÎÄ5@áFÙâèèTÐ!8®dŒœ‰k€Âçá¹ÿxQ¬@ÀŠQ¬@ÀŠQ¬@ÀŠQ¬@ÀŠQ¬@ÀŠQ¬@ÀŠQ¬˜]A€‡S||œn+::B·oÇ+!á¶’’’ý¿÷cuçΙL&EGGH’nëöíxIRtt¤’““ÍýEEEÈd2e2^¬ïdø¾Á`Pñâ¥2ÙÙ¹¸lmíþõº„lmmekk'gçâ’$'-j/I*Y²´$©hQ{9::ÉÆÆVÅ‹—£c1ÙÛ;¨xñ’™ŽȹèèHÅÄD)::Rwî$˜óȨ¨%''+::BF£Q11Q©òÑÄÄ;Š‹‹MÓ_rrJÛô8;—mšãNNÅT¤HQIR±bÅegg§âÅKÊÆÆFÅ‹—’J”(õ¿×%U´¨½Š/)gçäŒÈUa±¸¸EEE(::RQQæþý:::R±±ÑJH¸­ØØhÅÅÅü¯Ð¥øøØÿý{d¶Æ-Q¢” ƒŠ)*GÇb’Òãÿ)¼¥ÇήˆŠsÎðý„„EDü•áû)9¤ŽûnÑ11ñŽâÿŸ½ûªè8üKg$@ !¡zt¢ôŽÒT@‘EDT¤#]¢ôÞAB Ò{ïÉf¿?BVl(Ù$œ÷yò${ïÜ;gv“ÙÌÙ¹s3òßRSSP(rÕjWAÜùÉ@K 000Ä C 07·ÄÈÈ33 ÌÍ-U_ÍÌò$…B!„¢"ÉËË#!!–øøâãc‰"..Zµ­`ú`²/55™¤¤„'žWKKëþX3?ñ¦££ƒ©©y¡}E)Ÿ>H©Tv¯ÈòNVIMMF¡P’’„R™WìDÈŸXò`B°à»……VV¶X[ÛaccÿË++[¬¬×1zƒ IDATlÑÖ– >Ea’|Iåw–±$$äÅÅŨ:Ñ÷=˜è+*±¥§§_(9UÐ!bmm‡‰‰)†˜ššßOÒbnþ_²ËÜÜCŒŒŒU/ Ú_^|Š™™Nvv999¤§§’––ŸMMM¾?Ó1‹ääÿfB&%%Eff:aa÷HOO{ ÁšXä'QÀCIB+llòß,-m°¶Î°µuP½!X[Ûª>‰B!„BˆÒEtt8¡DD„Nxx‘‘¡ÄÆFCBBl¡«¿´´´°¶ÎOtÙØØanžŸssóP%ÇÌÌÌ17·ºÿsÁÕ3³üä]Y›@‘”” ºú-++«P2399””äG’œQQaÄÅŨ¡&µµµUÉA;;G*áì슃C%]prrÁÁ¡66ölµ(m’¬@²²2‰ŽŽ ::œÈÈ0bb"ˆŒ %&&’ˆˆPU‚/11…BQèX33 llìU‰!{{'¼½kaaaõÄ™gFFÆjmÙ¦£££zSyÞo.¹¹9ÌÂLJJxà“¯üÇqqÑ\¿~©P¢÷á×ÝÔÔ\•´·wÂÁ¡Î888coï|ÿçJ2õ\!„BQb))IÜ»w“{÷n|‹{÷n|‹ðð¢¢ÂÈÎÎR•µ´´ÁÁÁgg7*Wö Aƒ÷g·Ùakë JúY[ÛU¸ÙmcFKKë§:>//O•,¹?K2†øø˜ûy0._>Gtt8‰‰ñªãôõ î']©\Ù77*WöTý,ãÀŠE€å„B¡ 22”Ðл„…Ý%$äááÁ÷“{!ÄÄD’«*¯­­­­ƒ*‰ãáQÆ[«|66öªÔÊÊFf‚•#ººzªY|%õ¸™Ÿo —/Ÿcÿþ¿ˆŠ /ôflddŒ££ vvŽ89¹boK•¾ªbhhô<›)„B!„( ÁÁ·¸ví"7o^áÖ­ ûɾ[ª1ªŽŽ®*±çááCË–qttÁÙ9láää*ã‰gP0þ·µu(¶lffù ؈ˆP"#C æÞ½›?¾ðð`ÕÄ++[ªTñÄÍÍwwo¼¼jP­ZMÜÜ<ÐÑyt½CQ¶I° ‰ŽŽàÞ½›„†Þ%4ôÎýïù ¿ˆˆPrss000ÄÅ¥ ÎÎnØÙ9Q³f}lmqvvÅÖÖ''lm ­‘'P¢ÄaBB,ÑÑÎ"ÍO4GF†rãÆÂÂîZWÃÖÖ—*TªT¥PrÐÍÍWתòû(„B!D9v«W/póæUÂïæÍ«dgg¡­­‹KU<<ªS·nºu{ƒÊ•óg”¹¸TAWWOÓá ò—ÛªZµU«V+rNN6¡¡wU36ïÝËÿ¾aÃï„…Ý%//C<<|ðòò¥ZµšxyÕÀǧÎÎn¥ÜQ2"/e¹¹9DD„pýúenÞ¼BpðmBBnH\\4¿®ž££ nnî89¹P§N#ÜÜÜquuÇÍÍJ•ªT¸)Ï¢ì)Hz{×zl™üËÎÃU¿ÇßÝCHÈmU‚PWW''WÜÜÜñôô¥Zµ¸ººS­ZMììK«IB!„B5¥¤$qíÚEΞ=Ê™3G øøÔÁǧիׯÄĬ4›"„B!ÄKK©Trýú%Nž<À™3G¸pá¡¡wpsó v톌1ÚµâãSGu·\ñrÑÓÓÇË«^^5€>ªí©©É\¹ò/§ <ÍÎX´h..U¨S§ ´ I“Wðòª!ù ‘àsœœx?Ñw«Wó¿®_¿DvvÖý?üi±~~Ãpw÷ÆÝÝ›ªU«¡¯o éÐ…x¡ÌÍ-©U«µj5(´=//ðð`nß¾Æ;׸u+ˆ›7¯²cÇ:’“ÑÒÒÂÕÕ_ߺ÷‚ù‰ÁJ•*k¨%B!„BTyyyª„ßÉ“9uê ±˜›[Ò°aKúö}‹ZµR»vç¾1…xy˜ššÓ¨Q+5j¥Ú–˜G`à.^<Í¿ÿždîÜ©$''beeK£F­hÒÄŸÆý©V­¦$K‰$KH¡PpíÚEÎ;ÆùóÇ9wî8ÁÁ·€ü»ùúÖ¥I†Cõêuðòò•µ„xHþú ùk¶jձо‚uE ê7.#$ä6J¥KKkêÖmBýúM©_¿uê4’Ë „B!„PCzz*‡ýÍž=[9p`G¡„ß{ïM¦qãÖT¯^Gnî ž KKZµê¨ï) ‚‚.pâÄNž< JZ[ÛñÊ+¯Ò¶mZ¶ìˆ±±‰†#¯¸$XŒääDU¢ïܹc\¸pŠ´´LMÍ©W¯ =z RMƒvp¨¤ép…(÷*UªL¥J•i×®«j[ZZ AA\¾|Žs玳víæÌù¼½káçלºu›àç×WתŒ^!„Bˆ²#**Œ½{·±gÏVŽ߇B‘KݺM1â#š7o' ?Qjttt¨Q£>5jÔç­·Æ¡P(¸zõ_ŽÝÞ=[=ºzzú4mÚ†víºÒ¦Mœ5v…" À‡<øKxôèNž›G›6]°·wÒtˆB ££CÍš~Ô¬éÇÈ‘ILŒãر}ìÛ·™3'0uê{Ô«×”ž=Óµë@¹òë9 ¡Csøðß;¶—¤¤œÝhÙ²Œ¤Q£VØØØk:L!ÄìíhÛ¶ mÛv 33ƒÀÀÓ9ò‡ýͺu¿ ­­CýúMiÙ²#-[v fM?Y_B!„BT8J¥’Ó§³qãïìܹìì,üýX¸p#-[vÄÀÀPÓ! ñD––6ô!  ™™>ü76üÎgŸæë¯?"  ½z ¥AƒšµÜzi€‰‰ñìßÿ›7¯àر½âçל‘#'Ò¼y;jÔ¨/‰!ÊCC#Õ³ãÆ}©úéèÑ=¬^ý3ß}÷ ŽŽ.tìØ“€€>òÆ!„B!ʽ¼¼<öïßÎüùÓ <§§/£F}BïÞÃd‹(· hß¾;íÛw'));׳iÓrúõkIõêµyûíéÚu ::/mJ멼TÏVZZ ÿü³…mÛÖpäÈn éС;¿üòM›¶‘OE„¨@üI©Trõê¶oÿƒmÛÖ°lÙ<¼¼jХ˺v€««»¦ÃB!„BmÙÙYlÚ´œÅ‹gz‡Îû0cÆ"|}ëi:4!ž+ +ú÷Aÿþ#¸|ù‹ÍfâÄ7™?ÿKFŒ˜@ÏžƒÑÓÓ×t˜åÂK±ˆÝµkùä“‘4iâĤIo£§§Çœ9+8u*Šï¾[¿€$ÿ„¨À´´´ðõ­ËG}ÍÁƒwX·îMšøóûï?àïïÁo´e÷î-( M‡*„B!ÄíÝ»öí«3mÚû4mú »wñÃk$ù'*¼5ê3oÞZþùç*·æóÏGÓ®7û÷o×thåB…Nž={”¡C;P›Ó§1aÂ,NžŒdÑ¢?yíµþk:D¡Aé驚¡ÂHKKÑtjÓÒÒÂϯ9Ÿ>Ÿ'Âùå—¿000ä½÷zѶm5Ö­û•ÜÜM‡)„B!D!‘‘¡¼ûnOFŒèŠŸ_s¸ÅŒ‹©\ÙSÓ¡=¥RÉ•+ÿ’¥éPD9S¥Š_}µ„nQ¯^SÞ~û5FêMTT˜¦C+Ó*dðæÍ« Ö‰¾}[••Éï¿ÿÍß_aРQXXXi46…"—Ó§óÝwŸpèÐߥR§R©ä·ßæòóÏ3iÓÆ‹?¤Ñ™N»wo¡ysWnÞ¼ª‘úÿüsƒµ£M¯§>GQ¯£&^ÛEݶ¬Y³ˆZÓ¾½O‘ûþ‡½{·qåÊy–.ý¥Rù¢B~*::º¼òÊ«üúëvvï¢yóvLúíÚy³sçM‡'„B!›7/§C_®_¿Äòå»™3g%•4ÖSÛºu5¯¼âA—.õHNN,ññ2Æ-Zi=/eeœçàP‰ï¿_ͲeÿpõêÚ·÷aË–•‰¥<¨P ÀÜܾùf¯¾Z‡¸¸V®ÜËš5iÙ²C™¹¡G`ài6lXÊÂ…_Z*uþøãܾ}wÞù˜Ù³—’’’Tª3œ¢£# =622ÁÆÆ^c—]¿öZ ¹¹¹O}Ž¢^GM¼¶/ŠºméÛ÷mòòòÈË{ôMeÕªŸ¾EÛ¶]ðõ­‡OfÍšø"Ã~&Uªx1cÆ"öî½NÆ­xÿý¾ ÒˆˆM‡&„B!^RJ¥’¯¾ú>Ê€#ر#æÍÛi:¬gÖµë@:uêýÔÇË·h¥ñ¼”Åq^‹íÙ¹ó"ýú½Íøñƒ™9sB™›|RT˜`dd(}û¶`ùòùLú›7Ÿ¢iÓ6šëõê5eÈ÷KµÎ•+ââR€ Z°xñÖR똒’7îBÛZ´hÏÖ­gqu­Z*1™Ã·ß.géÒïùâ‹4OYT!î|ïÞM j‡‘‘ [¶œÆÃ£º¦Cz¢Ò¼CMVV&qqÑ™™‘‘Îô'$äv©×]Šz+Ò݇ž¶-ii)|üñ[¬Zµï‘}o¾9wüý;—ù;ïÖ«×”-[NóÙg£9²;³g/¥{÷7Š?P!„Bˆç`ùòY³fó篧C‡š§Ì1nÑ^ôóR^ÆyÝ»¿¡¡ï¿ßwwo ¥ÑxÊ’r?0==•·ß~ {þøãp™OþeÛ¶5Ô®mN󿮤¤$1þt¼¼tèÝ»©ª\ll“'gþüéLž<œwÞéAbbÜcÏ»iÓ2&OÀŽë™ w怜]»€þÙÌÍ›W‰eòäá,Yò-II ¬[÷+ƒ·g÷î-…γk×F>ÿ|4_=žaÃ:3gÎÕ‚°êÆsöìQš7wåàÁj=÷11‘ŒÙúõ­éÚկК J¥’Õ«æÓOߥgÏÆ Ò»wo¨uÞ‡=©mê8tèoªUÓÅÛ[Ÿ}ûþ"++“É“‡ãá¡E»vÞœùd³fMdÚ´÷‰‰)<ý}íÚ%èëàééûÈ±ÆÆ&ÔªÕ€ f¨ÝvM222fö쥼õÖ8&LÆ™3G4’B!„x „†ÞeæÌ |ðÁçe"ù÷¤±è³ŒU´wï6¼¼t9²ÿü³¹È22Æ-ÚãžuâP·LyçuêÔ‹÷ߟÊÌ™¬épÊŒr?pÞ¼i$%%°rå>,-­5ÎSéÒe7þÎW03³`ôèOÙ±c}¡rcÆôÇÖÖÑ£?àµ×ê2}úX¾ûnE‘çíÙsmڼƖ-+éС£F}¢ÚW\}vvŽ\¹ò/!!w˜={"o½5ŽAƒFÓ«W¾ýö~øa ›7¯àÀÌ» mmm.üŠO>AåÊtëö:ýµ–ë×/ñÕWùæÍ›W¹qã2GîaàÀwTõ-]ú=;v¬gÍšèêꑘG¯^M9sæ«VíW;ž””$ãHJJ(öyÏÊÊ`Ñ¢YLœ8‹œœlz÷nʬYX²d‹ÍÂÞÞ‰éÓB¡PP›þý[±ÿ­ÝAº¸¶©ó M«V èË®]iÚ´ †|ñÅBúgg77öÀÙÙ gg7&L˜©VÝ×®]dÓ¦eܽ{ƒeËæÑ©SoÖ­û…¬¬¢““[·®fÙ²y¬^}CbißÞ]Ýÿº’;7P·nãǶ¥~ý¦,Yò-3f,FGGGíçQ“&L˜É;×ù裡ìÙs­ÜÄ-„B!ʧŋgãääÊÈ‘ec í'EŸv¬ò°sçŽ1}úÏôï?ü±qÈ·äÏ‹:ãQuÊ”·qÞ»ïNbóæ,^<›Ï?Ÿ¯épÊ„r=0;;‹5k3räDœ5Î314|4¡dllRè±––>>uT«U«IPPà ©ÏÎΑڵ0vì4<=}ñõ­KíÚ ¹té,ññ1L›ö>ãÇ…¶vþ¯Ò€#èØ±'vvNEÖëééC»vÝ m‹‹‹fΜ) øººzXZÚðÞ{“9yò þ¹J­xüý L¡k×Å>::ºLšô îîÞx{×¢víFªsEE…³té÷tï>è~Y:wîMLL$ûöm+öÜ%i›ºú÷NNN¶êS%]]=:uêÅ©S‡ToYY™( \]ÝÕª»U«Nøù5G¡Pнûôï?œM›Nù÷”‘‘Î×_gèÐTëHXYÙÒ°aKU™¼¼<.^<ƒ¥åã×ó°±q %%‰›7¯¨ÝvMÓÒÒâ“O¾#,ì.ìÐt8B!„¢‚Ûµk#}ú¼©ú?^ÓŠ‹–t¬ò ¼¼<¾ùfõë7{bò¯82Æ}”:q¨S¦<ŽóôôôéÓçMvíÚ¨éPÊŒr¼wï&©©É´nÝIÓ¡”ŠU«öñÈÊÊdݺ_¹páé/¬>míü¬½ŽÎ³»]HKKàÌ™#äååZèÔÊÊ–… 7âééóØó>8[ àüù¤§§áììVh{›6¯pâÄ~µâ) î§ ººz…Îåææ®zs:wî¹¹9L™2’É“‡3yòp¢£Ãé×ïm ŒÔ:IÚ¦ŽÆýqu­ÊæÍÿÍø D¡ÈeçÎüO¶víÚH§N½JTwÁóP¹²çë?}ú0ÑÑx{×*´]_ß@õsrr¹¹9XXX=ö<ææ–@þe剫«;U«zz3B!„âyKJJ ..šZµh:•âÆ¢%«<è³ÏF‘’’DÛ¶]^x;*ú÷aêÄ¡N™ò:ΫU«11‘¤¤$i:”2¡Ü_ü2Q(,^<›‹Ï0dÈêÖmÌùó'4Ïõë—ÈÍÍA©T>ÓB£áá÷HJŠ/´ÝÊÊ##c¢¢ÂŸ)Nu=؆[·®bdd¢šÖý´žgÛ´´´èÙsóçO'&&’{÷nR§N#tttزe%ýû`׮̻ê¹× pûvð䛃¼)Š'”ÉÿÜ!//¯Dõ !„Bñ2(—”¥ÿ—‹‹–t¬ò ##cÖ®]Bƒ¨W¯é#ûKSE㪇:eÊë8O©Thä†1eQ¹žX¹²'¦¦æìß_ñ/ÉËËËã­·¸yó n¤qãÖš SSs²²2‹œâ[’\¸¸äº\ô”4qcCCc"#C‰Œ }d_||ŒÚçyÞmëÙsyyylÛ¶†+0xðûôì9„3gŽpüø>ìí044z!u$þÂÂî=¶Œ™™†$''>¶Lbbþ‹c‰ê×´àà[ܹs­L}+„B!*ssKlmÊÌ•'êŽEK2VyЇÎÀã:|0à‰ãˆÒPÑÆ¸êÄ¡N™ò:λxñ ööN˜ššk:”2¡\'õõ 0`$‹Ï&**LÓá<]]]ÒÓS eÔÓÒRUÙó Nqøð?ªTrrrTíÇyÜþâêSGÁzß}7¥Ðq—.eÿþí@þ§ii©O""[ffᵋ«P]'¯P䪎‹‹‹V­õP¿~3Z·îÌîÝ[x㶬X±€™3'°jÕOtìØ{{gb¹té,'O ##èèà¿™t––6Lœ8‹³grìØ^U]¿ÿ>ž=‡Ð¤É+jÅpôèêÕ³bçÎ O| âHNN,ô†Mvvé´hÑžÚµ²uëjÞ{¯[¶¬dåÊ…|òÉH ý:>¼Mݶ•DïÞà ¹Ã!c€ü)ó}°²²-4;MݺÓÓSÉËS<òiÎÃmñókN“&¯°qãï¬ZõéžæÌ™#ÄÇǰmÛ22ÒéÚu çÎ{ìó¹sÇèÔ©W™¸3”:òÁ8p`'ß|ó{¹‰[!„B”_#FL **ŒŸ~úZÓ¡”h,ªîX ==œ¡Päâë[—±c§±sç†bÛ,c\õŸuâP7Öò6Î[°`11Œ1AÓ¡”:ŸþùçO{ðúõë!'‡¾Å–6oût¦ZjO,wãò vnØÉ¨–žž>þþY³f›7¯¤iÓW°¶¶SëXM¸páóæ}Á½{7‰ÆÉÉ•*U¼¨Q£7n\fË–•œ;wŒ!CÆI•*^89¹Ò¬Ybc£8rd7.œ¤cÇž4kÖ†½{·L@@ßGÖf»|ùK–|Ãõë—ˆŒ ÅÚÚgg7 ‹­/22”_~ù–ääDÒÓS©S§»vmbíÚŤ¥¥ ¥¥Eƒ-éÔ©II œ;wœ“'P¹²<[u—Xgg7öíû‹½{·R¯^SãX¼x6aa÷ˆÂÅ¥*nnÔªÕ_ߺ,]ú=.œâüùX[Û2aÂL´´´8vl¯ZñDD„°oß6ڵ늻{ÑÓª7o^Áºu¿‘‘Njj ~~ÍØºu5ëÖýBffééi4kÖ–€€¾DE…qâÄúCC#¦M[€­­C‘¯cRRB‘¯mqm+)wwoRSS }‚æì\™ªU«©fé(®îµk—°víb22ÒˆŽÇÅ¥ vvNý=íØ±ÑÑüñÇ/¬^ý3&&¦ØÙ9Q½zmêÕkJ•*^T¯^› –R»vCœœ\ Å“™™ÁŒÿcÖ¬¥XYÙ–¸í¥-==É“‡óÇ¿ðí·Ëð÷/¾¯+°cÇzÐÏ! ¯úÇ!Äó6oÚ<útîLjOþÿëòlع“>ÿ ”"Bˆò¡¤ãÓçÅÜÜss+¾ùæcªU«‰§§o©Öÿ GGµÇ¢êŽU¶n]ÍŠóILŒ'== wwolmÙ¸q)Gî!22//_,-­ Å"cܢǸOz^ÔªSÆÛ»V¹çíØ±ž/¾ÃÔ©óhÖ¬m©×?oÞ´å¿>ÿ øÿ¿ÖïØúúôíÛ÷©ãÒRw éôéÓÒÒX?~ñyx0ïy¼Ú÷Õ'–Û¾n;cúáÖ­’…ƨQ} ºÀ„ 3yýõw ÝIGQz.^<ÃüùÓY´èÏBÛçÌù33 †¯¡ÈÔwúôa&ON||,sæ¬(ñ¥¿£G÷“4æ¯/¾BˆÅC˃?æÍ£ï«OþÿkÝöíô3†[Ê[¥™B”O;>}^¦MÃêÕ?3kÖotïþ†Fb¢@yçmÚ´Œ?~›AƒFñé§ßk$­å¿”·Šÿÿ«ÏèÑ`b’?ï)•ûK€ 88Tâ?ñæ›ãøê«ñtëրÇÿÑtXB«A»b¿öîݦé0K¤V­tí:ß~›«ÚvðàNrssÊě“„„Üaܸ70 5..Uٱガû'„B!4bêÔxóÍÿ1~ü`¾üòdffh:$ñ+Ë㼌Œt¦MÄ Ã1â#¦L™[üA/™ 5ENGG—qã¦Ó£Ç fÌÇС©_¿£F}B«VT·¥¢,9sFý» —'¯¾ÚÇÿaÏž­TªT™””$&L˜©é°ëöík,^<›Í›WP©Re~úiíÛw×tXB!„â%¦¥¥Åĉ³ðñ©—!’ IDATÃÔ©ï±wï6¾øb!-[vÐthâ%UÇy‡íbêÔQ$&Æñý÷«yíµþ§¬ªP ÀU«Vã—_þâßOòã_ðöÛ¯Q¥Šƒ¢[·7YG@ñb<ø‰O FR4…"—ƒw±bÅ|þ‡Ê•=ùê«Åtïþ†,! „B!ÊŒ®]Ò¸±?Ó§ÀСéÒe“&}ƒƒC%M‡&^BeeœÊ×_Ä_­åÕWû2eÊ÷ØÛ;i,ž²®BO‰«[·1¿þº]».ѤÉ+|ûídš4qbøð.lݺZu×!!ÄË#//Ó§3uê{4nìĈ]Q(,Zô'»wÑ«×PIþ !„Bˆ2ÇÁÁ™ùó×óË/qþüqZ·vgòäáܽ{CÓ¡ QªîܹΤIoãïïÁ… 'ùí·Ì›÷‡$ÿŠQ¡€<=}ùòËŸ9q"‚¯¿þ…¼¼<ÆBãÆŒ;½{·ÉZ BT`J¥’K—Î2sæZ·®Jÿþ­8}ú0o¾ù?öï¿Åòå»iÛ¶‹, „B!ʼW^y•={®1mÚNžD[[›š5ý¨YÓ‘#'’ššÌ¹sÇ9>ÿkîÜ©¤¦&cbbF:¨_¿µk7ÄǧÎÎnš_ˆ )%%‰«W/pùò9Ο?ÎٳLjŒ EGGŸ:ԯߌ7Þx?¿æTªTYÓá !„B¡1†ô!  QQáìßÿ{öleöì™1cuê4¦mÛ.ª¥¯ä2J¡ …‚K—ÎrôèöîÝJ`àiôõ hÖ¬-“'GÛ¶]äJÌçL€Å055§U«Ž´jÕÈ¿ƒè—9wîgÏcûö?X°àK”J%VøøÔÅǧ¾¾u©^½^^¾2SPˆ ¹CPЮ^½À•+ÿt;XYÙR¯^^ý]üüšS«Vù$H!„BˆÇppp¦ÿôï?‚ôô4ŽÝÍÞ½ÛXºô{¾ýv2¦¦æ4jÔŠÆýiܸ5¾¾õ$!(^…BÁ•+ç9qâ'Oàôéä¦&ckë@›6¯ñhÞ¼=FFÆšµÂ’` ikkãí] oïZ 0€ÔÔd‚‚¹zõW¯þË™3GX½úg²²2ÑÕÕÃËË—jÕjâáQªU½©ZµîîÞj¸5BhF^^aaw¹sç:·nqûö5nݺÊÕ«HNND[[77|}ëÒ§Ï[ª¤º££‹¦CB!„¢\266¡}ûî´oߥRÉÍ›W8~|?§NdÑ¢Y|ýõxÌÌ,hذ%uê4¢ví†ÔªÕ++[M‡.Ê¡„„XOsñâ.\8ÅéÓ‡IIIÂÆÆžÆ[3~üW4iâ§§¯¬Ó^J$ø˜ššÓ A 4h¡Ú¦PärçÎuÕ,¦7.³qã2BCï P(ÐÖÖÆÙÙ wwoªVõÆÝ=ÿ«J/*ɧ.¢BHHˆ%8ø6·oç'ùîܹ®úž€=žž>¸»WçÕWûáãS‡êÕkcllªáè…B!„¨˜´´´ðòª—W ](!xæÌ6lXÊܹSpu­z?ØÚµàãSssK ·@”%Éɉ\¹rž‹ÏxšÀÀÓ„†ÞÀÕÕÚµòá‡3$á§a’|AtttñôôÅÓÓ—.]¨¶çææBpðmnܸÌW¸yó ;v¬#&&]]=¬­m±·wÆÍÍWW÷Bß+K‚P” ™™ÄÄD|›Û…¾ß»w“””$ ÿwÚÉÉ//_üýU(…޳´´Qm Æ@ff˜šš«Æ¦o344ÂØØ]]=LMÍÊLQ¡È%55…ÜÜÒÓSÉÌÌ %%‰””$RS“U<ŠÚVäKLŒ+tNUÂÔηJÔªÕGÇJ8:º¨f_ZZZk¨ÕBÊÆo¼(u³ùªV­¦ö1éªd`JÊ£3Ð’’òg ef¦“••IHÈíBI¯üŸÓIIIRM)V‡‘‘±j½ss+ 044.TÎÔÔü± ---­'Þ©*##]uWÚ¢<sA'l99Ù(•J’“ÕkÔ}&&fª7"SÓüd¨‰‰ÆÆ¦âêꎩ©Ù³. ÏÀ|ð»B!„B”'––6XZÚP½zí'–{ðª¦øøbb"U“Y Ʀ!¤¤$«®ŽRgÜY0†43³@KKssKÕj Š\æÈÄÄ ]Ý©”ÜÜ\ÒÒR)›™™¡šÀQ0^T*óHIIB¡PššüÄøttt055ÇÜü¿„¦™™9ææ–TªTkëü$_þò^vª+¼¬­ížx^ñr’ P›‘‘1FFÆ888?ó¹rr²U3à233ÈÈH¸ß!*Uû!Ú¸B‘[¨ƒ|8a———Wh*sQõÔQ33‹'No~¸ó×ÖÖÆÌÌâ‘}ffùoúúúå_6kllŠžžzzúÌ€B!„Bñdëå—Tzzªjæ\ffééù“7ÒÒRÈÍÍ%55™¼<…jš”” :¶ ÌÃ’“Ù¦««‹««{‘Û¼²ÍÂÂJ59E[;?¹§§§w̘?^444ÆÌ̼Ø1ª%% @¡zzúXXècaa¥éP„B!„BT@ÆÆ¦›ÊÝi…ä¶-B!„B!„BT`’B!„B!„¢“ B!„B!„˜$…B!„B!„¨À$(„B!„B!D& @!„B!„B!*0I !„B!„BQIP!„B!„Bˆ LWÓ!ÄËÎCËCÓ!!ʙν;3ý|Ô=ºÏhvnØ©‘º…âyš÷Ç<^íûª¦ÃBˆR! @!„(† K½zM5†¢Xºt®¦C ^½& ö?M‡!„Om̘~šA!J•$…¢ ¨W¯)¯¾ÚWÓa!Ê;×iÁÑÑEú,!D¹& @!ÄËFÖB!„B!„¢“ B!„B!„˜$…B!„B!„¨À$(„B!„B!D& @!„¢”¤¥¥h:„ç¦"µEñâU¤>£"µE!ÄËC€B!Ê¥RÉo¿ÍåçŸgÒ¦~8…B¡é°kÅŠôë×’ž=›<±\xx0-[VfË–•/<¦.]êqîܱ÷<Ú’œœÈœ9Søæ›I%®_ˆ—ôÏNSýßÖ­«éÖ­µk›Ó³gcØQâ„Bˆ§% @!„åÎ?~ÁíÛ×xç™={)))Iäææh:¬Ç8p$))I(•yO,§§§=&&¦%®#::¢Ø2×®]$66êþ#-ŒŒLHMMfÏž­j×ó¬mÙ»wS¦ŒdÁ‚¤§§ª]¯"Ÿô*ýßo¿ÍeË–•ôè1ˆ>}ÞäúõK¼ýök=ºGíú…Bˆg! @!„åÎÊ• qq©@ƒ-X¼x+†š ê tttqp¨Tl9;;G¶l9MûöÝKtþ¤¤Æ{£Ørýµ–NjrìØ^êÔi„­­ƒµã‹/Æ T*ÕªëYÛÒ¶m¾új‰Zu !%ý_aå¡ÿKOOeß¾¿øõ×í úŸ~ú=+VìAKK‹%K¾Q«n!„âYIP!D¹’••I\\4ZZZš¥LÈÈHçƒúr»Ø²~8ƒÍ›OqéÒYâãcX·îW>ûìGöï¿YªÏ§¾¾A©Õ%DE"ý_aå¥ÿû÷ß“L˜0³P=õê5Å×·wïÞ|¡u !„$(„¢ÜØ´i“'`ÇŽõLž<œE‹f©ö_ºt–O>ÁرéÑ£kÖ,B¡ÈàС¿©VMoo}öíû‹¬¬L&Oއ‡íÚysòä í¦^½š0jTïŶk×F>ÿ|4_=žaÃ:3gβ³³)wòä† ëDýúÖ ÚQ5pU(r9xp'ãÆ½ÁÌ™©]ï?ÿlææÍ«ÄÇÇ2yòp–,ùö±ecc£øçŸÍܺ„££ éé©üùçJO—‰¶!Oú¿G•—þ¯Y³¶Ô®Ýð‘s˜™Y¨fs !„/š®¦B!ÔÕ³çÚ´y-[VÒ¡CFúDµ/<<˜Z³sç%\\ª0~ü¦Ly‡µk—аaK¦L™K@@_víÚHÓ¦m000ä‹/rèÐß8;»Ñ¸±?ÎÎn8;»1aÂLµãZºô{vìXÏš5ÐÕÕ#11Ž^½šræÌV­Ú¯šõÃæÍ+6l,7n\á›o&ѯ_Köî½All!üùç*|Gíº»u{¿þZËõë—Š½´vîܩܻw“E‹¶0~üfÎü•_ýŽ7ß àìÙ8´µµ5Ú!ÄãIÿ÷¨òÜÿ) ®]»Èĉ³[F!„xžd Bˆ aùòùXXX«fS¼÷Þd Á”)sèß899ÙìÞ½]]=:uêÅ©S‡HJJò/±S(¸ºº«Uo\\4sæLaàÀwÐÕÕÀÒÒ†÷Þ›ÌÉ“ùóÏUª²Ìœù+­Zuâ­·Æ1vì4¢¢ÂY·î\]«Ò¯ßpÕ9^„éÓbåʽ˜˜˜qáÂI._>Ǹq_rút4ÚÚÚåª-BˆÿHÿW¼²ÖÿíÙó'¾¾uéÕkè k³Bñ I !„¨¢¢ÂÈÈHW=vw÷ÆÒÒ†ððÕ¶Æýqu­ÊæÍ+TÛ‚‚Q(rÙ¹s=ùW§N½Ô®÷üù¤§§áììVh{›6¯pâÄ~Õ6SSóBezô ÀÅ‹gÐÒÒBGGGíºKJ[û¿·ýnÝÞ J/Õ@µ<µEñéÿŠW–ú¿¤¤æÏÿ’ï¾[!ë9 !„(5’BQ!øû˜DZc{HNN$##Ö­;©ÊhiiѳçŽÝMLL$gΡNF4kÖ–-[Vùàz¨]oxø=’’â m·²²ÅÈȘ¨¨ðÇëààŒ¡¡YYj×÷¼Lœ8ë‘nym‹/;éÿJFÓýß—_þO?ý[[‡F.„B<=YP!D…ЭÛëdff0~ü`z÷~“¨¨0~øa ~~Í •ëÙsóæMcÛ¶5\¸pŠÉ“¿ãĉý|øá Ž߇½½††Fj×ëâR€àà¢ïBéáQý‰ÇkiiQ­ZMµë{‘*R[„x™Hÿ÷ìJ«-+V, C‡î4jÔªäA !„Ï@f !„(W”Je‘Ûsss¸{÷Û·_`ܸéÌšõíÛw¤œ‹K7ögùò144ÂÁÁ™z`llÊÿþ÷:½{+Q<õë7ÅÔÔ\µ®VÈÈP22Òi×®ëc ½KNN}KTçôµµIKK}¦s@Ùh‹âñ¤ÿ{Tyêÿ¶n]¡¡Ñ#¯ÍÑ£{ž>p!„BM’BQ®DD„™™^hû¢E³8uê GŽìæäÉ\¼x†;Ež£wïa„„ÜaÈ1Ð++[jÕjP¢x,-m˜8qgÏU]~ðûïóèÙsMš¼€ŽŽII ¤§§ùùùó§3fÌgÅÎ,)޽½3 ±\ºt–“'Z ¬,¶¥à8…BñTq ñ²’þïQå¥ÿ;p`Ë—ÿHnnkÖ,bÍšE¬^ý3S§¾Ç­[AO³BQr °Bˆrãòås,Yò-Û¶­ÁË«¯¼ò*fføøÔeÑ¢ÙŒ;°Ð1^^5X¶ìœUÛúpñâ|}몶 ü>ׯ_zª¸|{{'/žÍîÝ[07·ÂÎΑ‰g©Ê|ôÑ×,\ø£FõÂÙ¹2††Føû”hÁýÇyýõwÙ·ï/ÆŽȇÎÀÈÈø©Ïõ¢ÛräÈn6o^À¡C³víbڴ邽½ÓSÇ,ÄË@ú¿¢•‡þ/0ð4£Fõ&33ƒóçOÚ§¯oÀñã__P!„x^$(„¢Ü¨Q£>ß¿šï¿_ýȾ¼<³fýFóæíˆ!>>†ˆˆP®] dåÊ|øá UYC¦Ný¡Ðñ¾¾u ˆKª]»n´k×í±û½½kñÃkŠ=O^^VV¶%ª»zõÚ=R|A5½È¶´hÑž-ÚóÝw+s”¢(Òÿ­<ôµk7äòå§›™(„B`òäoÉÌÌÀÜÜ’Î{—ZýÏÓ“Ú"„x1¤ÿ+¤ÿBQÖIP!D¹×µëëÜ»w‹+0gΧ›âååË믿˔)sÑÖ.»÷¼255'!!–üiݺË—ïÆÄÄLÓa=•ŠÔ!Ê éÿʆŠÔ!„“$…B”{:::Œ;±c§‘‘‘Ž¡¡ZZZšK-ææ–üñÇaM‡ñ\T¤¶Q^HÿW6T¤¶!„¨˜$(„¢By–;@ !Dy&ýŸB!§ì^ „B¼$ÒÒR4‚B¼0é驚A!„xéIP!ÄKçðáØ»w›¦Ã`ÅŠôë×’ž=›”J} E.§Oæ»ï>áС¿K¥N!Dé++}ÜŸ®bРv´iãõÔç(ªßzž}™ô‹B!^’BñRYµê'‚ƒoѶmM‡ÂÀ#IIIB©Ì+•úO³aÃR.üŠÈÈÐR©SQºÊR÷ÚkýQ(äææ>õ9Šê·žg_&ý¢Bˆ—…$…B¼4öïßÎñãûxýõw5 ::º88T*µúêÕkÊ!ï—Z}BˆÒUöú8œœ\žéEõ[ϳ/“~Q!ÄËBn"„⥖–ÂǿŪUû4ŠFééék:!Ä P‘û¸¢ú­çÙ—I¿(„âe @!„/…µk— ¯o€§§o¡íJ¥’5kqõê._>‡™™Ó¦- J•'¯YuéÒYÖ¬YDZZ*÷îݤoß·èÛ÷-ttžî­õäÉüüóL.\8EíÚ ™>ý'\]݈bΜ)8;»L||,3gþ‚¥¥ QQalÞ¼‚-[V²ví!ÆŽÀ­[AlÛvKK›bëݶm Ÿ|233 Ž !%%‰eËæñßS§N#6l8^l BÍ+ë}\LL$S¦¼ÃéÓ‡pq©Êœ9+ñôôy¦‹²k×FNœØ!ׯ_¦V-?Fþ}}µÏ!ý¢BˆŠH.BñRعsuë6~dû¢E³044búôŸX¿þ‘‘aôïߊŒŒôÇž+<<˜ZóùþûÕxxø0eÊ;ôìÙ„/¿ü_‰c‹‹‹aóæ 6–Q£¦pâÄúõk©Ša̘þ¤¦¦0zô§|õÕBCï0}úX®]»È¦M˸};ˆeËæÑ©SoììÉÊÊR«î.]P¿~SÕc33 Fþ/¯…Ê=)!„æ•å>.++ƒE‹f1qâ,Ö¬9È;ט5kÂ3ÅX”¥K¿ç×_ç0eÊ\&Mú–¹sW²}û:†íˆR©Tû<Ò/ !„¨ˆ$(„¢ÂËËËãâÅ3ÌÊˆŠ géÒïéÞ}¿^Uç퉉‰dß¾ÇßAsùòùXXXãâR€÷Þ› À€#˜2en‰ã300`æÌ_iÕªo½5ޱc§κu¿ ¥¥…OUùjÕj@«VðókŽB¡ {÷7èß8›6ÄÁÁYíú ÙfllRèñ“bBhVYïãttt™4éÜݽñö®EíÚ¸téì3Åø°¸¸hæÌ™ÂÀï ««€¥¥ ï½7™“'ò矫J³ô‹B!*¹X!D…—œœ@nnV…¶Ÿ;wŒÜܦLYh{¿~oc``ôØóEE…š™âî áá!OŸ©©y¡Ç=z æ›o&qñbþ¹`M¯¬¬Lþüs.œ*4›EWW]*Wö|ªúÕQ\ BÍ)ë}\AUÀÍÍóç?SŒ;þééi8;»ÚÞ¦Íkœ8±ŸîÝßxªøGúE!„å‰$…BTxÚÚ:(ŠBÛoÝºŠ‘‘ _}µ¤Dçó÷`ëÖÕ;¶—fÍÚ’œœHFF­[wz.ñ:88chhDVV†*îÅ‹gsñ↠Cݺ9þÄs©K]e!!DÑÊ[§¥¥õÌ1>,<ü¼©¼, IDATIIñ…¶[YÙbddLTTø3¿(Ò/ !„(O$(„¢Â33³ÀÀÀääÄBÛ ‰Œ %22GG—Bûâãc°¶¶+ò|ݺ½NffãǦwï7‰Š ã‡Öàç×ü¹Å¬¥¥Eµj5ÉËËã­·°±±gáªKƒKKYˆAñxå±{ÖæâR€ààÛEî÷ð¨þl>DúE!„å¬(„¢ÂÓÒÒ¢~ýfDGžâí] ¥RɬY m¾ÅÊ• Uóòò íÏÍÍáîÝlß~qã¦3kÖo´oßý‘z>N]¡¡wÉÉÉ!  /.œâðáhÜØ_µ?''GíËÌÔ‰AWW—ôôÔB³‡ÒÒRUÇ>k Bˆ«¼õqOcqê×oŠ©©9»wo)´=22”ÿ³wßqUÕoÇ?p‘% *Š à@P@Q܈šŠ“Òr¯ÒÌÔJ3[jj–Y®_i!ZjeæHÅ̹Å(n½÷ºÜßÈMäbÈ|Þ¯/ápî9Ï9÷úp¾Ï÷{¾'--•=ú«—I^Bñ<’ BˆçBÿþ#¹pÁ/_ã¬S§ž¸¸´e÷îM¼óÎ víÚÀ† +˜5k"¯½6 €Ÿ^J›6ܺu]ýº•+qæÌ_œ8qˆÓ§råÊ9‚ƒïæÛ_a¯+ŒB¡ !!ŽÔÔT*ÞÞó˜2å3ì욪o•Û¹s-7n\aûöŸ ºFtt—‰ŽŽ 55™œeÑ?…Åœœ@ZZŠzY“&ÍILŒç‡p÷îM–/ÿ’ÌÌ îܹA@ÀEb˜3ç†íÄýû·4{C„¥ª¼æ¸¸¸ãÉÊÊT/‹‰‰$33ƒ´´Tb,,oý{™¹yM¦O_Äùó'ñóóU¯÷Ë/^ 8†º·äE!„Ï) !„x. 8sóš\ºôÏüL:::¬Ys€—_Źs'øê«¸té4_½ŽêÕ-€ÜQ FFUÑÕýçO¦£cK‚‚˜:u$#Gvã•WÚÒµk#úôi¦žgª°×æãСC7&MĬYùòË÷éÚÕ“I“fвe{FŽ|‹€€KÌš5ºuë3cÆb ñöžÇž=›9vìT*_|1…€€‹êmÿ;ÿ3,[6€_]ËÑ£û;v*ýøá‡E|üñõ“…=<úRl ÆÆ&„†>àâÅSlÝ*·À ¡ å1Çùø¬çÒ¥¿Q*³Y¸pÉÉ‰ìØ±†sçN R©øúë™dgg=1ÆÂòVQ¹läÈ·øáV­ZÌܹï²téjÕªÃâÅkÔ1I^Bñ¼ÒQý‡qêC† ”¶{{¿#;;¼¶zñâП¸ÞÞm{™2l ·oËðy!DÅ5yò¨š‚÷öâó£Ž^^[yñÅ¡eÙóíÊ•sx{ÏcåÊßþÓvþ¬¬,ÜÝ{Ellaa!ܸq•JŇ~UJW,gÏçÂ?&Nœ^üÊâ©•4¿lõòbè‹O¾þÚ¶w/æLá¶êvñû2Rªâí½]ã˜EÙWþH^,¿ììt¤}*„x&Jš_T·‹¿þ2y2T­ÊöíOý%BñÜhÞ¼ ýûä矗òÆï?Õ6/3gÎ$üüB033§A{:wî…ϺR‹·"IIIÂ×w“'ÏÖv(B<·$Ç•/’…B”'r °BˆçÊ‹/ÃÞޙÇw?Õë¯_¿DDÄC–/ÿ’k×.žžFllGŽüηß~ÆÐ¡o–rÄC`àeÞÿ LLÌ´ŠÏ5Éqå‡äE!„剌BñÜéܹ×S¿¶ÿQÜ¿›õë—³dɧ›`oïĨQo3{öÒbçü«¬Z·v×vBˆG$Ç•’…B”'RB!J@¡P0uê\¦NKZZ*††Fê§A !DE'9N!„¨œ¤(„B<%##cm‡ „ÏŒä8!„¢ò1üB!„B!„BTbRBñ\9thîî6ܺu]k1¨T*~þy)?ü°îÝíùðÃ×P*•Z‹§4(•Ùœ={œo¾™Å±c”Úv.²fÍ·¨TªRÛ¦Ï+Éφä?!„…BËŽkX±b>áá!¥¶]''W[°hÑôRÛ¦Ï+Éφä?!„…B}€ÐÐ ÔI“—ì|Œ&ÇÁÌ™ãñöžÇÌ™ãyë­ÄÇÇäÛ–±qUš7oÃòå_=u`ýúÃ^æë¯g©·ú€#^àí·gòí·›°³sdöì·8°_~ù>QQá]ãäÉÃêIç5Ýö“âƒÜÆ]|| qEãÀcøôÓoèÕkóç¯fâÄéôë7‚V­þiššVcòäO±·wV/+­sÐ¥Ko<=‡¢££ƒ›[w ùâ‹XYÙ`mmKûö]°¶¶ÅÚÚ–3?õ{ªÉqL™2œää$&Oþ”ùóWr—yó¦Ø^«Vn8ðk…`€ÿ&ùOòŸä?!„Ïš…Bh]DD(kÖ|Ë+¯¼€B¡ oßÁDE…säÈjÕªƒ‹K[¦NKãÆN89µÄÅ¥-W¯žWogÝ:oªU«¡ž_êwf0bÄfÏ^JãÆŽôèñr¾}k²íââèÚÕ“Ë—“èßäSCCãËŒ«–(NMÎÀðáãÉÊÊTÒÓ«BŸ>ƒ8s昺Ÿ‘‘ŽR©ÄƦÑS¦Ç ££ƒ£c õÏÍ ¼\àu5kZ’””À­[ÿ)&!ÊÉ’ÿ$ÿ !„( zÚ@!„¸pÁìì,fÏž˜où°aob``€®®…âŸ?]uêÔãþý[êŸ#"ªG¤4jÔs󚄆«—ééüÓWܶ5‰/÷õ øé”Ö9hß¾+66 ññY¯n°^F©Ìfÿþí >~¥OŸAÏôxòlÜxÈmtÿöÛFüýÏ R© ¬gffäÞ2פIó2‰MˆgMòŸf$ÿIþBñßHP!„Öݾ}#£ªÌŸ¿ú?m§kWOvïÞ„ŸŸ/;z˜OZZ /¼Ð§\ÄW49::: 8oïyDE…sÿþ-Z´h‡B¡`×® êðÒ¥Ë$f¥RɪU‹¹råcÆL¡eËö\¼øwõtuso\ÈÉÉ)“¸„( ’ÿJä?!„¢hRB¡u††Æ„‡‡B:õòý.66Š5ji´—_Ezz}4šÁƒß "â!ß}·™Ö­ÝËE|eAÓs0p༼æ²gÏfüýÏ0sæ7üý÷Ÿ|øákœ:u„Úµ­044*b/¥'''‡qã<©Y³6+Vü À¶m?ºn||,{; •…ä¿Ò#ùO!„(š…Bh]“&ÍQ©T,Z4=ߨ‹n³kצLùL£ídggqï^{÷úS½ºE™Ç—““£¥Q”Âní‚Ü[óRS“Q*•ê[éRR’12*8‡Ô“hzêÕk@ûö]Y·níÛwÅÒÒš^½`llÂûïbõê=%ÚoQŠ;.ÿ3?~ þiôfeezžââ¢111+0‰¾™ä?É’ÿ„B”) !„кNzââÒ–Ý»7‘‘‘N¯^HNNä?vâåµÈ}Ê$€R™­~]LLd¾ùžV®\Ä™3áäÔ’Úµ­066Áܼ&66 ÕëDF†¹#Wò·mMâ;yò0ï¼3ˆ… ¢oßÁEkXXéé©ù–7iÒœýûwðà ðôʾ}ÛÈÌÌ ,,˜€€‹89¹–Ú9È3xðX>úh4+VìÀÈÈOÏ!øûŸ¡yó6ECQ’“HKKÑø¸tttعs--Z´ãòå³]#::‚ÀÀËXXXbaa äÎEÖ§Ï g>טeIòŸä?ÉB!Ê‚<X!„Öéèè°fÍ^~yçÎ૯>àÒ¥Ó|ýõ:ªW·ÀÏÏWý´É¥Kç…ÏzüýÏœœˆ—×\”J%ŽŽ- `êÔ‘ŒÙW^iK×®èÓ§¡œ:u„uë–°mÛOœ8qH£mçää<1>Èߨ¸*UªT)ò8¯]»ÀªU‹سg3»woR7jÇŽŠ‡G?~øa<†.]úкµ;ý )µsð8OÏ!Œ3'§–êe£G¿ËĉÓKüúûŸaÙ²yüúëZŽݧÑqµlÙž‘#ß" à³fM nÝú̘±C¼½çall@zz.ø=UlB”g’ÿ$ÿIþBQtTEÅ×À!C %…íÞÞÅïÈί­^¼8ôÅ'®·wÛ^¦ ›ÂíÛO–BhÝäÉC j ÞÛ‹Ïv:vxymåŇ–Ad•Ûáÿ‘••…»{bc£ˆ",,„7.£R©øðï´â3WYÏÁ’%ŸbjZñã?Òv(ZWÒü²ÕË‹¡/>ùúkÛÞ½ ›2…ÛªÛÅïÈdH©Š·÷vcÏ^eý¿_•õHþ{6ììt¤}*„x&Jš_T·‹¿þ2y2T­ÊöíOý%· !„¨/3gÎ$üüro1333§A{:wî…Ï:m†W&žÕ9hÓ¦øIþ-ú~Oµýâüõ×~²³³¤ñ+D$ÿIþB!Š#@!„•Âõë—ˆˆxÈòå_Òµ«'vvޤ¦&séÒiNœ8ÄÇ/ÐvˆÏܳ:çÎE¿Ò3rýº?II L›¶Pk1QÞIþ“ü'„BG €B!*…þýGqÿþmÖ¯_Î’%Ÿbll‚½½£F½ÍìÙK‹}:eePÏ£c [h; !ÊµÊø¿¤*ã9ü'„¢4IP!D¥ P(˜:u.S§Î%--CC#õŸr„x>Éÿ}9B!Dq¤(„¢Ò122ÖvZ'ç@ˆç“üß—s „B¦â…B!„B!„BhL €B!„B!„BTbRB!„B!„¢“ B!„B!„•˜…B!„B!„¨Ä¤(„B!„B!D%&@!„B!„B!*1) !„B!„BQ‰IP!„B!„BˆJL €B!„B!„BTbRB!„B!„¢“ B!„B!„•˜…B!„B!„¨Ä¤(„B!„B!D%¦§í„ÐTvvII $&Æ“””@FF:ééi¤¥¥’™™AJJJe6‰‰ñ(•J’’Ô¯KII.°½ÌÌ ÒÓS ,W(ô¨ZÕ´Àr}}}ŒŒª`hh„!ÆÆ&T©RSÓjèê*033GO/÷õ††Æ˜šVÃÌÌ##ãR>Bˆò"33C›RR’òå¤ììÜœ”““›“²²2IMM ==ŒŒôÛKMM&;;«Àr#£ªT©¢_`¹‰‰) EîŸójÕª£««ÀÔÔ }} 122F_߀ªUMÑ××ÇÔÔ3³Ü/R>BˆÊ"ïZ+--…¤¤Drr”êü俞Ê[/;;›””$õ¶”Êl’““ îä1‰‰qOü}•*ÿ\‡FWWSÓj…þœwÝ`jš›û¿®«ZÕ===õzffÕ100ÄÐÐè‰1 !Ê¿¤¤’“IJJP_³åää<º>Ë!))·í˜œœ˜/w=~Íö¸œœÜu cbb†®®¢Àrcã®áòòMnûQSSstuu133Wç-}}LM«abb–/¯ ñ_IPhEll±±QÄÄDNLL$11¹ËâHJÊ-ò=^ðKK+X¬{\þdš[ŒÐÑÑQÿ8…B‰‰Yå))IÄÆFXþøEoZZ ™™™ê¢cÞ’¢èéå ó ‚ÕªUWÿln^“ZµêP£F-jÖ¬……EjÖ¬MÍšµÐÓ«òÄcB”®øø¢£#‰"**œ˜˜ˆÇrSl¾œ”÷ozzÚ·™{A§ÀÔ´šºƒ J•*›X¿¨Fgddh<£R©HLŒxt›€R™{ñš™™QlÞ|>–èèˆ"ÿïœ÷xp^ÇJ^ž, SÓjbdTSS3upÕª¦Rµª©:æ}ösaù^¡™œœâ⢵!£‰ŽŽ &&R½,/ß=^ìKNNTwT%/WéèäÞoÕ†„Âó™J¥âáÃû…®Ÿ˜J¥ 99Q=XE¥ÊÉ÷»¢äµó ‚yÿV«VêÕ-µ#k«¯ÙªW· zu tuå†O‘ŸE©ÊÈH'4ôáá!„‡‡ððá}ÂÂBÔ?çý”Êlõktuu©Q£–úËܼ––u±·wÎ× ý÷÷y£ð SÖòây½Bii©Š˜yßç9ˆˆ%0ð2QQáÄÆF© ŒyÌÍkP³fm,,,±¶¶ÅÊʆ:uêaeeƒµµ-uêÔÃܼ†–ŽXˆŠ#++“ðð‡å¥`ÂÃCÔù*¯âñ‘wÿÎMիפNz884ûW>ú§±gbbªn?>2O›òö©©Éêžï¼"Á¿‹™y/ÁÁw‰"22,ß(ÈíÅ®YÓ’Zµê`e•›òrR^ŽªU«Ž–ŽVˆÊ!++“ØØhuãöŸ†nþeöŠ*lWÍ7ò7÷çjT¯nñh´›¹ºãáŸï111{Tà2Qw²æ,†Ü‘. EÁ‘.Å㣰s;RT…EãÉÌLW_×åŽðN&%%‰ŒŒtbc£ Z Å­Pèå+ æ6Ú-Ô õ7Üó~'DeAdd(aa!„…JhhîuZtt±±QÄÅEçëÕÑÑytmfAÍšµ03Ë-„ÙÚÚ©‹c¦¦f˜™ý3è"7§¨GW«V]‹G]PBB*•êQGLF¾bfbbII‰Šœ‰‰‰Rÿ]x¼¨«««.ÖªUK˺X[Û`iY÷Q{²––u©Y³¶Z”5í·LD…ÁÝ»7¹w/ˆû÷oqï^Ü!,,8ßÈ9Ã|+'§–Ô¬Y[„ò¾¯Q£V¥èxüÉÓ^°%''¦!™;)’¨¨pÂÃC¸DxxH¾Þ,##cêÖ­µu}4°§aC{4°§~ýÆÔ«× \ „( II Ü»Ľ{Aܽ{“»woòàÁmBC®¾(ªREŸ:uꪋVööÎêQ¸–XXXª{R+Cn244ÂÐÐè©/t󸑑aÄÄDæû>4ôgÎ#,,˜¨¨põkôõ °²²¡nÝܼ”÷Õ°¡66 ËE§Ú×`‹ˆ%22”ðð‡DF†BTT8qq¹#[þ}{™B¡ÈWªQ£ööÎF…˜?6z·z¾"“™™¹\!/7¥^HKKÍ7óߣ1∋Ë-æÞ¿[Ã÷½VVõ¨UËŠ:uêR»¶5––ÖXZÖ¥vm+ôõ Jõ„(-II Ü¿‹û÷oóàÁmîß¿õèú,˜ˆˆ‡ù@˜›×ÄÒÒkk[ê×·£M›NÝ%e©.úU–öããòòÐÓîÈÉÉQKcc£’ÌkS†þk×.J||¬úuúúêözýúvØÚÚQ¿~cõ÷r rå"W¢PJ¥’û÷oqãÆ/s÷î îÝË-öå122¦~ýÆ4h`»{ÇF€Ô£NzÒ›ðLLÌ011£Q£&O\/55…ÐÐêQLaaÁßåÊ•sìÙ³™¸¸h ÷ÖãzõР=5ÁÞÞ™¦M]hÜØ cã¢çÑ¢<‹ˆåæÍ«úsëÖuu‡DLL$û¹·±iHƸººÑ¯ßõ…•• µjÕ‘¹ïJ ¯3ÇÊÊæ‰ëeff<ÊG¹#+CCððá=n߾ޝïn""BÜQ0y…Á† pph†£c ìí%/‰ -==»„„ÜSED@©T¹[4hŒ­­º=éàÐ [[;É)qqÑ\»v‘ÀÀËܼy•7®tŒŒt ¶¶v4nìDÇŽŒ9Q=’£NzÚý¹el\•ÆiÜØ±Ðß'&Æ«GBå}={œÍ›W’––Š®®.66ptlƒC3š4iŽ“SKlmíÊøH„(ZVV&—¹~ÝŸ7®pãÆ®_÷'>>K˺888ãììÊK/ S禺uëˈ-Ð×7ÀÖÖ®È<’ššühtæ?£ÇýýO³cÇRR’Ôy©iSš4iN“&ÍqvvÅÆ¦Q‰…S*³ ¾Ëƒ·óù>Ìý÷ñùéÌÍkP·n¬¬êao{ua&¯#BŠzâߪTÑ4ª¯øÂ¡R™Mtt¡¡ÁDG‡¢åÂ… ~„„ÜS®ÒÓ«‚•• õê5PÕ­Û›†Ô¯ßX¦n{øð>ׯûsëV€ºàwëÖu233ÐÕÕ¥^½†ØÙ5¥e˼üò«Ô¯o§¾3Iæ7/ hØÐ†  ý}VV&!!÷Ô#6ïßÏýwÇŽ_xøð999bg爽½Ͱ·wÆÑ±ÖÖ¶e|4¢$¤…ôœQ*³¹sççÏŸäܹ\¹ržÛ·¯£R©¨V­:;ѬYk xfÍZãää*#2* 33s\\ÚââÒ¶Àï""B¹zõ<·nt}û¶³|ù—(•JLLÌhÒ¤9mÚt¢ukwZµr“Š(3yŸÍ¼ütíÚÒÓÓ¨REŸúõÓ¼yk:wîEãÆN´hÑN£NQ~›àä䊓“kß=)/™šVÃÅ¥-­[»Ó¬YkZ·v—¹OÅ3•˜σ·yðàAA׸u+€îpëV€ú¡?¹£÷¬±µmDÓ¦-èÕk66°µm„­­]‘Ç QZ ½G£Gë>q½„„8‚ƒïðàÁ‚ƒïJTT‡ýÆ;ê§œššV£~ýÆØÚ6ÂÆ¦ööNØÛ;cgç(zŽ%%%pãÆõµÙåËgÕµk[aoïLÛ¶]5êm7vÂÉ©¥<ð¦¨RE¿ÈaVV&÷î þé㳞;wÉÉÉÁÌÌ{{gu{R®ÙË)Vr©©Éœ9sŒS§Žpþ¼ÉÈHÇÌÌœ–-ÛÓ·ï`Z´h‡‹K[¹e÷9‘׳ìáÑO½,==ë×ýñ÷?Í¥K§Ù·o;+W.BGG‡† hÑ¢=nnÝèØÑ£ØÛ…ЄJ¥âæÍ«øùùò÷ßGñ÷?MTT8 …M›ºÐª•#FL¤eËöԯ߸ÒÍó"ò+:/]ââÅ¿¹t)w¤ —×\tuu±³s¤U+7ÜܺãæÖ].,ÅSINNT,¾~ÝŸÛ·¯sûv z>ã¼5¡kWOÆû€FšHOT(ÕªU§ZµÖ4kÖºÀïT*¹w/ˆ;wnpçÎ nßdß¾m<|xŸœœ uë6 Q£&ê)[ШQm_Éä]›>}”sçNàï†{ØÚÚáâÒ– ¦áâÒGÇê§åŠçK•*úØÛ;coï Q/ONN$ à—/Ÿåòå³ìß¿ƒ•+P¯^Z´hG›6èСööÎ2–HÖ®d233¸xñ~~G8uê—.&'G‰½½3íÚuaÔ¨·pqiG£FMä?P344ÂÕµ®®ÔËbc£¸t)· x႟~ú6é4h`¯nt»¹u£FZZŒ\T$ÁÁwñóó 0¬« IDAT}Tôû“èèÌÍkжmÆû–-ÛÓ¬Yki €¼¼ä†««›zYddþþ§¹xñoÎ;Á¯¿®E©ÌÆÁ¡;zàæÖºRµª©#åQHÈ=s }— ¸DpðõM›æN‰Ñ·ï`5jJÆÔ­[_®•D¥¦££C:¹swwèÐ-ßï233¸sçwïÞ|T äĉƒ¬Yó-YY™boSK[дi š6u‘âx’““£.ø>ýgÎ#..33sÚ¶íÌСãhÞ<÷Ž"y/ŠcbbF»v]h×®‹zY|| —/ŸãÊ•³\ºtš¥Kç˜Oõê´k×…ºÒ¾}WšÉßÛ2"ÀJ >>_ß=<èɇHOOÃÖÖŽŽ»3zôd:tè&£#D‰Õ¨Q‹îÝ_¢{÷—€Ü'^¸àÇ©SGðóóeÛ¶ŸÈÉQÒ¬Ykzõ@¯^Šœ“P<ŸT*þþgøã<èý{AÓ¦M'Æû€Ž=prr•Ñ}Bcµk[ѳç+ôìù ;Êýôé¿Ôyé—_¾CWWA»v]å¥Wd¾ÚçPJJþþg¸pÁ NqéÒß$$Ä¡££ƒM#œœZ2hК6ÍÉT·n}m‡,D¹£¯o@Ó¦.4mê’oyvvAAê‚úõëþ<¸K=?oýúiÕ*·ó¦U«Ž884“”#©©É;ö‡ïæèÑ}ù ~ï¼3“öí_ iÓòž‰Ran^“.]zÓ¥Ko ÷a1þüý÷QNŸ>ª.Ö¨Q‹nÝ^Äã;÷–)Èž!)VPaaÁ:´‹ƒwqæÌ1ôôôèØÑƒ9s¼pwïA½z ´¢¨d Õ#ÿ>øàK’“9}ú/Žùµk½øæ›Y4jÔ„^½л÷@š7o#=9Ï!¥RÉÙ³Çýv‚­­½{¤[·qu퀾¾¶Ã•„±± ݺ½H·n/É©SG8xp_=“/¾˜B‹íèÝ{ ½{¤~ýÆZŽX< ÁÁw8Þ‹OqþüInÞ¼ŠR©¤nÝú´níÎÔ©sqvnEÓ¦.2:TˆÿHO¯Šú6àþYÂõëþ\¾|– üX¼xÉɉT­jJË–íiÕª£º0hjZM{ðŠˆxˆ¯ïÞÍ©SGP*³iÙ²&|Œ»{)ø‰2£P(pvn…³s+Æû¥RÉõë—8yò0‡ïfòä!T©¢›[wzôèO÷îýJü4uñdR¬@2338|x7>>ëøë¯ýèëàæÖE‹~¢gÏWdQ¦LLÌðð臇G?æÍûž€€‹øúîá÷ß·ðà iÐÀžþýG2dÈò4¨ç@xx¿ý¶‘¿çáÃû4nìÄË/¢{÷—hÝÚ]ŠÁ¢LÔ¬Y›—^ÎK/ '33ƒ³gã뻇Ÿ^Ê¢EÓiÖ¬5#FL ÿ‘2Iy–ššÂÅ‹§ðõÝÑ#{¾‹B¡GÆêÆmÛv‘ÎP!ÊPÞ­Äy2J¥’;w¹zõ<çÎdÿþx{ÏCWWGÇ–¸»÷ÀݽíÛ¿ O†}22ÒñõÝ£n7V©¢OÇŽ|ö™Ý»÷£vm+m‡( …‚fÍrç(8q:ññ1øùáÈ‘=,\89sÞÁÕÕG˵[)‘`pãÆ¶ný‘ß~Û@RR"]»öeÅŠ_éÒ¥Œ¤å‚®®®:y¿÷Þç\dÇŽ_X·nË—E·nž ú&]»ö• £+‘¬¬Lú­[Wãçç‹……%ƒ½Î A¯úÔ0!Ê’¾¾º9kÖüü|Ù¾ý'>ÿü].œFÿþ#6ìMœ[i;TQ •JÅ•+ç8~ü ÇÿÁ… §P©rhÖ¬5ýû¤S§^´hÑCm‡*„xD¡P¨0`Àh¢£#8sæÇÿÁîÝ›X¹rææ5pwïA§N½xá…>Å>ÕXM¥Rqöìq~ýõöïßAff]»z²bůtîÜ[r¤(÷ÌÍkâé9OÏ!¤§§qüøìØñ Ÿ}6™ >ÆÓsƒ½N›6´j…%-ñrìâÅS¬X1Ÿ?ÿÜK:õ6l<£F½-sÕˆrÏÉÉ•9s\™1c±zÔêÛoÀÊʆ±c§2rä[R¼®À²²2ùý÷-|÷Ý\>¼‡›[w¾ûn ½z½"½ø¢\R(tîÜ‹Î{‘˜Ͼ}ÛX¿~97~¯¾E´cGm‡)þ%(èûömg×® '$ıÿvvî\ǰaiÚÔ…7ßüþýGÊà’’™×Ë¡³g3ztOîHBB?ÿ¼ãÇï3mÚB)þ‰ E_ßOÏ!¬^½‡C‡éØÑƒ >¦gϦlÙ²Š¬¬Lm‡(J 33ƒõë—Óµ«³fM¤[7OŽ»Çºu‡ðô"Å?Q!˜™™3|øöîõgÃ_ôôôxíµŒÕÓ§ÿÒvxϽàà;,_þ}ú4£OŸfìܹ–—^Æï¿_âÌ™–-ÛÆðá¤ø'D%`cÓˆáÃ'°lÙ6Μ‰`õê=4jÔ„o¾™EÇŽõxã O||Ö“’’¤íPË¥ÌÌ ¶lYMMxûíØÚÚ±gÏþøã'N—⟨4ªU«Îðáضí»wŸÇÎΑéÓß W/'¶nýQÚ”% Àr$"â!'¾Âðá]ÈÎÎbÃ_¶m;A—.}dþ,Qáկߘ ~äÈ‘ ºtéÃܹSèÕË ??_m‡&4pâÄ!zölʂѫ×þüóŸ}¶ ++m‡&ÄSssëΦMGÙ´é(#GveâÄ—‰ˆÕfXÏ¥RÉÁƒ>¼úªݺ5fíZ/Üܺ±}ûIŽ½Ã‡~…£c m‡)„x† èÞý%–.ÝÈéÓ|óÍ:ôôôøä“7éÐÁŠÙ³ßâæÍ«Ú³ÜðõÝCÏžM™;÷]ÜܺqèP ß}·''Wm‡&Ä3åìÜ /¯-[F«V¥ô9—šš¬í*Št­il\•~ýF°jÕnþþ;Œ?^ÀéÓGéÛ·9cÇöåâÅSÚQkÂÃCxûíL˜ÐŸÖ­Ý9zô6_}µªÂ>å^¥Rp‰ÌÌ m‡RiT¤ÿëÿEƒöÌŸ¿š£GoãêêÆ›o¾Ä¤Iƒ‰ˆx¨íÐÊ5)jYll¯½ÖƒÏ>›ÌÈ‘o±w¯™ÍC¤Tfsöìq¾ùfÇŽýQ&ûT©TüüóR~øa!Ý»Ûóᇯ¡T*Ëdß…9thîî6ܺu]+û_¿~9ÆufàÀe¶ÏãÇâ뻇€€‹¬Yó­Ö¸uêÔcÅŠ_Y¾|Gî£OgΜ9¦•XDáΞ=NŸ>Îüõ×~–/ß·÷ö2+üI~Ò~~ÊSVç¥<䦎=Ø·ï2ƽÉìÙyã OéœxFöî݆‡‡Ÿ>™Î{søð ~üñw^x¡/ …B«±IþÑ~þùí·¼öZºw·êmö>jã½}V4=–Í›W2bÄ ôìéXèïËCî}sóŒý.^gÍšý¤¦&3xpGÞxÓ;wnh;¼2å㳎^½œ¸yó*ëÖbÉ’ ºCv÷îMtëfG¿~®$&Æ—øõ’7ó+ËveyÊ––uùöÛM¬]{ë×ýéÙÓ‘]»6h-žòN €Ztÿþ-lOHÈ]||N3mÚB Êlÿ—/ŸeÇŽ5¬X1Ÿðð2Ùç²e_pçÎ Þzk‹¯!))ìì¬2Ù7@ddX¾ŸŒªR³fm­=käȉ$%% Rå”Éþ6nüžnãáÑ''W[°hÑô2ÙwQz÷È×puucôèžüöÛF­Æ#rýþûFîI‹íùãz÷X¦û—ü¤ýü”§,ÎKyÊM††F̘ñ?¶o÷#(胻ú@+±TFÞçÕW=˜:u:tãÈ‘ æÍû¾\^‘ü£ýüóÒKÃQ*•dgg?õ6 {µñÞ>+šËСo’““CNNÁÂHyʽÅÑÑÑ¡K—>lÝzœu놧§ K–|ŠRùôŸ“Š@¥R1þ‡|üñëŒ1}û.ãîÞCÛaýgýû¤OŸÁOýzÉ›ù•U»²¼æNz²ÿ† {“>ÍÂ…ÓÊ]‡Fy @- }À«¯zP£F-||ÎàìܪÌcpuuc̘wËtŸ6¬ ^½´iÓ‰U«v—Y’LHˆãƒ^Í·¬S§žìÞ}›†eÿ)zeÖs÷çŸ{9uê£F½­^Ö¡C7LLLY¿~y™ÄP”jÕª³|ù^ý=>þx {÷nÓj<Ï»C‡vñÁ¯ñê«ï°bůT«V½Ìcü¤ýü”çYŸ—òš›Z´hÇΧÑ×7àÕW=ˆŠ ×Z,•…¯ïúõs%&&’íÛýX´èg¬­mµV’´Ÿ VVõþÓ6 {µñÞ>+š‹B¡ N‚ç²¼æ^M¸»÷`×®sÌœù ?ÿ¼„aÃ:WêÛþ>ÿ|2k×.céÒ|òÉ×Zï,MÕ«×|ê×JÞ̯,Ú•å=o2kÖ¾þzkÖ|Ë_¼§íÊ)jAff“& ÆÔ´kÖ zu ­ÅR¥Š~™í+##˜˜H­Ìç“––Ê{ï '8øN™ï»ÿ|2 |ÄØ±}Y²d¶zrZMã9þ$îî6üõ×~ÎýéÓG;¶­ZÕàõ×{çÛVqçÛÕµ:3fŒãË/ßçË/ßÇÃÃ;;õí´[¶¬F_ß€Æ ìר¸*Í›·aùò¯4ŠóY›9ókš5kͬYdضÌ™óvvMùì3/m‡R€ä§\e•ŸŠ:/šÄ¡é:!7Õ¬Yoïíœ?’;Öh5–ŠêÌ™cLŸ>Ž ¦±hÑÏe:ÝIi‘ü“K×GQQáLœø ­ZÕ ÿÖùæ×R©TlÚôŸ~ú6¶g̘^Ü»¤ÑvÿM“œö$ÇŽýƒƒMšèsäÈïdd¤3sæxììtèÑ£ §Orï4¨“&ýsëã“öñ~XHŸ>͈åõ×{Ó¹sý'~®þY³&°hÑtæÎ}—¨¨ü·*V„Ü«©ÆÙºõ8ææ5?¾©©)Ú©Ô„„ÜcáÂi¼÷Þçôê5@Ûá<1¿ý—Ïÿã|}÷`o¯`âÄ—9xЧÐu$oïiÛ•OÊ7-oôé3ˆwßÃÂ…ËT.‘`ËÎÎbåÊÅŒ;•† ´Nýú U«.dMM«1yò§ØÛ;ç[oÊ”á$''1yò§ÌŸ¿š»Ì›7µÈí8†O?ý€^½0þj&Nœ®ÑþjÕªC@À%‚ƒï²xñtÆû€õëx™¯¿ž¥^ÏÇg=>>ëùüsoæÌùŽîÝû1kÖN:ÂË/Âѱ5jX0þjÆÿˆ¨¨p‚‚®qòäá|£:Ö¬ù–Ÿ~ZÂìÙKù䓯Yºt{÷nãõ×{£R©4Ž'))øø&‰‰ÂÇg=cÇNeÒ¤Ùüý÷Q† ëLZZªFç{Р×Y¸ð'fÏ^ʘ1ïL›6èß$û÷ïxâh‹V­Ü8pà×r1ºE¡Ðã³Ï¼¸té4~~¾Úç¹réÒiN:¬YKÊå ÉOe›ŸŠ:/šÄ¡é:%799µdذ7ùþûùÒ1QBJe6Ó¦¥Gþ|ôÑ|m‡óÔ$ÿhçú(##•+1}ú"6oþ‹»wo°hÑ4õïW®\„¡¡óæ}Ïöí~„‡?døð.êë'Mi’¯ŠÓ¥Ko<=‡¢££ƒ›[w ùâ‹XYÙ`mmKûö]°¶¶ÅÚÚ–3k´ï7®°sçZîÜ díZ/úôL­ZuÈÈ(¼8¹{÷&¾ÿ~sæx1}ú"¦LùŒ›7¯å[§¢ä^M™›×dժ߈fÙ²/´N©Yµj1VV6꿽Úö¤üö´Ÿÿ»pÁyó~`åÊߊ,zJÞ|²ÿÒ®|R¾©ˆyãí·?¡vmkV­*üóö<’`;þ$qqÑ 2NÛ¡ÉÐиÀ2cãªù~ÖÑÑÁѱ…úg‡f^~&û«U«..m˜:u.;áäÔ—¶\½zÈ}šòܹïòÑGóÕ·,Ž1Þ½R«–U¡ûmÜØ‘=^η,&&’%Kf3rä[èéUr/*Þyg&§OÿÅo¿mÔ(€®]=¹|9I]„{.ü‰.]ú0nÜL:—ˆˆP¶mûxòùV©TŒûOãâóÏß%;;›yó¾GGG‡œœ®\9‡¹yÑslÔ¬iIRR·nkYprrÅÙ¹U4ñl>ü ØÓ¶mgm‡R$ÉOeŸŸþM“84Y§¢å¦¡CÇ|W¦'(¡?ÿÜKhèfÎüFÛ¡üg’Ê>ÿ(z|òÉÿhÔ¨ Mš4ÇÅ¥z[¡¬Yó-¯¼òÚ£uôí;˜¨¨pŽÙSì¶Krlš>|âõ×ßSÏ…V½ºE¾¿ë-÷jÊÒ².&Lcóæ•%¹Yž8ð+C†¼¡þlh[qù­¤ŸÿÇåääð¿ÿ}B«V>|üSÇ(yó¿µ+‹Ê7µjÕ©y£J}† yƒ~Õv(å†ËØÝ»7©V­ºÖ'uÿ¯6n<ÂÛoBFF:Û¶ý„¿ÿ™÷¶–„®®ȽÌS§N=RR’8wî999ùÎkõê¬Xñ+;¹]==½|?_¼ø7©©)&%ïÞý%þþûOâÉ£P(4:>³|?0€+Wrÿ“Î·ŽŽuëÖr‡¤=ºqã>ÀÁ¡‰‰qdgg=ñvs33s wHxyѬY+îÞ½©í0ž+÷îáììªí0þ3ÉO¥›ŸþM“84Y§¢å¦¦M]P(’—JèêÕ ØÙ5UOÔ^ÙIþ)Ýü£§W%ß¶lm© .ø‘ÅìÙ™9s<3gŽ'22”aÃÞÄÀ@óÛÌ5=6M´oß›†øø¬W/ ¼ŒR™ÍþýÛÜ¢NŸ>ƒJ´ï¼óPÜӲϞ=NddMš4Ï·\_ß@ý}E˽%ѵk_’’*Å\€ qÄÄDÒ¼ym‡¢V\~+éçÿqŸ}6‰¤¤<<ú=óã¨ìyó¿´+s£`¾©Èy£yó6DE…“”” íPÊ)–¹ÜYR©äûïðþû£¨_¿±Ö'ó¾yó*ÙÙYÿùÖ¬ÐÐû$$Äæ[^½ºFFÆDD„þ§íkÊÒÒCC#22ÒÍÎwjj óæMÅÚÚ–wߣ^ž÷GåIñóz·ÊÓgS¥Riebß癎ŽN¥¸½QòÓ³¥Iš¬SQs“䥒« yES’ž­Çÿÿݾ}#£ªÌŸ¿ºÀWý5Þfi›ŽŽŽáäÉCDE…sîÜ Z´hGÇŽìÚµÈ-€äÝÚXÚçõÎ@àÉ©¨¹÷y“÷Y/OïAqù­¤ŸÿdzeËj.^M»òß*rÞÈ{åú-—ËXÆ$%%”›'ä<œœÆóäÖ­V¬ø•öí_ÐvH˜˜˜‘‘‘^èã’ÜP¯^nOσ…¿?vvMŸ.À§ ££ƒƒC3Ï··÷þ{™¦ÇVƒ%8ø.cÆLrooôôBõêùætÓtß©©Éää( Œ¾ù÷±´níN‡Ýøõ×_ظñ{ÒÒR¹|ù,çÎ 66Š={6“––Zar¯¦ââ¢?¾?–Lžü©¶Ã)5&L#"â!ß¿@Û¡”(¿iúùÔí¥2'§–L:—ýûw{Ì’7 WíÊ¢òMEÌË—ETT&L+~åç„âóÏ?ÿüi_¼}ûvÈÊb¨§g±ëÎõò¢ï¾88?ùÖ™ kAìß±Ÿ÷Þ{ê°Ê½æÍÛpôè>víÚÀK/ /ttYð÷?ƒ—×Ü¿‹èèH¬¬lhÐÀggW‚‚®±k×.\ðc̘)DE…Ó =VV6tìØèèNœ8„¿ÿiz÷HÇŽÝñõÝChè<=‡èy»ví«Wÿ›7¯Bµ°¶¶ÅÀÀ°Øý…‡‡ðã_“˜Ojj2-Z´ãÀlÙ²Š””$ttthÓ¦3}ú "!!Ž NqúôQê×·cÆŒÅê§ Y[ÛräÈïøúîÆÕÕøøV­ZÌÇ÷‰ŽŽ ^½†ØÚÚѼyœœZ²fÍ·øûŸáâÅ¿©QÂiÓ¢££ƒŸŸ¯Fñ„…säÈzôèO£FEñnÒ¤9aa!øø¬åìÙãøùùâîÞƒ‘#'¹“À>é|üòËw4hИ¤¤xNœ8Ä;ùúëYÔ¯oG×®ž4iÒœ;ÖàâÒ++›|ûOOO㫯ÞgÑ¢5T¯nQš±§2oÞTþúk?«VýFÍšµµÎSÛ·o;ègá9´øüè5×*ÁÔ IDAT‹¾}‡àà ý3WW7¾ÿ~aaÁtïþ’ÖæËü¤ýüô¤óR\€FëT„ÜÁoxâèØ‚™3¿)sÈ”4¿ éÛg‡'_] bÇþý¼÷ù{Åïû>ÈÒÇÓs¨FñZ[ÛbkkÇ¢EÓ ¾K§N½¨R¥|<Õ²0’´Ÿ||Ö³mÛ¤¥¥’œœDëÖÙ½{Û¶ýHzz©©)tìè§çP""ò÷ßG9vì ˜;w9–…¾ q…¾·šä«’hÔ¨ ÉÉIùF;Y[×§aCõ(½<Åí{Ë–ÕlÙ²Š´´"#C©W¯µjYù9íÝ{‘‘alÝú#›6ý@Õª&ÔªeEÓ¦.¸ººÑ =M›º”ûÜ«©  kŒÛ—´´TÖ¬9€……æ×Ž^^sËuûÔÌÌ3³êüï3pphFãÆNeºÿÇ×y<¿iúùß½{ë×{Kjj 5Á¢¿þº†“'‚½½ææ5òÅ"yóÙµ+“’رã—ù&oÛ)oìÛ·/¾˜Âœ9^tìèQæû/i~ùü½â¯¿¶ïÛúú ªÙõWatTÿá~Œ!C†@J Û½½‹ß‘^[½xqè‹O\oï¶½L6…Û·+öm"Å fذÎÔ¨Q‹Ÿ~Ú[¡ ¢b¸råÞÞóX¹ò·|Ë—,ùSÓjŒÿ‘–"Ë•““±v­ß~»OÏ!Z翚MHÈ=m‡%D±Ú´©Uì—¯ïm‡Y"å5÷jâØ±ôë׊ŋg0aÂ4¶lù«ÄÅ¿ŠdΜïxã÷ùè£Ñ|ùåû¤§§i;$ñœ*ïy#--•¹s§0mÚX&Lø˜Ù³—ÿ¢çŒµÈÖÖŸÓ4lèÀàÁn,\øq¾{ý…(m/¾8 {{gÞÍõëþ$%%0mÚB­Æ´oßvz÷væÊ•ólØàËK/ ×j<"—§ç6n<µkéÝÛ¹Ø9G„ø/ÊSnJKK嫯>`øð.8;»²cÇ)êÔ©§•X*#++Ö®=Èòå;8ÞfÎÏÝ»7µšE:w.ªØ/~Ú³ÄÊSî-NNN¾¾{<رcûR¯^¸Ê{ï}ŽB¡§íðž)¦O_Ä’%رc }û6çøñƒÚK<§ÊkÞ8vì}û6ÇÇgß~»‰>š_.¦m)o*w¶¬ÌÍkòË/°mÛO,\ø1îbÞ¼ïqwï¡íÐD%Õ¹s/õ÷ŽŽ-´GXX0sçNáðáß6ìMfÌø¦¦Õ´(ÈÕÕ®²páǼûîPz÷Èœ9ßaiYWÛ¡‰J¨<䦓'3{ö[$$IJ`Á ôºVâxôî==úã㳞+æ³mÛOtîܛѣ'Ó¥KïJß ¢¼(¹÷IâãcصkëÖyóàÁmºvõÄÇçŒúI­Ï“þýGÒ¾}WæÍ{×_ïM¿~#øä“ÿÉu™(så)o„‡‡°`ÁÇüþû^|q(³gKíÚVZ©<“€å€ŽŽƽÉ×pphÆèÑ=1âNž<¬íЄ(u!!÷˜=û-ºukÌÍ›WÙ°Á—¯¾Z%Å¿rÊÔ´_}õöî;<ªjkàð/™IïuÒ{!H B MÞETDÅ –{Eý¼ˆzõ‚bA,ØP, "Eé%RC $Rhé½÷Lòý2š€À$a½Ï“'3“ÉÌ:“Éš³×^çìÏøæ›$$":Ú›¹sŸ&;;]Û¡ qÓìÝ»I“¢™wk;´6Aޝh…ºtéÉ×_o 1ñŸ|ò&>:•Ê™‘#ïcÒ¤'pqñÐvˆB\“ÚÚ6oþ•åË?c÷î-¸¸x0wî"Æ{¥ROÛá‰ë¤¯oÀý÷?Åĉ²ví2>üðu~øáSºwïÇĉq×]£äï*Z½’’"þøãg¾ùæC’’â â»ï¶Ð½{?m‡&WWOžzê%žzê%’“X·îgÖ¬ùžÏ>›µµݺE5€¾}‡ÊaoB´qii§Øµk3[¶¬e×®ÍÔÕÕÒ¹swžþM†¿{m‡ØªééésÏ=S?þa¶mû>zƒ‘#»àãȘ1“;vŠ,`%Ú…ââBÖ¯_Á?,&!á™? #F܇B¡ÐvxmŠ[±ÀÀN|øáO¤¤$òã_ðÓO_òÅïл÷`&Lx„>}îÆÀÀPÛa q‰øøƒ¬X±„5k¾§²²‚~ý†ñùçkéÝ{°$év@©ÔcôèÉ ~/[¶¬eùòÏ™9s"66öŒó cÇNÁÛ»ƒ¶ÃBC­®g×®ÍüôÓ—lÞü+FFÆŒu?ï¾ûÖÏ]#®Ì×7ˆ™3ƒ˜1c‰‰‡‰‰ÙÀŸndΜé¼òÊ“v¦wïAôì9ÐÐŒŒŒµ²â*òò²‰Ý¡ù_ÎÉÉÀÊÊ–¨¨¼þú'ôê5H:°o€®®.ýû§ÿá<¸‹_~ùš?~“wß}…èè»;v ½{ÆÐÐHÛ¡ qͪ««Ø¾}+W~Ãöí`llÂС÷0wî":wî®íðÚ,)¶>>¼üò»üûßÿ#&f«WËôéÐÓÓ£Gþ 2žFÊ9Ô„Ö444pèÐn¶nýWqútžž~<ñÄl™}lÇ”J= àAcÈÉÉ`õêïøá‡OY¼x®®^ôë7Œ!CÆ%«p‰Û®¦¦š;7±uëolÞ¼†üü‚ƒÃyõÕ1â>ŒM´¢¸F:::…Æ“O¾HUU%‡íf×®Ílܸš?~…B‰§§]ºô¤K—(‚ƒÃñõ ÒvèBܱÔj5§N >þ ìâÀ¤¦GWW—€€NŒu?QQèÖ-Zý¹‰ÂãbΜÙ²e-«V-ÕŒÃãè×owß=•ÊIÛ¡ q‰¢¢|¶o_ÇÖ­¿±cÇzªª*èܹ;¯½ö‘ì»Ý$’mÛ==}ÍìNNN›6­aãÆUÌž=GéÑ£?ޤgϸºzi;\ÑΕ–³oßöóƒë_),ÌÃÇ'!CÆ3pà(BBºh;Dq©TÎ<þø Lú<ìdÆ•lÚ´šo¾Yˆ‹‹ƒ¡oß¡„…õÎeqËäåe³{÷6o^Ãöí먮®¢S§nLú<ƒ‘ÏÆvÂÈȘ¨¨DE àßÿþg9xp‡ïáàÁÝüüóW¨Õõ88¸EXXw;ÐQ&K…¸E22ÎrüxGîçСÝÄÅÅRYYŽ™™;wcèÐ tîÜλajj®ípÛ=C† Ï!ãÉÉÉdÛ¶¦ýõùógóßÿ>KÇŽ‘ôï?œ¨¨…É:B+Ôj5ññÏŸ àWŽݾ¾=zô祗пÿpìì´f»"À6J¥ræþûŸâþûŸ¢¤¤ˆ­[ײqãjÞ|ó9*++pqñ GþtïÞîÝûÉ?ŽøÇª««8xp»woaÏž­ÄǤ±±‘ÐЮLúŽÂËË_Ûa -S(DFö!2²¯¼ò>ññÙ°a%7®âË/ßÅÐЈðð(zôèOýe§Sü#eìÛ·ƒ]»6³{÷’“P(”DFöaöì·0`¤t9ÜœÝqvvgĈITVVpìØ~ØÅ‘#{Y´è ŠŠò¦s t"  #éС#®®žÚ _ˆ6¥®®–ä䎻àë%%EèèèàééG§NÝ6ìÂÂzà㈮®¬;©M*•'>ÆÄ‰QYYÁ®]›Ø²e-K–¼Ï;$©©9½‰ŒŒ&2²eßLÜjµšÄÄÃìÝ»}û¶³ÿŸ”——bk«¢_¿a<ùä‹DE ”SzÜBRl,,¬=z2£GO¦®®–Ç÷²gÏVvïÞÂÊ•K©¯¯Ã×7ˆˆˆÞtìAÇŽ‘xyùˇ±¸ªüüŽÙÇÑ£±šATMM5^^þtïÞÇû7ݺEcii£íPE+¥££CHHBBºðüóo’™yŽÝ»·°k×f¾þúÞ~ûE,,¬ˆˆèCXXw:uêFpp¸´÷‹+ÊÉÉ$.n‡ïåÀÄÅÅÒР¦C‡Pzõº‹^˜GDDoŒMµªÐ"cc“óÙhÍmYYi-Š«WLJ¾FCCææ–tÄÛ;//¼½;àéé³³»ì+‰;Vuu§O'qêÔIΜI"5õIIñ$''R__‡¡¡~~Ávbðà±ç ê¡ÒÝ×Ê›0pà(Ecc#))‰ìÙ³ØØ,^<·Þz33 ºvíEÇŽ„†v%$¤ VV¶Ú]´AEEù=ºŸcÇËþýRVV‚=‘‘}xþù7éÖ-Ÿ@9]Ðm"ÀvFOOŸˆˆÞDDôfæÌW©¬¬`ÿþöìÙÊÁƒ»Y±b 55Õ˜™YÚ•N"éØ1’.rÒÝ;Xee9‰‰Gˆ‹‹%.nGŽì##ã,:::xyùÓ±c$ãÇ?D÷îýpppÑv¸¢rrrcܸ‡7î!’’âÙ½{ {÷ngÉ’÷ÉÍÍB¡PâïL§NÝ4ùÉÓÓOf¢ï@ÍyéÈ‘}9²—#Gö‘••†B¡ÀÛ;€°°î<øàÓtïÞOVŠËÑÑGGWúõ¦¹­²²œ'Žrüx‰‰GHM=Ά +)(ÈšV>÷òòÇÓÓOO?¼¼:àåå»»·L~‰vA­V“““ÁéÓIœ>ÝTä;}ú$§O'‘‘q–ÆÆF %..xyùÓ»÷`ü:âéé/ŸÍmœŽŽ¾¾Aøú1yòôÁv²bÅÞ{ï?@S÷tS1°+¡¡]脹¹¥–·@´&¥¥Å$&æØ±=ºŸ£G÷“ž~WW/BC»òÜsÿ•‚Ÿ–I°366¡OŸ»éÓçn i%ÄS§NjNÈ»aÃ*>ùä-Í x󇀯o ÁÁáv’NŠvD­V“™y–¤¤RRINNàØ±ƒœ:u‚††MaxìØ)‡Ö]füÄ-ã猟_0S¦Ìšº»âãƒw±jÕRªª*Q*õððð%$¤é¤þ>>„†v•S´#ÍûËå%ssKBBº0~üÇ…¥¥µ¶C퀱±)aa= ëÑâöÒÒbÎKåܹS¤¥"99˜˜ ,Yò>UU•@ÓùµìípsóÂÕÕ«Åw;;G9ô\´%%E¤¥Ò¼Ÿ›¿çäd’ž~šêê*ÌÍ-qsóÆÍÍ‹1cÄ×777/¼½äp¼;ÄÅA€²²Nž<¦Ù7ûê«wÉÍÍšŽBóñi3úù5íŸÉرý«««åÌ™dâ㒜ܴߖœœHzúi±·w$88œ1c$88œN"e¢¶‘àF¡PjûèÑ“¦ƒ„„CœÌÙ³)œ>¤ùžœœ@MM5 …77o:teذ{ð÷!  “œIh•Jå„JåDÿþ误ãäÉcœ8qTó=&fƒ¦;ÇÞÞ?¿`M^òôôÃÃÃWWO”J=mnЏŒ²²ΜIæÌ™¦ÜtêÔIM^jþ¼qw÷¡C‡P†Ÿ¨ÉK..Ú]ÜaÌÍ- '88¼ÅíjµšŒŒ3œ;wŠôô3ddœ!=ý 'OcëÖµšAqóc¸¸xààà‚½½Óùü挽½#.ØÙ9`k«ºÝ›&Ú‘ººZrs³ÈÉÉÐ|ÏÉÉ$77“œœL23Ï‘‘q–ººZ é(!''7\]=quõ¤[·¾¸¸xàâ⇇/ÖÖvZÞ"Ñ™™Yœ_]½§fÂ6;;'Ž’”ORRìäÇ?§ºº ó£pw÷ÁÝÝwwÜܼquõD_ß@Ë[$®Emm çÎâܹTΞM9ÿ=•S§Nž~†ÆÆF ðñ Ä×7ˆI“Çß?„BQ©œµ¾¸ ) ,,¬4'äoÖÐÐ@ZÚ)Ž#))žÔÔÄÆÆðÓO_RVV4ãææ}~ðí““NNn88¸àèè*Ý9·Pyy)YYidfž#++¬¬tM‚>s&™’’" igÏÍÍ _ºu‹æ¦Ñ¡C(>>iy+„¸:¥R  0‚‚ÂZÜžŸŸÃ‰G9qâ(©©ÇIJŠgÆ•äåeMÎÎîxxøâáዳ³;ŽŽMyÉÙÙ;;G™¼¸jjª/ÈIiddœ%#ã,§O'qæL²¦p«Têáââ»»ááQLœøñõ ’.Ѫ5Ož¹¹y_öç55Õ- ƒéég4‡WîßCVV:••åšûëééck«Ò\°¶¶ÅÊÊ[[ÖÖvšëVV¶’·î55ÕåSX˜O~~……yåST”OnnyyÙdg§“——­É©ÐÔ¹ecc½½#*•3®®žDFöÁÙÙWWO\\<°·w’÷¸)\ppp!:zˆæ6µZMzúiNžtAn*¾¤@v1ccSôôô03³@WWÑbÕ<3”Ê–ÿçJ¥&&-g[)--¾ä±«ª*©­­šÎ™XQQNmm5ÕÕU-~v%Vì47 J­­íðôôkÑ imm‡½½#ÖÖvrú!n…BÃuŸNårÝfN<6 Ë4E­üü**ʨ®®¢¢¢œŠŠ¿ `×ÃÜÜôôô12j:„ïâÁµB¡¸ê¤åßMè––kŽ2¹ØÅûiÕÕ•ÔÔÔœÿݦâhmmf!—k¡««{¾h`Œ!ææ–ahh¤éºT©œ5ņ‹÷õšóª™™E«˜¬¢5²´´ÁÒÒ†B¯z¿æ†‰æ·¼¼lÍ!óÍy.++²²Òó9¯©`Ö|(땘šš£P(03³@GGW“ÏàÊcÈËí¿Õ××_6w6OÀ_ûs šÉ‚‹s×Åšsgs“LÓ—9ææ–8;{`mÝTäkC6O$Èy=ÅåHuAh…¥¥5––Ö;~!µZ­é +--¦®®ŽŠŠ2ÍÎ\uuÓ`·¼¼ µº¾EÂoî€iùx—OÒÖÖ¶\:À½ðà9é››[¢«Ûô¡§§§™9ož]jÞékË3äBÜI,,¬°°°ºÎÜTßb°]WWKee…f‡¯¢¢LsŸæ<Öìr;55í(6ÓÑÑÁÙÙ]3©ÑL_ÿ¯Á¶R©ÄÄÄL3ˆ64lê@l„[XX¡P([t+ !Ú—æÿogg÷üXwÀÕÕÕRWW§9Lµ²²œºº:Í@šÎa×|8ßÅï v¥ç»xöB*•3zzúWüyÓ$Ë_ç£jÞƒ¿ø ESž„¦nL==}ttt43w@^íù„·Wó„äõª¬,×tÎUWWiòYóø°¼¼”†µf’áÂ}³Ë!á¯I… )•ÊË6Á4ïŸ5³°°Òä]ݦâ^ó8ROOcc 13kêZ”CpÅÍ$@Ñf( MáP!Z …B©9I¿B´ÆÆ¦›JnB´i͹LV§dÙ!„B!„B!Ú1) !„B!„BÑŽIP!„B!„BˆvL €B!„B!„B´cRB!„B!„¢“ B!„B!„혅B!„B!„hǤ(„B!„B!D;¦ÔvBÜL••唕•PZZ¬ùºðú_—‹(--¦±±‘††ÊÊJ4QQQF}}=µµ5TWWÐÐЀ®nSÝÜÐÐ}}”J%&&fšÇ07·DGG]]]ÌÌ,07·ÂÜÜssËó×/ÙØØô¶¼NBˆ›£®®ö²¹¦´´è²¹§¶¶¸4ÏTU5å™ÆÆ–ù¨¾¾¥²é£Z¡h™gÌÌ,4ùÈÈè¯|dbbö·¹ÆÌ¬é²……Õ­‘„B3ša IDAT!„Z'@Ñ&››Evv:yyÙde¥‘››ENN99™äädPP‡Z]Éï*Ê` +ÌÌ,prrCWWüU°ƒ–i¥R“Ëå**Ê©¯¯Zà/,(64¨)--&-í%%E- WŠÕÖÖ•Ê{{G\°³sÀÑÑ;;\°·wÂÒÒú¾¢Bˆ«©®®"''ƒÜÜ,²²Ò4y'//›ììtrr2ÉÏÏÖüß_ìrÅ7KKk Œ€«ç™ óÑ…Z )--Öüì‚bYY ¹¹Y¤¦×䛲²’+Æjiis>¿8cgç¨É7æ[[ …â^I!„B!Dk @Ñ*TUUrútgÎ$k¾Ÿ;—ª)òÕÔTkîkddÜb`Ú­[4.ØØØkÚ}·ÄØØD‹[vev+^Ø)TP«)<$%ųk×f²³Ó[ Þ Q©œQ©œpsóÆÃÃ_<=ýðððÅÈÈX‹[&Dë§V«ÉÈ8£É9§NäܹT²²ÒÉÍͤ¤¤Hs_¥R[[ŽŽ.ØÙ9Ø™èè!¨TN-:ê.œdhš»/Ì;%%Eåk ›yyÙ$$"77‹¢¢|Íï* lmU¨TÎ89¹µÈ9^^þX[ÛiqË„B!„G €â¶©¯¯#-í´f°}æL²fð4 2=ððð%0°ÑÑCpppÆÞÞ •Ê •Ê33 -oÉÍallб±)*•ó5Ý¿¬¬DÓñ˜››IVV:yyYœ9“Bll ™™gQ«Õ8:º¶ 7¹ºz¢TêÝÊÍ¢UÉÉÉäÌ™¤ &’9}ú$ç΢®®{Mñ¼cÇHT*'ìí4¹ÇÖVuÙŽ¼¶FOO{llì¯éþµµ5šë漓““AFÆYvìøƒo¾Y¨™˜07·¼$ß4_¿ð°e!„B!„vHPÜõõuœ>D|üAŽ;H|üAQ]]€……®®^øúÒ«×]¸ºzáææ…·w€t¯]AS—‘>>—ýy}}YYiœ;wŠ´´S$%%’’ÈΛHO?Mcc#J¥¾„„„Üô&¯¹hrr2‰?¨ùŠ‹‹¥  }}Üܼñó bÀ€‘¸¹yáêê…¿¶¶*-GÞ:éëàêꉫ«çïSRRDZÚ)’“HNN$-í›7ÿJJJ¢&ßÛÛ;jòMpp8¡¡]±³s¸]›!„B!„@ €â&(++!1ñ ‡HLŽŸ_žž~²ØÅ- TêáêÚTÔ¸XEEgÎ$“”” ù;mÚ´†òòR %ÞÞ êLPP ꌩ©¹¶Bˆ¿W__Grr¢&ß$&&1ñ••å(•zøúØ™éÓÿÿP¼¼ü¥àt‹XXXaaÑTØ»P}}éégHI9ÎñãGˆ?ÄŠKX¸p.*•3ÁÁMù¦ù»““›66A!„Bˆ;‚ÅuËÉÉdß¾íÄÆÆ° ©©'hllÄÚÚŽàà0ú÷ÎŒs ꌛ›w»8t®­311#((Œ  0F~hZDàܹTâãiŠ‚}ô_ŠŠòÑÑÑÁÛ;€ˆˆÞç¿ú R9iy+ⲂÇ÷°oßbcwpôè~jjª144Âß?Dó¾ Ãß?D³¸†Ðžænc_ ¡¹½¨(Ÿ„„Ã$$4åµk—±hÑë466bccO×®½ˆŒìCDDüü‚5« !„B!þ)Š¿•‘q–ØØçß1œ=›‚R©GHHú÷Á¿þõ?‚ƒÃpppÑv¨â:èèèàî»C‡NÐÜž••FBÂ!ÜÍþý1üøã¨ÕõxxøjŠ‘‘}¤[GÜ2e8°“ØØöíÛÁ±c¨¯¯ÃÝ݇ˆˆÞL˜ðAAaøø PÈÇX[beeKÏžéÙs æ¶òòRKlìÞ{ï?”–ciiM—.=‰ŒŒ&"¢7d%b!„B!nŒœÄ% óؾ}»wo%6vgÑ×7 cdž ›HDDoÂÂz´ÚÕuÅ?ãè芣£+ŒšV+n.îÛ·ƒU«¾¥®®""úУG?úô¹[V7¬¶¶†}ûvð矈!1ñ0jµoïDDôaòäéDFö¹æsDÛbjj®é6~ôÑçQ«Õœúè¿ü÷¿Ïbjj~¾ ؇èè!øùk;t!„B!Ú ) ’“زe-[¶¬åÈ‘½(•z„…õ`ܸ‡ˆŒìCÇŽ‘i;L¡ÆÆ¦ôêu½zÝ@uuGŽìÕ Î_zé1Ôêz:uêFÿþÃéßø*¢YQQ>Û¶ýÎÖ­¿³ŠŠ2|}ƒèÖ-šÇû7½eqŽ;”B¡ 0°3yè¡Y466’œœÀÞ½ÛÙ¿?†Ï?‡yó^ÀÕÕ“~ýšrNddYá\!„Bˆ«àJ­Vsøð¶ný͛ךzKKzôèÇüùK8p”,!.ËÐЈnÝúÒ­[_ªª*Ù½{ [·þÆ×_Àüù³qqñ gÏ»è×o½{BOO_ËQ‹Ö -í[¶¬eëÖߨ·otêÉ´i/s×]£ñôôÓvˆ¢ÒÑÑÁÏ/?¿`&OžNCC‰‰‡5便K?ÄÈȘnÝúžŸ„!‹¾!„Bq)ÞAêêjÙ¶íw~ûm911(++ÁÏ/˜»îMÿþÃéØ1RN¸.®›‘‘±¦ó¯¡áŽÙ«é&]¾ü3ÌÌ,èÝ{0ÇO$:zˆï0 ‡X½ú;6oþ•sçR±¶¶£oß¡|ðÁ2z÷$+‚‹ë¦««KppÓÊÃ3g¾JZÚi¶mû-[Öòê«OóŸÿóÌ}3`ÀFž,ïI!„BqÇ‘`;R]]źu?ñË/ß» k†¿—·Þú‚.ÚOÜáT*gM1ðèÑý¬Zµ”o¿ýˆE‹^'22š1c&3dÈYl¦‰aÅŠ%¬_ÿ 55ÕDGßÍÂ…Ëé×oúúÚOÜÁ 5ÅÀÜÜ,~ýõV®ü†U«¾ÅÝ݇ѣ`ܸ‡pttÕv¨B!„BÜrr·v -í4ÿûß¿‰Šráå—ÇŠO>YÅÞ½™Ì™³PŠ¢Õ íÊœ9²gOŸ|²ssK^~ùq¢¢\™7ïÒÓÏh;Dq••,[¶˜!CB¹÷Þ>œž±c§ §§¯íð„¸eT*'^xa;wžãñÇ_àûï?!:Ú‹>zƒŠŠ2m‡'„B!Ä “`+¶ÿŸŒÁôéãñõ dݺc¼óÎR¼½;h;4!n+Ÿ@,ø–?þ8†——?O>9†±c»qðà.m‡Ö®äæf1{ö# BJÊq>úh«WïgÀ€èèèh;F­®×vxB!„B\7)¶Bii§yúé LœØ +Ö®=ÌÂ…?âã íЄÐ*Ÿ@-ú™_=„‰‰÷ÜÓ‹3&’‘qVÛ¡µi55Õ|üñ›ôïïÇ®]›yç¥üöÛîºk´þÄÍÔÔœ3æ°}û)ÆŽ}7Þx†!C:³AÛ¡ !„Bq]¤ØŠÔÕÕòᇯ1hP 'NåóÏ×òÍ7éÐ!TÛ¡ ѪvâÛo7óé§«IL<ÌÀøè£7¨««ÕvhmÎŽ0hP Ÿ|òO<ñ›6`ĈIèêÊǃÍ,-­™=ûm6lHÀËËŸ‡Ìã$++MÛ¡ !„BqMd„×JÄÆÆ0lX'>ûl>Ï>ûüqŒ~ý†i;¬›fÓ¦ÕDE¹’’r\Û¡Ü0µºžýûÿdÁ‚—¥û£•0`ë×ÇóÌ3¯ñÉ'o1lXgØ©í°Ú„¼¼lfμ—‡Bhh›7ŸdÚ´ÿÃÐÐHÛ¡Ý4’wÄÍæîîÃ'Ÿ¬ä»ï¶pêÔIîº+%KÞG­Vk;4!„B!®J €ZVUUÉœ9Ó˜4)WW/Ö¯O`êÔçP*õ´ÚMedd‚=†×ý»¹¹Y· ¢ëwôè~V¬XÂÇ¿Ivvú?z¬Ù¦Öò:´6J¥>ú/Ö¯OÀÙÙ‰{3wî ª««´Z«µví2 $.n_}µŽ… —£R9i;¬›NòNK’wnžîÝûñûïq<òȳ̟?›ñã{pút’¶ÃB!„⊤¨Eññ92œµk—ñþû?ðÅ¿áìì®í°n‰ž=òë¯quõ¼®ß+))âÙgï¿EQ]ŸÎ»óàƒOÿãǹ‘mjM¯CkåââÁW_­cÁ‚oY½ú[FŽìBbâam‡Õª”–óÌ3÷ñÌ3÷1räýüñG<}úÜ­í°nÉ;‘¼sóéë0kÖ\Ö®=„Z­fĈ0–-[¬í°„B!„¸,¥¶¸S}ùå»ÌŸ?›®]{±té&\´R«SUUÉÌ™IK;¥íP4ôôôÿÑïßÈ6µÆ×¡59ò>ºvíÅóÏ?Ș1ݘ={>S¦ÌÔvXZËôé㩯¯ã«¯ÖÑ»÷`m‡Ô*µÆÿ7É;­›O ¿ü²‡>x•9s¦±cÇzÞ~ûkÌÌ,´š·Leeee%翊/¸\BII¥¥ÅTVVP[[CEEju=¥¥Å¨ÕjÊÊJ¨¯¯£¢¢œÚÚêK:ökkk¨ªª¼æXÌÍ-/Y°ÊÌÌ]]ææ–(•JLLÌÐ×7ÀÈÈCCcôõ 055ÃÈÈ33 ÌÍ-13³ÀÌÌ «×ÛÛQ9B´Gµµ5”——jòPii±æzyy)åå¥TT”SZZLcc#¥¥Eç¿7å¥òòRÔêzÊË˨¯¯£²²ü’ç())º®˜,,¬.¹ÍØØ¥RSS3 %¦¦æ( M37·:ÿÝSLMÍ13³ÀÔÔSSsÌÍ-17·Ô\××7¸á×LÜ™¤x›UVV0{ö#¬_¿‚gŸ}ƒÇûw»?Ù~II6¬ä·ß–óÀÓ8p‰‰Gøõ×ïY¿þÖ­;Æë¯ÏdãÆÕ¸¹y±pár\]½Ø¸q))Ç)))⥗ÅÓÓŸG}žÆÆF–-[Ìñãq$$ÂÌÌ‚¹s?ÂÃלœ V­ú–Õ«¿cùòfͺ—ÔÔÌ›÷11ë¯ú|ùù9¼ûîÿáääFfæ9 óùßÿ¾ÀÒÒæº¶91ñ_ýÞÞ8th7UU•,]ºéŠÛtµç½‘×áj1Ü œœÜøî»-|úéÿxóÍ爋‹å¿ÿý ccm‡¦Ë—ÎܹOÓ­[_Þ}÷[¬¬lµÒ-'yGòÎí¤TêñÜsÿ¥W¯AÌœ9‘Ñ£#øä“•øúi;4!®‰Z]O^^699™äRT”O^^6¹摟ŸC~~……yæS__wÉcèêêž/œ5PÍÎt›¸..èèè¶(Êééé_òÙ¬««ÀÔÔüšânhh¸_¬¤¤ˆ††ÊÊJ4ƒúÚÚª«+).. ¦¦šÊÊ **Ê4Å‚ŠŠ²Ë>‡‘‘1ÖÖvØÛ;bee«¹lmm‡µµvvØØØ£R9]wÞB\^uuÙÙéš|TPД‡ŠŠòÏ硼ó9)Ÿ¢¢|jkk.û8ÆÆ&š"š‘‘ Øtu¸ºz¡««‹™™ …â|¾RbjjvÉc™˜4í®…Z]ÙœR^^v>'•j&DšrUSa2=ý4””QUU¡)`VVV\öy ±´´ÁÚÚ;;M޲²²ÅÖV…MÓe{\ÚÕ¹¾Å‘àm”‘q–©S‡‘——Í×_o GþÚé¶ÈËË&99]»63iÒØÙ9˜x„´´ÓÌŸÿ<ò,<0±c»ñÎ;/óÁË9ò>~ûm9IIñ¼ùæçšÇ[¼xöö޼þú'¨Õj† eâÄÞlÛ–ÊÉ“ÇX¹òΜIæ›o2xð8~úé ,,¬þöùf̘ˆ­­ŠéÓ_`ذN¼þú,,øöº¶yÆŒ{˜7ï+Â㨮®âÁï¸â6]íyoäu022¾b w ]]]žzê%BC»2s潌߃Ï?_‹““›¶C»mÔêzþóŸiüøãç<õÔËÌš5·ÝO84“¼#yG""z³fÍA¦OϘ1Ýx÷Ýo8p”¶Ã‚ÂÂ<ÒÓÏNfæ9²²ÒÈÊJ';;ŒŒ³äåeµXÌÆÈÈ[[¶¶*¬­íprr£cÇHÍ`ò¹æË&&—˜Û’æ‚aiiSã…D¹šhnn& ‡(,Ì£  ¯E1ÔÐÐggw\pppÁÉÉ GGW]pttÅÕÕKàâŽWQQFFÆY²²ÒÈÉÉ$;»)åäd’™yŽÜÜLŠ‹ [üŽ©©¹&Y[ÛâààBHH¬­í°´´9ŸÌ5yÉÌÌRSÔkÔêzÊÊJ/ê¸nêpl.Š6HÏ;¥™Ä¹¸iii½½NNn¨TNš\¥R9áè芋‹ÆÆ¦ZÚJq;Hð69qâ(?|7VV¶üúëÁ;ªáãÀ€#ùê«÷4·ÙÙ9Ú•]»63kÖ\ÍŒihhWâã^ñ±rr2Y²ä}öìÉ@¡Pp÷ÝãøðÃרºu-C‡ÞCxø RSO0jÔý¸»û0q⣚Çþ»çÓÑÑ!  £æºŸ_0'N½®í­¯¯ãÌ™dâã…¡¡S§>wÕß¹Þçý»×aР1×C{Õ³ç@Ö¬9Àc`ܸ,Yòþþ!Úë–«ªªäé§'°oßv>ýtŒÔvH·•äÉ;ÚboïÈ?lãÕWŸfÚ´qÌ™ó!÷Ý÷¤¶Ãw€ââΜIáÌ™dÍ×Ù³M×KK‹5÷³³sÀÑÑ‚ƒÃ8p”¦@eoï„ýÙ1¯««‹……ÕeÛ»š¦î¤\M!##ã,ÙÙéde¥KVVšf®££ƒƒƒ ¾¸»ûàá዇‡æºÎ'Ú‹üüÒÒNqöl*gϦpî\*gϦrî\*¹šû™˜˜áèèŠJå„JåLPPØ…)glmUXYÙÞñÿ …KKk,-­¯ë÷jkk(*Ê'??‡ìì M¡5++ôô3<¸‹ÌÌ´‡<ÛÚªpsóÆÝÝ77ï—mlìoö¦‰ÛL €·Á¾};xüñ‘…ñé§«îÈó)•—¾Õtu›fd.l¥vppáìÙ”+>ΡC»©¯¯ãÿþïñ·ßsÏT ŒÎ?— …wwŸë~¾ï¿ß @MM5kÖ|O\\,×´Í”J=zõÄë¯Ï"))žýëÛr½Ïûw¯ÃÄО¹¸x°|y O<1š‰{³xñ""zk;¬[¦¸¸©S‡ræL ß~»…N"µ’VHÞ‘¼£-J¥o¼ñ)ŽŽ®Ì™3œœLž}öum‡%Ú‰ŠŠ2’’â9yòØ_ñMç usóÂÃ×®]{1~üÃxxøâêê‰Jå|Ǥo6++[¬¬lññ ¼â}ÊÊJÈÊJÓd›¿¶o_§Yá]WWggüýCð÷Æß?ÿ<=}¯ù°C!n·ÌÌs¤¤$ròd<))‰$%Å“šzBSôÖÓÓÇÕÕ77oBBº0|øDÜܼquõÄÑѵÍw·vúú¨TΚâê•”——’NZÚiΞMÑkãâbIK;M]]-ÐT°õñ ÀÏ/oïüýCðñ ¸£š›Ú:ù4¹Åbcc˜:u(ÑÑCX°à[Ùéú‡RScddÒâ´›I­VóÙgó9vì>8ƒN"9|xïu?ÎÇÿÂK/=Êò埳aÃ*-ú‰nÝúÞ´ç½–×ázchïÌÍ-ùúëõ<ûìý<òÈ–,YO—.=µÖMWZZÌäÉ)..àçŸwáéé§íÚ<É;M$ï\¿iÓ^F¥r⥗¥¡AÍóÏ¿©íDSX˜G\\,GŽìãĉ8NžXγÏÞÏC 櫯þ k×^Úë¦)//eÊ”Aä²|ùÍ"⟑¼ÓDòÎ7î! ÿþ÷Cèéé3sæ«ÚI´Ruuµ$$&.nqq±>¼—sçRððð%(¨3ãÇ?ŒŸ_0:„âââyÉê·¢í122&  c‹S2@SÁ%99á|·g<'Nıpák §§OPPg:vŒ¤cÇ:uм¤]ˆ¥Vד””À±c8zt?GîçäÉcÔ××annIPP!!];v >>øú]÷¡©¢íhêNvÇÙÙîÝûµøYqq!IIMÝŸMù*uë~¦¬¬¥RB íJHHBC»âë(5-“Wÿ9uê$<2”=ú³páry£ß ]]]**þ:'¿Ì›÷ï½÷½æösçRY½ú;f̘sÃÏËŸnä­·þ×ÕÕ]÷¡xµµ5,_þ9“'OgĈI„‡GѯŸ/{÷nÃÇ'à’mº–ç½Þ×á‰'f_5†;™B¡`Á‚¥L›6Ž'žÅ/¿ìÕ¬`Ú–Õ××ñä“cÈÎNgÙ2)þý’wšHÞ¹yFžL]]/½ô(vvš…iÄM­VsüøvíÚÌ®]›9p`'55Õ˜ššãïÂÝw#<<ŠÎ»ý£‰Ñ6éëvÉa{99™<¸‹våË?£¦¦kk;ºu‹&*jááQ² ¹¸fju=ÇÇiòÐþýRVV‚±± èÒ¥'=4“ààp¼½¤£OhXZZÑû’S+åädøøƒ<¸‹Õ«¿¥²²##c;Ó¥KO¢¢Ð¥KO µýIªR·@qq!=677oÞ{ï{”J=m‡¤u¹¹Y@S—H³²² éC§YAA.UU•šëööN劊2Âã íʯ¿þ@MM5wÝ5šòòR6lXÉ…˨¬,§¡AMii1ææ–×ü|ͳè+W~CÇŽ=ºŸääòós8qâ(¶¶*M›{UÕå—boöóÏ_qß}O¢P(P©œ03³ÐìÀ]¼MÍî«=ï®Sùü IDAT¼W‹áN§Tê±páÜw__}t8+Vì¹î“~·6¯½6“C‡v³|yŒtœ'yGòNk2aÂ#äädðê«OãîîCTÔm‡$´ %å811ëÙ³g+ûöí ¢¢ •Ê™=úñúëŸÐ¹sw<=ý¤³O\‘JåÄ!ã2d<Ðt×ÄÄÃÄÆÆ°gÏVÞx㪪*qqñ GþtïÞÞ½K‡–ÐP«Õ:´[“‡ŽÙKuu.tëÍ‹/¾CçÎÝðöÓ ˆÒ´¨‹ýûšÞs))‰>¼—ýûcX½ú;/ž‡¡¡;w'2²Ý»÷£sçîòž»Å¤|“5440kÖ½ÔÔTóùçk122ÖvHZ·gÏV–.ý€Ÿ~ú’;7±{÷¶n] À{ïý‡ÂÂÆÆ&,Y²ž‘#ïãÀü÷¿ÏräÈ>Þyg)VV¶,_þ911hlläµ×f˜xàšž/$¤ “&=Abâ^~ù1œÝ™={>†,Zô:))‰|øaÓIÜùå¶o_wÅmV*•L:”Å‹ç1gÎtþõ¯·èܹÀ%ÛÔ­[ß«>¯±±éu¿ƒC>þx%UU•Ìšuïuw\µ&Ë–-fٲżÿþ„„tÑv8­‚äÉ;­Ñôé¯0tèž~zéég´ޏM’“øàƒW4(ˆAƒyÿý9ÔÔT3mÚˬYs€]»Òx祌;//)þ‰ëb``HçÎÝyüñøúë ÄÅ•°fÍ&Mz‚ŒŒ³¼ðÂÃDDØ3aBO¾þúÍ¢#âÎRUUÉ–-kyùåljŠraâÄÞ,[¶kk[^yå}Ö¯g×®4,ø–{Ÿ_°bÄM£P(ð÷aâÄGY°à[vïNg÷î Þ~ûÜÝ}øå—¯¹çž^DDØóôÓXµj©f]Ü\:ÿ`Ô;~üx¨¨àçE‹þþ‰¼½YøãB†NzÕûýþÓï̸g©©ms0þÅ xûíY±b· Ä…hŽÝÏøñQÌž=Ÿ‡šuÓwúôñ`RÁ¢Ÿÿ>?zëx³pá :ẟ'5õ#G†óðÃÏÊ*£B´55ÕŒ¹¹%ß¿í†X×›_~\¸ C¯¾ÿõÓï¿sÏŒ¤6¦þýóŸ&,Zôó5Ç|§9th7¿þú›6­!;;WW/ ÍÀ£ ë!‡Ð‰Û¦¼¼”íÛ×±qã*¶oÿƒÊÊr:vŒ`Р1Œy*•³¶CÔoov=>-,Ìã·ß~dóæ5ÄÆÆÐР¦K—žôí;ŒFÈBq¢U9uê$[¶üÊÖ­¿qðà.tuDFöaÀ€‘ vf²¹­¸ÞüÒ˜ú÷û_ã§O~þùÆ÷¿äà›èĉ£,Xð23g¾*Å?!ÚˆÐЮ̘ñÞ~ûE¢¢\v…¾Öª¾¾ŽgŸ½_ß fÌø¶ÃB\CÞ}÷;FŽà³Ïæóä“/j;$q“æ±råR~þùKRRŽãçÌ„ 0pà(;i;ï¼ó}úÜÍ„ зïP9mQ;PWWËÖ­¿±rå7lßþ†DGßͼy_ѧÏÝr(¸hµ¼¼üñòú>ú/Š‹ ؾý¶n]ËÛo¿ÈoÏŸžå›o62jÔýRümž±± £G?ÀÒ¥›ˆ‰9ÃԩϳÁƒƒxòÉ1ÄÅÅj;Ä6E €7ACC ¼Ì¨Qбc„¶ÃBÜ€ààpFŒ˜Ä;ï¼DCCƒ¶Ãù[‹ÏC©TÊáƒB´QzzúÌžý6¿ÿþ# ‡´ޏ{÷nc„žÜ”J=–/á§Ÿv2zôdl‹6ÉÅŃY³æ²}û)þïÿÞcÛ¶ßéÛׇ—_~œœœ m‡'.£i¢/cÇv#77“>ZÁÆÇyâ‰Ù88¸h;>‡¡RG2¡ ÷ ò¶§gàÀ1;¶—  ©£ßpìØ^Ú´±àÊ•3lØàÆ–-'¨Y³¾Ô±„\Tꘪª={æäÉ8:gÖ¬Q8:6ãÅ‹'RG+°d2Û¶9ÓºuîÝ»ÁßcÛ¶ÓÔ«×\êhÙ"—Ëñõ½CBB¼ÔQ~©VÔ¯o˶m§ù믣ܽ{Ö­«°}ûjd2™ÔÑò$јMïÞ½áÆKôê5Lê(ÙvéÒiÎËÛ=Ÿ>‘É’¸qãK—NÃÃãT†_çⲆž=co_/××ÓΜq£aC3üüfèùÙö¼²mRrtŽ—×Å<}ÉËáÃ;i×î§b&7Qw¾ïºsš¨;9¯C4449qbŸÔQ„4ÈdIÌ™ãÄèÑ4nÜšãÇïÓ²eç\]n?är9›7/gýú…ØÚV`ܸ¾’He¶îä´œ¨á™õißèë{›-[VHÖûNCC“‰røðMââbéÔɆ3gÜ$ÉR……}`Р¶,X0žnÝpüø=š5k'u¬l;|xÍ››Ó±cM""Â2ýzQ«R+ȵ  yóöœ<ù€ÇðÇãéÛ·>¼—,O^%³éôiW´´thР…ÔQ²eçÎu¼~ýŒ-:J%CîÝ»Áþý[X»vþ~£ã0"#Ñ˳>Æ[V×Ó45µ144F]]#CÏÏȶç•m“R£F­ÐÒÒáôé¼ù7((€;w®Ó¦½ÔQ²MÔï¿îœ&êNÎSSS§yóöœ>í*uá?6¬ û÷oaݺƒ,Y²=×O¼HñýXµj.ÏŸ?føðÉüùç"#ÃIJJÌ•u_\¾•Ùº“Ór¢†gÆçûF ‹šT©RE‹&åʺ¿¦råjìÛw…Î{3rd7öíÛ,iž‚äíÛWtíZ—W¯üpu½ÎÌ™+ÑÒÒ–:VŽèÔÉ;»îY~½¨U©‰Z•rÒbôèÙìßÿK~ú©‘˜Ðè?D`6]¼x’¦Mí$û¢ç„óçqõª;½{:J†Õ¬YŸþýÍô딕U(V¬¤$ëÎiµâðᛘ™•ÍÐó3²íyeÛ¤¤¦¦N“&m¸pá¸ÔQÒtéÒ)ÔÕ5hذ¥ÔQ²EÔÜYwNuçûhÙ²ÞÞ—‰ŠŠ:Šð™ àí}™;ÎѪUI2Hñýرc-¦¦e¨U«7ε߹áᡌÛ'Õ}™­;9-'jxF¥µo¬W¯9::º¸¸¬É• _£¢¢Êüùë5jS§'-rÁ‡ïéÝ;åïïêê……EM©#å8Ã,¿VÔªÔD­ú?KKk\]½ÐÐФW¯f|ü,u¤ãêêÂÀcøå—é\»vž=+¶ÏÉɨ¨HFšÁ‚›ð÷Á¼yc2µ uê4aøðÉXYÙ(î¯[·)5=zvºëzüø>nãùóGlÛæŒ]wŒŒŠóöí+ž>}À•+gS v›‘Üém{ZÒû<89õ¤gÏÁ 6‰•+÷—©÷*¯)]º<@ž<³ð:_×uGÔQwÒRªT9ò^Í)ˆŽû‡„„8Åw)¯éرÖÖÿoèÓÕ-̨Q3¨PÁ2Õó2[Sìíû3cÆ Z·îÊ‚›6lR†ÖgdT_ß;¼yó‚?ÿœÄÏ?ÅÅå,ÝcÉ’ÿ7¹ººàêêÂìÙ«™9s%¶¶™6m(W¯ºÓ¹soªT©N‘"EY°`C†Œ'880ͺ“^ÝÍhžÈÈpÂÂ>šîûžÝÞ­Û.ü›éÓ—Ó¿ÿ¯¼{÷†ZµÑ©“#ðõ}ã'ÖÖõ9yò@ž˜ÙRMM_~™ÎÑ£ÿdúD•1Û¶9Ó¾}O,-­¥Ž¢ð­Ïx“&mh×î'”””¨_ßuu æÎ]‹‰‰%J”¢nÝf”(QŠ%J1yòŸi®ãÖ-OæÍ[φ ‡hݺkšÏµêÛD­J­jÕZ´oß“mÛVI%O €Ù‘ò,\Ø@â$Y“œœÌýûÞ_œU `Ë–téÒeeeÚ¶íNpp îî_Ÿ­sûöÕ.\D1ÃÈ‘SèÕk(Ó§/ÏT¶¬,ëÇ÷,[6GÇᨨ¨ ¯oÈÈ‘S¹~ý¢â¬€ºº: þM“&vüüóXÆŒ™CPP{÷þ€’’UªTW<¿bE«,]šåè8}ý"©ÖíáqŠ]Ö¿µ®&Mì°±iˆL&£K—>88 áàÁë4hÐ"ÍY3’;½mÿ¯ô>II‰¼|ùŸ›@ÊૃËô{•—|úNgd'›ÛÂÃCómÍQwDÝuçk 6ÈÒ,ˆBÎóôt§~}[tu Kå«44´¾¸ï¿äTMÉÈúŒŒŠS­ZmÆŒ™CùòXXÔ ZµÚŠïéÇÁÌ™ó+ãÇ/ P¡”C^½†Ò¦=FF&i®·|ù*_ÔŒÔÝŒähÖ¬÷îE*l¿%;5\.—3pàÿ°gÏþ•¤¤$æÍ[‡’’ÒW÷Ÿ34,Fdd8~~¾éfÍ ­ZuF.OÆËËCê(?œ¨¨îܹNûö=¥Ž’Jz5ÅÁa‰‰ Šp**ªØÙuÃËËCñ›:>>™L†™Y¹TËNNNfñâ)X[7ÀÁaH–3ŠZ%jUZÚ·ÿ‰[·<‰ŽŽ”:ŠäT¤ŸEEE¢¤¤„¦fþœ‰)""”¤¤Ä/nÝò$))‘éÓ‡¥º¿gÏÁ¨«}ܱ  ·©zs”+W }}C2?óNV–uûö5bb¢)Q¢Tªûmm;píÚyºtI¬UGG/ÕsºvíÇâÅS¸?¥ÐîÜé¤ì¤ÚÉÝ»^Y7@KK›®]ûáⲆÐÐ ŠrìØ?̘±RñœôÖ¥¢¢Š²²Š¢WÚÿïÿò뛑Üémû¥÷yPQQ¥qã6Ì›7†'O|˜0a¡dƒ¥çmm]”””òÜ€ür¹œ˜˜(´´t¤Ž’e¢îüŸ¨;¢î|N[[—ÈÈp©c@pð;*W®&uŒlË©š’Q… ))ÑR¼¸)¯^ùàí}™äääTäeíÚß\îëNFënzy>QVVÎÐöe§†+))Q²di åòÁ Ž3tèD*V´¾¾oüœžž>rù^¥JU3”ù{ÒÖÖE_ß  ©£üpÞ¿‡\.WœÌ+Ò«)uë6Ã̬,®®.ІªGî!“%qâÄ>†ròäììº}±ìY³~ÁȨ8-ZtüîÛ!jUÁªUffåËåP®\%©ãHJ4fƒ¦¦r¹œøø¸|9 ÿ§bóßî¹Ïž=DSS› 6ejyÍšµãðá]xzž£AƒDD„MÓ¦v™Î–•e}ºt*<ücªû Š¢©©õÍ(ÅŠ•@CC“øøX å=Ù¸ñOîß÷¦'jÔ¨ËíÛ×2½CÙ²enn;èÖm… )§*š9¹®¬,ë¿Ûþ_ù<¬]{€©S‡°gÏ&Nreõê½Ô«×ƒ¡¡±Ô1R^ÂÂ>¦ÿÄ<.'¿ç9áÉ’’‘ËåÙ¸?;u7'e¥†ÇÄD3oÞJ”(ů¿ÎTÜÿµ}ãç>õDJNNÎéMÉ™,‰ÈÈð|}UB^õ©%¯]^Þg\II {ûþ¬^=àà@^½ò£zõ:(++ãæ¶CѸ|ùÎ/–­©©Åž=›èÚµ¯älŠZõcÕ*€ÐÐôõ‹HœDz¢0>çÈÈð|Ù¨«[uu/.9ÒÐÐ"0ПÀ@Š7MõØÇÁ)b”æò:wîM\\,ãÇ÷£{÷A½eåÊÝØØ4Ìt¶¬,ËÔ4å,Íë×i,on^ù›ëTRR¢bE+’““ùùçv+Îô|í2µŒ(_¾ µk7fß¾ÍhhhÒ¹soÅc9¹®ì,ëÓ¶§%#Ÿ–/ßIóæíY°`ØqôèÊ—¯’¥m‘ZddÊwâÓw‰ˆ+ðg¥óŠjÕê°cÇd2Y†{\ä59]Sr‚ŽŽññqøùù~1^aBBûP©RUär9‹MJuÿë×ÏØ±c­âÿÿÛªÿiL¦cÇî2vì<-Úœæ%Y9Ñe}ÎÚº>::z_̼èOll -[vúêkýý_’˜˜H»v?q÷®—.V V ˜˜˜­Ëf†þ;°þöT3 æäº²º¬Ï·=-é}âÙ½{#:9ràÀ5är9×®Ïô6äïÞùykÇõ‰¡a1E¾üHÔÿuGÔÏúS´h1©c@§N½øø1W×íRGù*bb¢RõÂˆŽŽRÔº¬~7¿öxzëˈOã\-]:=Õë||nrþü1 ¥çHttÔ7—“º›–¬öRÉl ÷óóåï¿—Ñ¢EÇTc…;wä«ûÆÏ…†† ££÷Eƒ„T6lX„MÃ|?1Y^¤¤¤DƒØ½{>¼—:ñšbjZ†ºu›±}û*444)V¬­[wEKK‡ß~ëM÷îÓ\¾ººK—ºðþý;Å ¿ß"jUÆôZÄîÝéÑcÔQòј ffåÐÖÖÅ×÷ŽÔQ²¬S'GnÝòLõ¥oÔ¨ÕªÕæðá]ŒÙ 7·ìر–iӆѷï/lÞ¼œZµŠâç÷Pñº áåu‘Ë—Ïpýúîß÷æÍ›©Ö—ÖëÒ’‘e}Ÿ-66HTuÒ¤EܼyOÏsŠçmÝꌽ}Å4eeeÂÃC‰‰Iy\.gõêy89ÍÂܼ²¢«÷ÁƒÛxüø>ûömæéÓ„„ñèÑ=BB‚¾XwzÚ¶íNáÂ4jÔJÑ-Èкbb¢HN–}q¦åýûw@Jo˜Œ.+½mOë}ÍÈça߾͊m±b%ÐÕ-œ§f-Ë,_ßÛèèè)ÎÜå%UªTçáÃü[s@ÔuGÔÔÂÂ>ð:Õ Ü‚tÌÌÊáè8Œ?þ˜@@Àk©ã¤ùݯT©*a¬_ÿ/^}ZàⲆ… '²sç:Ú´±Àظ¡¡!øøÜäúõ ÄÆÆ|Qw2ZwÓËpåÊYjÖ4àĉýßüäD Ÿ9óTUU™9ÓY±Ü¤¤DÅDWií?wë–'vvÝòD¯Ô½{ÿæÊ•³Lœ¸Pê(?¬Aƒ~COOŸñãûç‰K)3SSºwÈ›7/èßß H¹¼·]»¥jÕZ©–ûé;%“%aaQƒ1cæpâÄ~Ö­ûã›yD­J›¨U©Éd2Æï‡¾~ -uœxʉý'=:˱r’’çÏ#)))W,ý*UªÊþý[¨V­6&&f@ÊvÙÙu#(è-×®]ÀÃãšÌ™³FÑ;áþ}oîÞ½A·ný10( ¤ü@ݵk=GŽìæÀmìÙ³‰­[WrâÄ~Zµê‚ŽŽnš¯KKzËzöì!ÎÎsyõÊ÷˜˜˜Q¦LªV­……E ¶lYÁÝ»^ܾ}"EŠ2qâBEÁ«T©*ïÞùãêº7.áéyކ [âè˜2Ø|ñ⦄„qùòîÞ½N›6ö4h`˹sGx©iÖ­ûã‹u‹ŠŠ II‰88 E[ûÿ8¤·®ÈÈpöïßJll4ïß`jZ##®^ugãÆ?yûö!!A˜š–¥N&ß\V»v?aeeóÍm¿{×ë‹÷µlÙŠßü<Èd2ÜÆéÓ®½åСØÛ÷ËôY­¼dÏž*¤œ­3EÇïµDÚý”~}tžãLÛ¶=¨X1ý3eoß¾âèÑ2d|¶Æ%‘’¨;¢îˆº“š·÷ÜÜ\˜?³õ¥GÛ¶XVüöï¯OŸ²ÿÄ FÏNÿòñ}Ç!Qí«=8uë6ãèÑ=9²›víz ©ùåÌ’¹!­ïG™2°´¬ÉÓ§psÛÁ­[žôïïDpp eÊTÀÄÄŒ lÓýnªªª¥Z׃·Ø´i1OžøèO‘"F”(Q uut×èÏ_-!""Œ˜˜(ªW¯Ãɓٳg#ÑÑ)“æÕªÕ;»n„‡‡rëÖU®_¿@éÒæLžü'êê”(Q w÷£œ;w˜š5ëöዺSª”yºu×Óó\†ò¼{÷w÷#´lÙ‰rå¾~9^vk8ÀÖ­+)S¦<‘‘a\¾|†“'²dÉ4J—6§Y³viîp $Ý IDAT?‰‹‹å÷ßcÑ¢-ßÜåSŒß¡C'|µ7W~áì<'ÏŸªªªbcÓ•+gáïÿ[ÛŽ’þîKï3þyM)W®QQ‘©>%J”¦lÙŠ©&X:|x..« ûHLL4åÊU¢hÑâ8°…+WÎèO… _ŒÛ&j•¨U!“ɘ<ùg.\8ÎÖ­'“›ä–ÌÖ—Ù£ÓÿýµïøqPSã§Ÿ²þûKIžë‹zôèÑÑì[½:ý™›ãü3íjÿÍçÛ{ §žN<{öýf)ËI+VÌú÷,Ø›|{0~ÿ¾7«WÏcÆCÙZÎÙ³‡HLL¤aÖ|üÌÇÁ¼{çÏãÇ÷ËåŒ÷»$Ë„ÌËå4jTŠž=ãä4+ËË5ªhG³z_úõÑ\Égçhß>ýbþä‰mÛVeÿ~OÉIÎQwáÿæÎ§ç9NžôÉÐó3[_þqvæ§ößþýµ÷Ø1z:9ñLþ,ýõ÷ÑÚ¬^½/Cyó«wïÞЫWS45µÙ¸ñpª!á{øÚ¾qÙ²èêfÈñ%Kqôè&NHûö=Y´hsªÞåù‘¹¹Rž?>½xñ#FØÓ´i[/ÞúÅ ¯‚ …¼^«"#Ã7®/W®œeýz77në2[_äÏÒÿýÕcÔ(ÐÖN鈗Eù»jç­[w!(è-÷ï{K%˪V­E§NŽlÞ¼<ËËxôè3gþBÛ¶ÝÑÓÓ§L™ X[7 }ûŸí–þ“ó0QwáÿÜÝäÙšS™˜˜±{÷ETTTéڵǎí•:’ðƒKkßxñâ ’’%= Ž‰‰fîÜÑŒ㈣ãp.ü;ß7þåM›¶eûö3ܾ}•Îkåë±n…G^­Užžçèܹ>>7Ù±ãœ$y™¨ÜÙdaQ“2e*pàÀV©£dKûö=©PÁ’³ggéõÞ!(è-kÖÌçÁƒ[ÄÅÅòñc0îîGY±b?ý4X’e Bf8°õ‹Kò;»n:´“¤¤D©£d‹¨;‚W¯ºóæÍ Ú¶í.u! &&füóÏ%ììº1z´Æu!(è냟 Bv}¾o|øð.‘‘á’ŽµwùòÚµ«Ê¡C;X²d;Ó§/Ïc{$µj5âС›”/oAŸ>-7®ïWÇñ„Ü’×jUHHcÇö¡oß–T¬h…››w¾¾Zê{Q‘:À _¿Q,^<…ß~›‹¾¾¡Ôq²,;­ã:õæÕ«g¸¸¬aÙ²hiéP¡‚½{`úôå™:K˜“Ë„Ì ûˆ›› '.’:Ê7õíû ›6-áØ±½tîÜ[ê8Ù"êŽPÐýý÷2êÖm*&Éô´´™?=;:0eÊÚ´±`Ô¨8:GKK[êxÂèó}£TµáÕ+?V­š‹›Ûììº1kÖ*ŒŒŠK’EH™ìjÃ7Ξ=Ìœ9¿ÒºuFŒ˜‚£ãð +ßC^¨UŸÆð^¿~!::zlÜx8ßÎÏD`èÑc+VÌÂÅe ¿þ:Sê8’PVVf̘9Œ3‡ØØ444³<&bN.K2cûöU¨ªªÑ­[©£|S±b%±³ëƦM‹éرWmœuGÈïž<ñáâŬ]{@ê(BÔ­ÛŒãÇï±zõõeÿþ-RGÉDÝò£?þOåÊÕiÑ"Î^\ihh2~ü.^|AÏžƒY³æwš4)ËŠ³3( B~#—ËñòòÀÉ©'vv–Ü¿ïÍâÅ[9}ú¡hü˃´´´?~—.½bðàñüóÏ_4iR†éÓ‡sïÞ ©ã Âwu÷®Ó¦ £I“2ìÝû7C†Làòå׌¿@4þe€hÌ!?ÿ<–"Eвxñ©£Ÿ‰‰‰’:‚O,Z4 CCc#u” 17¯Œ£ã0–-›NTT„Ôq„ψº#dÄÙ³‡¸té4³f9Ø^¼ùY‘"FŒ¿—ôíû »v­§iÓ² Ø–'ö“˜˜ uDAHWpp 7þI«V•éÕ«)¯_?gÅŠ]œ8áCçνÅXy\áÂüòË4<<^2uêRnÜð k×:´icɆ ‹ z+uDAÈþ¬_¿6m,°·¯‹·÷%¦M[†‡ÇKFŽœŠžž¾Ôó ñ‹3‡¨«k0mÚ2ÜÜ\pw?*uœ†\.góæå¬_¿[Û Œ×™L–îëÚIß¾-±µ­ )³ïÌ764ÃÏïa¶–ãë{›-[V —Ës(YÁpöì!ÚÉôé+PW×:N†=›äädæÌq’:ÊEÔÌu'óBCC˜1c;÷¦V­FRDzA_¿cÆÌÁÓÓŸU«ö¢¤¤ÄèÑ4h`Êüù¿qãÆ¥ ÕAÈ-QQ=º‡áûҨQ)Ö¯_HãÆ­9rä6nn7hß¾§8)‘ÏhhhÒ§ÏHNòåÀkԫ׌ÿ¤qãÒ І={6ñþý;©c B¦°gÏFú÷oMãÆ¥Ù´i1õëÛrðàuNò¥wïhhhJ3ßÕ=µnÝ•.]ú2iÒ ‚ƒ¥ŽóCXµj.ÏŸ?føðÉüùç"#ÿ:óéç;¶Éd$%%åVÔlÑÔÔÆÐÐ8KOŸo·…EMªT©Î¢E“r2ÞíÇ÷L›6Œ=Ѳeþº O_ß%K¶áêºcÇþ‘:ÎCÔô‰º“=3gŽDI©3f¬”:ŠCTTT±³ëÆæÍǹxñýúÂÃã$M¨_¿Ó¦ åÂ…ã$$ÄKU(€BB‚سg#ƒµ£vmcÆëGll4þ¹…«W˜5k5¤Ž)ä€5ê2gή^ ÀÙù44´˜? ”¤K—Ú¬^=_ßÛRÇ„/Èår<¸ÅªUséÒ¥6 ¦œHÓÔÔfÕª½\½ÀìÙ«©^½ŽÔQó5јÃfÏ^…¶¶.¿ýÖû«ŒBÆíرSÓ2ÔªÕˆ§y°Êر}ÿ¯¬¬Œ‰‰inÅ̶FZqøðMÌÌÊfêuÿÝn€zõ𣣣‹‹ËšœŒøCJJJä×_{¢¥¥ÃŒ+¤Ž“%MšØáè8œéÓ‡óìÙ#©ãüDÝù6Qw²gË–œR¤ˆõê5cܸߩU«VV6RGò%%%ªV­EÕªµ=z6II‰ó篗:Ž(+++ú† ›D\\,·o_åÖ­«Ü½{]»Öãì<ee*U²¢FzXZÖ¤R¥ªT¬h•¯{­ ™èÏ“'>”.]‡!Šœ›7/W¬g„?xÍñãû˜4i‘â}JNNfÔ¨ìÚµž1cæ|sÛ ‹ŽŸŸ/•*UÍÎÛøCÙ¶Í™ Æ1eÊúô)uœU±¢Û·Ÿ¡oß Þ•uëŠ3jÿuGÔ©àéÓ}Zð×_GÐ×7”:–äDÝuG ×®gøð®XXÔdãÆÃ¢A^ÈSÓ2˜š–¡CÅ}AAøù¥|ùøÜäâÅ“lÚ´D1»°±± *XbfVŽR¥Ê)nË—·Ÿ¿< >>Ž×¯Ÿñô©/oÞ<çõëçŠ[ÿÈårTUÕþí1cCÓ¦m)_Þ‚êÕëP´h1©ã TáÂ4nÜšÆÿõAbb/_>U4ùøÜdÏžM¼}û’äädTTT111KU‡>Ý–-[QôbΣâ z›ª6}ºõóó%..øÿ¾¦víÆôî=œòå-°°¨––ŽÄ[ ¤G4æ‚¡C'R¤ˆS§%0ПßßP Ïм <ücªû Š¢©©EPPÀw[÷ç¯Ïž=DSS;ÍËØ¾eíÚL:„={6qê”+«Wï¥^½æŒM¤Ž% SÓ”†ˆ×¯Óž=Óܼr®äÐÐÐ"0ПÀ@Š7MõØÇÁ_IUEE…åËwÒ¼y{,Ç€v=z=âããðóóýwgø ñ9vðC¹ré¿Gaa) }ÀÞ  ~ù¥;Ožø°iÓaš4±“:R®07¯Ì¾}žüüs;ºt©ÍŠ»R¹-hDÝÉQw2N&“±dÉT6mZÌÀc˜2e‰¢aT¾uu ,,jbaQó‹Ç¢¢"xõÊ—/Ÿâïÿ’À@Þ¾}Å¥K§x÷ΟÐÐÅs544)V¬$††Æ)bDÑ¢ÆÃÐЈ"EŒ066¡H# Š¢«[¸@ŒáAdd8>¼'$$ˆƒùð!˜ààw|üÂÇÁ*ÿt¢DYYccEkýú¶”(aF©Ræ”)SSÓ2©zZ ÂBGG5êR£FÝ/‹‹‹U4 úû¿ø·1*_ßÛœ?”ÀÀ·$&&(ž¯¯oHÑ¢Æý·&ÃÀ (††)u¨hÑb)b„¾¾!ºº…ÑÒÒÎÍM•\LL‘‘„††ü[›Þóñc0?†Bpp ¡¡)ÿ yOXØÅkÕÔÔS5ºÖ«× 3J–,CéÒæ˜™• °?(ј‹š5kÇÁƒ×1žÎmX½z66 ¥Ž•묭룣£Ç™3nŠq¬ eãØØZ¶ì¤\²™Q… "::*S9*UªŠ\.gÑ¢I,_¾Sqÿë×ÏpsÛ“Ó¬/^“Ïž=›è×o:9bcÓ[Û \»vžjÕj°tétÖ®= 8àóñ¹ÉÛ·¯hÓÆ>SùÒòþý;>~ ¦mÛîÀ··;44½/ ’7.ñë¯?¡«[˜ƒ¯Q¾¼…Ô‘r•±± {÷^aêÔ!üüs;Ǝϰa“ dÏQw²NÔŒ aôè^x{_.½…¼OGGKKk,-­Ó|<..VÑC-å¿·ÿ@óúõsnß¾¦ø™L–êµêêèêFOOÿ?·èé飭­ƒªªÚÚº¨¨¨ ««O¡B…ÐÓÓGYY=TUÕ¾8hWUUCSóËyUUµTŸDD„~q_xx(2™Œ¨¨’’‰ŽŽ"!!ž¸¸ââb‰#::Š˜˜(""ˆŒ OuJddø½™544)RÄ#£âŠ‰Ê•«Q´h1ŒMþ=€.‘‘‰˜AþCCC“Š­¨XÑê«Ï $(è-o ôçãÇ`BCCøð!?¿‡Š­´j’²²2ºº…ÑÕÕÿ÷¶0ººzèêFGG--Åxt)·Jèé¥Ô%]ÝŸÝ*£«ûe7=½/Dzûô=ÿoH»6EFFœ,SÔ—O·a€\1ægxx(11QŠ“‘‘ŸnɈ%**"íW¡H‘¢U4V©R]Ñ€šÒóÛcãúDmA'sY… –:äÍ„ èÕ«ƒcìØyê, ¾¾!“&-bæÌ‘xzž£AƒlÝꌽ}êÕkÀ»wþÄÅŤ»Lcㄆ†àãs“èèHªU«Chè""ÂHLLP\rýáÃ{≡Q£VT«V›Ã‡wGëÖ]‰ŠŠàÔ©ƒ8;ïùêºöíÛLïÞ#PVV¦X±èêÆÒÒšš5ëÑ´i[Μq£OŸ´mÛ·o_þ‘?þø H9S“œ,#""L1V¤`ŠKC>IHˆçáûT©R€5kæcoߟêÕë|u»?Í"zë–'vvÝ äP™,‰¿þZʲe3hÒ¤ K—º¤z¿ MM-–/ßIݺM™5kžžçX¼x+ÅŠ•”:Z®uGÔïíÊ•³L˜0€B… ±{÷EÅû%yYÊþ•Óí-—Ë ŸêܧƒÑÈÈÔ hoÞ<'22œ˜˜(âã㉎ŽD&K""",S'Yrššš:ššZhhh¡¦¦ŽŽŽ.ZZ:ІsóÊŠ†ÌÂ… >k@ÐÇÐ0¥±OŒo%ß—‘QqŒŒŠcee“îsSC û@dd¸¢±ìóúADD¯‰ŽŽú¬Á-¹\þoÛôôôôQRRBOÏ@Ñ©­­£h¼411CG'¥1³paÅ¿utôÐ×7ÄÐÐH ã dˆh”€¶¶.kÖìçŸ61þo\»vžeËvP¦L©£åGÇá›°q㟜9㆞žFFÅ™4iÜbÓ¦%@Ê8J*XÒ¼yû¯ÎvÖ»÷ÜÝ2fŒ#ãÆýÎÉ“¸sç2Y Nä·ßæròä¼½/#—ËY²d*“'ÿÉ–-'™;׉˗ÏpãÆ%š6mË’%Û10(úÕì*** Üžzõšóúõs&Løƒš5ë°fÍ~þøc|2‡ß”´ñ//ÓÐÐdæÌ•lÞ|œ§OжmUvïÞ fm²¥ Õ°°Lœ8GÇf”-[‘S§äZ㟠‚ ‚ d…hÌTUÕ5j'Oú``P”nÝê1~|¿TÓp OttII‰™/çáûDF†3qâÂï˜,ï aîÜÑôèÑ]ÝÂ9r›_~™V ÆÔ̪&Mì8yòŒfÎ'ºv­Ã—¤Ž(–¯û IDAT%HLÔo“ɒسg#­[[àáq’Å‹·²aÃ!Ñ‹VAAÈóD`Rª”9Û¶féR®]»@‹Y·îâã㤎&ä²;×qùòd2Ó¦ ÅÛûr†^W¥Ju:tpøÎé¤Ëš5¿Ó¤IYΜqcéRvíºî æBjšŒ=›C‡¼)\Ø€^½šâäÔÿ—RG$ êη?Œ¶m«2gŽݺ àìÙÇtíÚOêX‚ ‚ ‚!b}FrîÜ–-ÛÁ‹O°·¯‹½}]ÜÜv/uDAȲ  –/ŸIãÆ¥™<ùg*T°äðá[lÙr‚zõšKOAArŒḩ Š2zôl®\yÃüùëyöìÝ»×§K—Úìܹް°RG„ ûÈŽkéÒ¥6Ý»7àÅ‹'üþû._~“Ó,ôõ ¥Ž(ÊÊÊtèàÀ‘#·Ø³ÇSÓ2Lš4ˆÆKó矓ñóó•:¢ dˆL–ÄùóÇprê©8Ùгç`<<^°bÅ.,,jHQAArœ˜8ŸSW× {÷tï>[·<Ù¹s N`þüßhÞ¼=ööýhÚ´-ªªjRG…ÄÄ.\8ÎÁƒÛ9þªªª´icϬYÎbœ­| víÆÔ®Ý˜  vïÞÀÁƒÛذaVV6tíÚ—Ž{ahh,uLAHÅ×÷6nçÈ‘Ý|øðžZµ±páß´oÿjjêRÇAA„ïJ4þ@¬­`mÝ€¸¸XÜÝâ꺑#»£££KÛ¶=èÚµ/66)³ ‚ž>}€«« û÷o!44„š5ë3{ö*:vì…¶¶®Ôñ„L*V¬cÆÌÁÉi·nyâêêÂòå3Y°`õê5§k×¾ØÙuGSSKê¨BÀ‰û8p`¾¾·)Q¢ݺ  gÏÁ”.]^êx‚ ‚ ‚kDàHCC“vízЮ]Þ½{ƒ›ÛÜÜ\سg#ææ•éܹ7-[v¦R¥ªRG €Gîqöì!ÚÉóç)_Þ‚Aƒ~£sçÞ˜˜˜IOÈ… ¢V­FÔªÕˆéÓ—s挮®.Lš4ˆ¹sGÓ®ÝOØÙu£nݦ¢§•ðÝ}øðw÷£=º‡«WÝÑÑÑ£}ûžÌ™³këRÇAAIˆÀœ‰‰#FLaĈ)Ü»wWW¶o_Ͳe305-ƒ­mGZ´èHݺMÅeÂBŽHLLàÚµ ¸»áܹ#¼}û #£â´k÷Ë–í jÕZRG¾#MM-:ur¤S'GÞ¿Çáû8th'{ölD[[—&MÚТEGš5k‡AQ©ã ?ˆÇïãî~”sçs÷®ªªj4nÜgç°µí žAA„O4 ÕªÕ¦ZµÚ̘±_ßÛœ;ww÷£¸¸¬FCC“úõmiÑ¢#-ZtÂȨ¸Ôq…|$,ì#žžçpw?ÂÙ³‡‰Œ Ç̬-[v¦]»X[7 P!1çPAcllÂàÁã"“ɸ}û*îîG9sÆçÏc`P”fÍÚ2hÐXš4iƒŽŽžÔ1AA!Ï €P¡B…°²²ÁÊʆѣgãïÿRÑsböì_™9s$ÖÖ hܸ5uê4¥zõ:¢÷„J||wïzáåu‘K—NsûöU”•U¨[·)ãÆýŽ­mJ–,-uL!)Q¢CqpJtt$—.æÜ¹#¸¸¬ÁÙy¥J™Ó¬Y;êÕkFíÚ)RÄHêÈBãçç‹——W¯ºãáqЍ¨*UªŠ]7lm;R½zq¢AAA¾B4 ˜š–¡_¿Qôë7Ѝ¨<*XbeeCÕª)uÇܼЏ4HJJäÅ‹'Šzããs_ßÛÄÆÆ ­­Ku:t"66 ©Q£.**ªRGAA„|O4 ™bll’ª‡àû÷ï¸{÷:>>·ðõ½Í?ÿlÂÙy%K–ÆÒÒ++k,,jbii±±‰”ñ ¼÷ïßñàÁ-<¸¥ø›½}û €bÅJbiY“¶m»ceeMµjuÄßKœ––6 ¶¤aÖ@Êåè÷îÝP|Ž==ϱcÇd2ºº…±°¨‰••5––ÖXZÖ¤lÙJ(++K¼WLL4ÞáÁƒÛŠ¿ÙÓ§¾$%%¢¥¥ƒ…E ¬¬lèÙs0U«Ö¢BK”””¤Ž-‚ ‚ ?Ñ(d‹±± ­Zu¡U«.ŠûÂÃCyúô¢GÙáûY¾|&r¹œÂ… 03+G©Rå·åË[P¹r5qqIHˆçÕ+?ž>õåÍ›ç¼~ýœ7ožóôéþÇÞ}ÇUY·ÿÀaãaÉÆÁÅ\¸GŠú˜ZéÃcš£l˜–IŽÌžÒ|ÊÌl¨©¿L3¬Ô2g˜fÑ{k9p‚¢Âaï}8¿?ˆ; Pô0®÷ëÅ‹sîsëûåð…û:×ý½“’”ŠÍÐÐQ´hDË–íeŽ5Q+XZZѱcw:vì®,»±¢ìÏ?±jÕb ò155ÃÕµ‘2Öøû*cO£F^’lªZm ×®]VÆšË—ËÆ›¨¨H®]»„V«¥Azèa:tèÎ /L’ŠM!„B!0IŠjgkkO»v]h×®‹²,++ƒ3gŽsŽØØ ÄÆ^à—_ÖsåJZm ..îxzúãå凧§M›úâä䆻{c]dÞ§¿iµ%¤¤h¸v-ž¤¤k\¾C\\”Ò¯åI>•Ê„F<ñôôÃß¿}ú ÂÇ'€ÀÀ¶ØØØ¸BTSüüñó dРg(..âÂ…Ó\¸p𨨠ÄÅEqäÈÖ® '//(›óÔÓ³l¼ñòò§iS_<<šâä䊫k#¹ÙÑuÒÓSHJJ !!žk×.Åŋ牋‹">>–’’bœœ\ñòòÇÓÓöí»âååO@@+™>@!„B“ŒŠx llìx䑞<òHO½å%%ÅÄÇÇ*É«òõ]»~!1ñ :ccc]pqñÀÙÙ W×F8;»áæÖX9YoØÐ;LMÍ ÑÄ{V\\DVV©©I$&^!))Aï»Fs¤¤k¤¤h(--ÀÈÈ7·ÆxzúáãÓŒ'ðòòÇË˟ƽdî,Qo™ššý}pÛ›^Óh®þ4".î±±Qlݺ–K—b(..RÖ³±±ÃÅÅwœÝqsk„““›òÝÕÕµÚ+«²iÕF§Ó‘••Azz Éɉ$$Ä+ß“’Ðh®þýu¢¢Be;[[{%q:pàå±§§ŸTr !„BQCIP”‰‰©’°ºQqq‘^L£¹JbâU’“ˆŽŽäÀ$$Ä“ŸŸ§·……%66vØØØ¡VÛ¢V—}/_fkk¯<76.›ÌÆÆN¹Ðʪ¦¦¦Ê¾ÌÍ-”X¥Ò¥°°€‚‚ü¿c-VªŠÊOªJKµdeeÜô•Ivö?³²2”}•³´´RœnnñöÀÕÕCIH¸º6ÂÉɵÖ&<…0”²ß!:v|To¹N§#%ECrr‰‰WIJº†FsMƒÎœ9Nrr"iiÉzÛ©T&·cnünnn ”ý~›™™`ff®T«P«ÿI¢YXXQPP6ÆiµZrr²”ײ²2”Iòòr(..›ÊƗ̿ǗŒëßüýz&&¦8:º(ãNóæmèÑ£?..¸ºz(Ùµ5á)„B!D}& @Qc™ššááѦw\/;;æ*©©Iʉíõ‰¶òÇqqQ×½–Nvv¦RIw¿ÿ}òo§$llìpvvÃ×·™^b üqÆθ¸”U !###œœ\•ä×í¢Ñ\#99á–cMyâ-!!žóçOé½v}%Ýýbm­ÖSÊ»»7QÆ£òå¶¶öØÛ7Äѱ¬Ý2'¢B!„u“$E­WVåg‹¯oó»Ú¾´´T¯&;;®,1˜——«\X~â^^µcjj†••5FFÆz ;µÚV&·¢Ž233§qc¯{š×.77›’’²ùO ò)** ¨¨ˆüü\ llÊÏÏÅÚZ­lgkk¯<¶¶V+s£^_­,„B!„7’ ¨÷ŒõNª¯,„÷Ãí’zB!„Bq?H‰’B!„B!„u˜$…B!„B!„¨Ã$(„B!„B!D& @!„B!„B!ê0I !„B!„BQ‡IP!„B!„Bˆ:L€B!„B!„BÔa&†àvV¯^jè„â®ÅÇÇÒ8À¹Òëïß¿ììŒû‘¢®¨êø"„B!DKš™™¡¶Uóáœ)†E!î‰PÓJ­§¶U³åçÕlùyõ}ŽHQWTv|B!„j`°÷ÀÞü™ñ§¡ÃBˆFÆKNLfÿöý„„„:!„¢^:uê¶ö¶†C!„Bƒ‘à2fÌC‡ „BÔKsçÎ5tB!„B”Ì(„B!„B!D& @!„B!„B!ê0I !„B!„BQ‡IP!„B!„Bˆ:L€B!îJAAA­Þ¿B!„BÔr`!„Áét:~þùgŠ‹‹Ù±c~~~Œ7ccÃ|FqäÈÂÃÙ6m‰¡&Û³güññññ,]º´Öí_!„B!ê©BžžnÐý®[·Žk×®1pà@^yåòòòÐjµ÷%¦[¹1Nssslll055}`1Ô&ÁÁÁ”––Þ·ŸÑýÞ¿B!„BÔ7’¢žËÍÍeáÂ…Ýïo¿ý†““L:õ%ßngË–-™3gÎÎÎ$†ÚÆØØ‡Z»!„B!„¨o$(D=VXXÈüùóÑh4Ûoqq1™™™Uk •q¿Ú/„B!„BÔ$2 5؉'8vì*•ŠèèhzöìIHHˆòú¡C‡8}ú4fffÄÇÇãííMhh(¦¦¦ÄÅűgÏ:ħŸ~ÊòåË9räÎÎÎL˜0>Ì•+WÈÍÍeÉ’%¸»»óøã£Ó騶mqqqÄÆÆbeeÅ /¼€››—.]bùòåDFFHXXû÷ïç»ï¾cðàÁ<þøã·ÝïvíÚÅÉ“'8pà‰‰‰¸ººâèèÈÒ¥K±²²bñâÅäåå±uëVÖ¬Yƒ¯¯/³fͪTû*êÇ[ÅÙ³gO:ľ}ûèÛ·/íÛ·¯¶þ¾•Ên—™™ÉêÕ«qtt$%%…¬¬,ÆŽ‹Z­&>>ž½{÷rðàAf̘ÁÎ;Ù¾};¼ð øûûóÝwßqôèQJJJ;v,­ZµRb¸ÓÏ»"|ùå—DFFâììLXX˜2oâb.oûÏ?ÿŒ‡‡çÏŸ§°°éÓ§WzÿB!„B!*G*…¨¡vïÞÍîÝ»yá…xî¹çh×®K–,áôéÓlÙ²…Í›73jÔ(FŽÉøñã9pà³fÍB§ÓaggG\\III¬\¹’0}út._¾ÌêÕ«èÚµ+žžž¨ÕjÆŒ£$é"""033côèѼÿþû¤¥¥1cÆ iÚ´)¯¿þ:ööö¤¥¥akkKFFcÇŽeàÀ¨TªÛî÷FÝ»wgÔ¨QtèÐ1cÆðä“OŒ¿¿¿²ž••¡¡¡4nÜXYV™öUÔ·Š3##ƒøøxN:Eii©²Ÿêèï[©ìvóçÏ'??ŸÐÐPÆŒCRR_ý5¶¶¶¤¦¦’ÀºuëhÛ¶-Ÿ~ú)–––|ñÅ|ûí·ôêÕ‹?þGGG–-[¦Ã~ÞwRTTDDDÇçÝwßåÚµk¬\¹²R1Ì›7ž|òI&L˜@qqq•ö/„B!„¢r$(D ”••Exx8O?ý´ril¯^½èر#öööJeUïÞ½Q©T¨Õj Ddd${÷îÅÎÎ___žzê)5j„§§'>>>\¼xñ¶ÇNOOgË–-tëÖ (›­S§NdddpìØ14hÀ3Ïùä.[zo*j_Eýx;×ï **ê¾õweÚ0cÆ  lÎÄ={öN§»i×Ï¥X~#ë÷[Þ†ììllll*ýó¾•J¥·o.\¸P©˜U*­Zµâ믿æòåË >ü¦ÄkEûB!„BQ9’¢Ч¤¤NwË›c$''““£·\­VcnnNzzú]ûêÕ«˜››3f̘ ×}ôÑG9sæ ;vìP.å­I*êÇʺŸý]Y¥¥¥DDDpñâE{ì1|}}‰ŠŠºã6·jsù²òË›«òó®ÈÇ«(æÉ“'³dɶoßÎáÇ™4iUjB!„BˆŠÉ%ÀBÔ@–––såÊ•›^+..V*Únw÷Ú{¹I‚™™iii¤¦¦ÞôZùe£Pvݽ{÷ÒµkW~ùå.]ºt×Ǽ_*êÇʺŸý]:ŽÙ³gsåÊ&OžLóæÍ«mß•ýyWUebV©T„……†J¥bÖ¬Y\½zõ®)„B!„âÖ$(D äããÀêÕ«õ.ó¼xñ"ÇÇßßKKKŽ9¢·]jj*………´k×®ÒÇ222¢  @yÞ¤It:«V­Ò[O£Ñð믿*ÏW¯^Íã?Î3Ï<ƒ¥¥%Ë–-Ó‹õÆýV•J¥¢  @ïFzǨHEýXÙ8«³¿ïFtt4ýõ—^uœV«­R_ÜNeÞUUQÌÅÅÅlÛ¶ €.]º(7S)¿ÉB!„Bˆê#— Q=ôÐC´iÓ†#GŽðÞ{ïÑ©S'’““ÉÉÉaìØ±Œ1‚eË–qêÔ)~øa¶nÝJ÷îÝ•¤K^^P–x)—™™©wwW{{{²³³¹xñ"ùùùàããÃÞ½{)..¦C‡äååqøða&L˜”͉—ššJË–-2dááálÛ¶Þ½{ßr¿¾¾¾z7ñ(W^yvãg›4iÂÁƒÙ¸q#;wfÿþý”””ššJll,^^^¶¯2ýx«8Ë/é-¯€S«ÕÕÖß·RÙívíÚ…¯¯/111ÄÇÇ“™™É¥K—°³³#??@/aZ¾,;;û¦e%%%´lٲŸ÷­äää——GII &&&JÌ%%%zqß.fkkkvîÜIŸ>}066ÆÁÁ+++¼½½+µÿ[½—„B!„BÜšT QCMš4‰>}úÀºuë(((à™gžQ^ïÝ»7¯¿þ:›6m"<<œ~ø;;;^yåN:ÅÑ£GX³f YYYìÞ½›èèhòóóY»v-¥¥¥ôéÓ‡† òÙgŸ‘¹¹9o¿ý6]»våܹs|óÍ7DGGóꫯ¢V«9}ú4sçÎÅÑÑQ©æ²¶¶`ùòåJÕØ­ö{£ØØX"""Ø·o{÷îU’aýû÷'((ˆˆˆ-ZDëÖ­y衇 "--­Òí«¨oŒ3**Š­[·°cÇNžþèt:²³õÇ™’’²ñ¨¨¨@oœ±¶þgœQ«í”ñÈÊÊSS3lmí•ñÆÆÆ•JÒ2âÁwšB!„¢Zegg¢Ñ\E£¹FRÒ54škdd¤‘¡œp_â••AqqQ…ûU©LhÐ@­23Ó•±&>þâßÖeãNdäŸ$%]#99N€³³~~zcŸ_s¼½P©TÕÕ4Q‹HP!„Bˆzª¨¨óçOqæÌq"#ÿ$6öqqQ$$Ä£ÓéP©T¸»7ÅËËŸfôî=OO?<=ýpwo\oç®233WªüZ´ºíziiÉÄÅEqñâyâ⢈‹‹b×®­ÄÅE)—$ÛÚÚãåå——?þþ- lK``ìì>¨æñÀèt:._ŽáÌ™œ9sœ˜˜sÄÆ^àòåeN='åwbР‘xzúáååOÓ¦¾XXX¸†ckk­­=~~·]'??K—¢‰‹‹RÆó Nóë¯HOOÊÆ¯&M|ðö~Ÿš7oC‹miÜØ[*•ë¸úù[!„Bˆz&77›³gÿâÌ™ãœ9s‚ÈÈDEERRRŒµµš€€–øú6£k×>ÊÉ·\Fvoœppp¢mÛÎzËu: ñÊ zlì.^<Ï×_/@£¹ €‡GSåļyó6¶ÁÅÅÃÍâ®hµ%ÄÄœS’}‘‘'ˆŒü“ììLT*||ðó ä_ÿú7ÞÞáåå§§Ÿ^…®¨KK+ZÐò¦×23Ó‰½ |ÅÅE±mÛ&–.ý­V‹Zm«Œ5åcT Ö-’B!„¢ŽÑjK8}ú8GŽìáÔ©£DFž ..ŠÒÒRìì lK—.};öMš7oƒ§§_ºL·®322Âݽ îîMî¥÷ZJІÈÈœ>]–0Y·îkâãßÀÑÑE91oÛ¶3íÛw­ö¹Ê„¸[ññ±9²›'y‚sçNRP™™9þþ-hÑ¢-ýû!0°--ëu5Ÿ!ØÚÚÓºuGZ·î¨·íÚu!0°­ÌéTØØØÑ©S:uê¡,KMMâÏ?qìØ>öíÛÆ×_Fii){Ü‹ví‚y䑞¸º62`ä¢.‰¿¨Œ9‡íâÚµËXZZѼyžxbØßï».Õz×na&&¦4o^v)ðàÁÏ7ÿÝ™5k’Þß  `ù»SÃIP!„BˆZ 1ñ ;vlfûöŸ8p`……J%ÆÛoÏ•JŒz¦aCgBB'$äq22R9zt/þÁáûY»ö+JKKiÞ¼ ={ $äqZ´’sQiùùyìÛ÷;;vlfÇŽÍ$''bm­&((˜áÃ_¦C‡n´lÙ^ªûê •Ê„-‚hÑ"ˆ1c¦*•ç‡íâÈ‘Ý|úé4òòrpvv£gÏôè1€àà^XZZ:tñ7I !„BQét:Μ9Îöí?±}ûODFžÀÒÒŠ.]úð¿ÿ-¢s癋I(ììҫדôêõ$ÙÙ™9²‡?þø™uë–³pá{¸¸¸Ó£GY2°s癃MÜD£¹ª÷ACQQ!?ÜŽ‘#_¥K—Þ´hѶÞÞý[è355#((˜  `à-eîÙ½{cûöŸX³æ+ÌÌÌéÜ9„ÇéÑc..^“ß\!„B!jˆ‚‚|öïßÎöí?±sçf4šk¸¹5¦GþLžü>:õËëD¥¨Õ¶ôì9€ž=ðÞ{ŸyBI&ÿð×XXXÜ‹ž=§gÏ89¹:da ×ÐpæÌq,-­îÍŒ éÙsŽŽ.†QÔ*• ­Zu U«Œ7ääÄ¿«Gâý÷'2mÚXZ´R*—›7ocèëI !„Ba`§OcÆo‰ˆXEFF*¾¾Í8p$={ ((X.Û÷¬|>¯ñãß!--™]»¶²cÇff͚ĴichÓæþýïgxâ‰aXY50t¸â>KL¼BDÄ*Ö® '6ö :Ó­Û¿3f*>ú˜¼Ä=srreÈ2äE 8zt/Û·ÿÄêÕ_2þ ¼½bÀ€¡üûßÏи±·¡Ã­$(„B!„\ºÍÆ+øñÇÄÇÇÒ¼ykÆ{›þý‡ÈeRâ¾rppbРg4è òùãŸÙ¸ñ[fÌx•>x¾}Cù÷¿Ÿ¡C‡î:\QMrr²øå—õlØð ‡ïÆÁÁ‰ÇšyóVÉüâ¾27· 8¸ÁÁ½˜>}>'O!"b+W~΢E3iß¾¡¡ÏÒ·o(ÖÖjC‡[gIPQëiµZ nûzqq1………wÜGaa!%%%wu|kkë;¾nbb‚…Åí/×211ÁÜÜü®Ž-„¢vÉÎÎdÛ¶6n\ÁþýÛqrrå±Çú,m ž¨‡,,,éÛ7”¾}CÉÌLgëÖµlØð-Ç÷ÄÅѾ}C<øyš5keèPÅ](--åøñýlܸ‚M›¾£¤¤˜.]z³`Áôé3Pnà!8###åRá·ßžËÁƒ;ÿþbÓ§¿LHÈã 8’G}L曬fÒ›Bˆ»VTTDqq1yyy”””ŸŸ_á²ÒÒRrssýäÝõ‰ºòmòóóõ¶Óétèt:òòòt“ï;sssLLʆf 展•FFFaeUv'-•J…¥eÙäÝ×'¯ß‡••&&&XZZbjjŠ™™Ùm—©T*eßB!ª×ŸbÅŠElݺ###z÷HxøÏ÷F¥R:˜Ì!£6l¬\ª^;¶o¿]Ä/¿¬ÇÆÆŽ!C^ä?ÿyOO?C‡&ÄÙÚÚ3lØX† K\\k׆³bÅÿñùçзo(Ï<ó*mÛv6t˜µš$…¨%JKKÉÎÎ&''‡œœ²³³•ç×'ôòóóÉÍÍÕKìåååÝ6IU^ vcâÆÄÄGGGåòUsssLMM•×,,,033ÓK •'{ÌÌÌô’}¢f¹UuæõIßòÄb~~>Z­VI"*ÉEFsÓûîv—a—WÞøu}Ò°üK­V£V«iРòØÔT.MBÔ.¹¹Ù¬]ÎÒ¥“––LïÞO2eÊl‚ƒ{:4!ªÌØØX©ÐINNdÆoøæ›…|ñŇ<þøPÆŽ}_ßæ†³^+--eçÎ-,^<›'ТEï¾»ˆGbaaièð„¨2OO?¦L™Ík¯ýmÛ6±|ù|&0°-Ï=÷O<1\Î5ï‚$…0€ÂÂB½$Þõɼ[}ÏÎÎV*À®gnnŽZ­¾© ËÅÅE¯:ëú׬­­õ^“äJýS^-icS½Õ'·«2½þqnn®^õiJJŠÞk9997í×ÜÜ\/!x}’ðúååÏmll*¬øBˆû!99‘¯¾šË÷ß/ÁÈȈaÃÆ2jÔk8;»:4!ª…““+cÆLå¹ç&±Š/¿ü˜Ç{˜ž=çå—ÿKëÖ b½RRRÌÆ+øòˉ½@HȬ[·Ÿ6m1thBT 33súõL¿~ƒ9vlK—~Äo<Ç_|ÈèÑS8p„ÌcY’¢š‘‘‘Azz:YYY¤¥¥‘™™Izz:Ê㬬¬›.Ã422Ò«zjР¶¶¶xxxÜ”ð¸þ±$ïDMbllŒµµõ=%ßt:]…Éðììl®\¹¢—8¿ñwÊØØµZ­­-öööØÙÙ)mllpppPžK²Pq¯ÒÓSX²ä#V®ü?Ôj[Æ{›aÃÆÊe¾¢Î233gðàç Åöí›X²d¡¡èÑ£?'¾'7´¹Ï´Z-›6­â³ÏÞ%1ñ Ž`ñâøø:4!î›  `–,‰ ::’¥K?fÚ´±|þùŒÿO<1L*+A€BT 33“ŒŒ ÒÒÒô{7&ûnœO­Vcgg§|¹¹¹áààpÛ &!DY2ÜÆÆ¦ÊÕ‰………7UÔfeeéýÎÆÇÇ+¿³ås$BÙœ‡åÉÀ[% mmmqppÀÞÞ^þ±BèÉÍÍféÒY¾|>–Lž<‹aÃÆbn^õ9e…¨ŒéÝ{ ½{d÷î_™?ÿž|²}ú bòäY’ªf:Ž­[×1þ;ÄÅEú,¯¾:¦†MˆÆ×·9}´œ°°,\øS§>Ï_ÌfâÄ™ôíjèðj4IŠz­¸¸˜´´4ÒÓÓIOO')) F£ûìŒÿAAÁ¼ýö\~¸¡Ã«‘$(ê,NGZZ)))ÊWjj*ÉÉɤ¦¦’ššJvv¶²¾¹¹9NNN4lØGGG|||”çööö888`i)“è Q˜™™áââ‚‹‹Ë×+..&33“ÔÔԛƓ‹/räÈ233õöëä䄃ƒŽŽŽz_8;;Ë¥ûBÔrGŽìá½÷¸páŒ$7„¸‘‘½z=AÏž”$ù¦M« ›Áˆ㤒þ.$''2gÎT""VÌÆ‡iÑ"ÈÐa Qcxyù3þw<ÿüDÞ"ÿþwGÉÔ©spt¼óÿúõ$E­—žžÎ•+WÐh4h4¥ŠïêÕ«zw¾-¯Üqqq¡Y³fÊÉxy5Tî!ô˜šš* ¼Û¹S%ñ¥K—HLL$//OYÿú±¨Q£F4jÔÜÜÜäC!j°ŒŒ4>úh*kÖ|E×®ÿbÞ¼ïðõmfè°ªÍï¿ÿÈÿþ7žo¾ù­Ö¶K«-áøñìÞý íÛw£[·:¤zËØØ˜!C^dÀ€!,^<›?|ƒ ¾åƒ–Êü€•¤Óéøá‡/ùè£7Q«mY°à{ì?†«ÚȘ#ª[Ë–íùá‡=üüóZæÌyƒ>}š1uêG<õÔ ržÿ7IŠO«Õ’””DbbâM_IIIÊ\^ 4ÀÕÕWWWÚ¶mKÿþýqvvÆÉÉ ;;;Œ Ü!D]djjZa5avv6iiih4½1lçΤ¦¦*—ÛÛÛ+ãØ_’Âp6o^ÍÌ™066fÁ‚è×o°¡Cªv––Ö4lè|Wó&%%Ôˆ;Ÿ>ž+W®pùòeHIIQ’|666Êɰ¿¿?®®®¸¸¸àêêJƒ Ü!„¸µòÿ4mzó$ÝÅÅÅ7%9sæ )))”––`kk‹««+7¦qãÆ4jÔˆ&Mš`kkû ›#D½‘••Á;ï¼ÂæÍ«yúé1¼ñƇ¨Õuów®K—ÞtéÒ»ÊÛef¦3iÒV®Ü~¢ªš6mÁÜÜ‚uë–ßÓ~î¦M5©j"Ÿ¾ûîÖ® göì×ÙµëæÍ[Ióæm Z¢ÓéXµj1~8?¿@~üñÍ›·6tX÷…Œ9ÿ1§úYYYóÖ[Ÿ2pàþûßÑ<ñD[þûßOxúé1õºP€â+..æêÕ«J²¯ü+)) N‡©©©rY\@@€^Œ•••¡ÃBˆjuý˜w£’’½ èk×®qåÊ:¤ÌaªV«iÒ¤‰’lÔ¨7–E„¸G‡íâõן¡¤¤˜ðð­ri×-äççñÚkC‰¿hèP¦¦f÷´ýÝ´©&öCMdddÄSO½@×®}xýõgù÷¿;1qâ{Œ=E®ÔRS“xãçØ³ç7^~ù¿„…½ƒJ%§ë׫‰¿k2æÔlÍ›·aýú,Xð.ÿûß«ìܹ…9sÂqpp2th!#Џot:‰‰‰ÄÅÅ)ɾ˗/£ÑhÐjµ¨T*ÜÝÝiÔ¨>ú¨ròêââ"ÿ!`bb‚»»;îîî7½–‘‘¡÷!Jll,{öìQæ´··×«ôôô¤I“&r!* ÓéX¶ìS>þø¿ôì9€Ù³¿ÄÞ¾rw¯­23Óùõ× lÞ¼š‘#ÇÑ»÷@"#ÿdÓ¦UüòËz~þù3g¾Æo¿ýH“&Þ,X°šÆ½ùí·DGŸ%33·Þ—×CŒý::Žï¿_ÂÙ³qæÌqÔj[Þ}÷ÿðôôC£¹ÊÆ+øñÇ•¬^½› ž&&æs愳{÷/w<@JІ¹s§áîÞ„k×.“––‡.«òX"#ÿäë¯?ÃÇ'€ãÇ÷“ŸŸÇ·ßþ~Û6Ýé¸wÓwŠ¡®sskÌŠÛøê«OùôÓi=º—O?]¡C3˜'2nÜ033gõê]´mÛÙÐ!ÝW2æÈ˜ó ™˜˜2iÒûtïþ“&à‰'‚ø¿ÿ[G«V Ú' @QmÒÓÓ¹xñ¢òuáÂ¥BÅÞÞoooÚ·o¯T§4jÔ3³{ûÄD!ê+;;;ìììxøá‡õ–—ß©|:…èèh¶oßNAA*• 777¼½½•/___LLäß!òòr˜:õy~ýu#S¦ÌæÅ'׋K…’“‰Š:þ}Û6l,NN®DFþI||,}4•^˜ÄÈ‘¯Ú‰O>y›Ï>ûž'ŸÎæÍ«¹pá4|𥲿%KæàììÆÌ™‹Ñjµôë×’¡C»±sg çÏŸbÆoˆ‹‹â›oзïX³f¶¶ö ,l(ŽŽ.¼úêt hÍÌ™øôÓUjsXØæÌ '((˜‚‚|ž}¶ÀmÛt§ãÞM?XZZÝ6†úÀØØ˜Ñ£§Ì«¯fàÀö,^¼‡z¸âë˜ï¾û‚÷Þ{àà^Ì»[[{C‡tßɘ#cŽ!qŒ‰‡3th7fÌXÈС£ Ö%ÿñ‹»’““CLL ÑÑÑÄÄÄCzz:FFF¸»»ãëëËàÁƒñññÁÓÓS*N„â±··ÇÞÞ^/1¨ÓéHHHPÆë˜˜dÈ‹˜›[þ},ST*š6õ­òñV­Ú@aa«øë¯ÃÊÔ+ËÄÄ”®]ÿÅÌ™¸pá4S¦|HïÞï¸MU[Q?ÜM u•““++VlcêÔçyá…þÌ™ΓO7tX÷Uqqo¼ñ[·®cÞ¼U<þøÓ†é“1GÆC166fÒ¤™4oÞšI“FËܹ+ïêŽÔµ$ÅMŠ‹‹‰‰‰áìÙ³œ={–óçÏ“ŸŸZ­Æßߟnݺáëë‹666†W!Ä]P©TxzzâééIHHEEEÄÅÅ)U‚û÷ïgýúõʺ4oÞœ€€Ôjµ[ DõˆŽŽdøðž¸»7᫯¶ÔÛ‰Á«KLÌY,-­õ.K«NZ­–¥K?âÔ©£<ûl­[wäĉƒUÞÏ矯筷F³zõ—üúëF-ZC§N=ªí¸•釪ÆP—™ššñé§+pvvcòä‘äååðôÓc*Þ°***dìØA;¶ððŸéÜ9ÄÐ!Õj2攑1§êúö E­¶å•WByå•P/Þ€™™¹¡Ãº¯$(Ðh4œ:uŠS§Nñ矒ŸŸÍš5cÈ!àååUgJcóó󥚥š<辬ë?»ݾ‚‚,,êþ§]B]xoš™™áïï¿¿¿²,33“èèhÎ;Ç©S§øùçŸÑét¸¸¸ððÃDË–-eªQ+]¼xž‘#{áééÇòå[±²’»gß+ +¯˜xWWý»›§¥%ßS‚µ´´”^èGÆÎ|þùzÖ¬YvWû211aÞ¼UôèÑŸ>˜Ì¨Q}Ù¼ùO|}›UËq+ÓU‰¡>022âÍ7?F­¶ãw^ÁÄÄ”ÁƒŸ7tXÕª¸¸ˆqãþÉX¹r;?ÜÎÐ!Õz2攑1çî÷bÅŠm<óLoÆŠÿû¿u˜˜ÔÝÿiëÇ$â&¹¹¹ìÛ·… òÒK/1~üx¾ÿþ{ŒŒŒxöÙgY´hK—.eâĉôë×ooï:‘üûõ×_yçwxûí·ï¸^JJ ¯¼ò {öì¹éµÜÜ\V¯^Íwß}Wéãêt:¶lÙÂ?þHXX .¤´´´ÊñW䯿þâØ±cÄÆÆ²eË–*—§WŽöåÞ½{yóÍ7yöÙgyë­·8qâDµ¯:Õåþܳg3gÎ$,,ì®cÖjµœ={–ï¿ÿž¿þúë¶Ëj«Ê¶å÷ßgÆŒLœ8ñ–¯?È÷Ñý`kkKPPÇçÃ?$<<œ©S§Ò¶m[Ξ=Ëœ9sxî¹ç˜5k›7o&11ÑÐ! Q)W¯^bĈ<<< ÿY’wÉØØ˜ÜÜåùC=ŒN§cΜ©zë]¾ÃÊ•ŸßÓ±þúë0{öüFÇŽ*ËŠ‹‹«<®òý÷Kxâ‰a¬_NÇÁƒ;›ÛT™ãVµ*Š¡>7îmÆ›Æ[ofóæÕ†§Ú”––2aÂ0ÞÍòå¿Hòï.ɘÃ-·‘1çîµlÙžåË·ràÀN&N~_ÎÓk ©¬G4 GåèÑ£œ;w€€€úõëGË–-ëT…ßíôêÕ‹ßÿ½ÂAÛÄÄ[[Û›*£Ž;Æîë‚Èí IDATÝ»9pà}ûö­ôq×­[GFF£G& €ˆˆ´ZmµNôûÛo¿ЧOÙÝœòòòXµj#FŒ¨¶c\ï^úrË–-œƒÈÉÉâ×_7°`AY2'//‡ÒR-YYØØØUúxåÿ§nØð ­ZuàäÉ#DE!%Eùs'qtt!'' €üüÜ;¶wíÚp†•J…‹‹;jµ-moÙ¦ò¿Íw:îÝôÃb¨ï&Lx—ÜÜlÞxã9<<‹/fÆŒ7½6`À^}õUZ·n]cª±¢¢¢>|¸^øûûãååU#.”þ¬š[ý>Tõw¤&»Û¶Ô¶÷Qu166& €€€†ÎÕ«W9xð û÷ïg÷îÝ4lØÎ;Ó½{wš4ibèpE=õÕWsÙµë¾ÿþ<<š:œá‘GzòÈ#=oZ¾kW¬ÞóAƒF2hÐH½e-Ù·/^o™s箼屆ÍС£oZÞ¹sH¥Ž7sæbfÎ\¬·ìèÑ*ˆ*3¡½™™97¾íë·jSEÇ­j?Tƒ(û›2oÞ*BCaâÄá|÷ݵò<&:ú,ï¼ó £GO¹éý\_ɘ£OÆœš#4tQQg˜>},­ZuÀÛû!C‡T­êÎYZ=§Óé8}ú4Û·oçÈ‘#˜˜˜Ð¡CÌÃ?\+ÿXÞ­C‡qúôiÌÌ̈ÇÛÛ›ÐÐЛ*îΜ9Ã?þHtt4¾¾¾¼øâ‹¸¸¸ Õj9yò${÷îÅÎÎŽ‘#ïþõ®]»8yò$ 11WWWž|òÉJÇZÑ:Û·oÇÔÔ”FÝt|sss¼½½Ù°a/¿ür•ã¿}y»KR­¬¬°²²ªT\·;”ݰ`õêÕ8::’’’BVVcÇŽUîXúÜsÏÑ¡CåXÇ'!!ñãÇÓµk×zÙŸ|ùå—DFFâììLXX@ÙØ²mÛ6âââˆÅÊÊŠ^x77·ûÖþÛù믿˜={6FFF¼þúë´lÙ’ððp¶oߎ››/½ô¤¤¤0oÞ<˜ûŒ«W¯2gÎœÛÆräÈŽ?Nƒ (,,$==]ïõûù>ªM<<< %44”øøxöïßÏÞ½{ùé§Ÿðóó£gÏžËhÄu†yó¦3a»´ióˆ¡ÃBTÀÚZÍÂ…?0p`{–,™Ã+¯¼e誤¤¤˜)SžÅǧ“'¿oèp„•0eÊlŽÙÄ ÃX¿þ¦¦f†©ÚÈ%Àµ\aa![·n%,,Œ™3g’ššÊ‹/¾ÈÒ¥K7n­[·®WÉ¿-[¶°yófFÅÈ‘#?~<`Ö¬Yz—žeee±{÷nú÷ïOhh(gΜáwÞ¡°°ÔÔTRSSÙ³g÷O÷îÝ5j:t`̘1Jò¯2±VfƒâççwÛüýý9tèP•'3}}YZZÊåË—éÚµâK"ît<€ùó瓟ŸOhh(cÆŒ!))‰¯¿þZÙþÑGåå—_æÙgŸ¥oß¾¤¤¤@—.]€úןEEEDDD0|øpÞ}÷]®]»ÆÊ•ÿ|r™™£Gæý÷ß'--3f(ý]Ýí¿“V­ZñÈ#`ddD‹-055åÅ_¤aÆ8::€££#ŽŽŽÊ<{ûòåËìÚµ‹«W¯²uëV:uê„%%%·ŒcïÞ½üøã<ÿüó >œÁƒ¯ÿ ìýzÕf7fÈ!,X°€3fàââBxx8cÆŒ!<<FcèE§Õj™8q8­Zu`ôè)†GQI~~Lšô>Ÿ}ö?¢£# N•,^<›èè³|öÙ÷uú΢BÔ%*• ü ±±çYºôcC‡S­$XKeffòÃ?ðòË/óÝwßѦMæÎËÌ™3éÑ£æææ†ñ+¯üêÝ»·’ôT«Õ 4ˆÈÈHöîÝ«¬kbbÂË/¿LëÖ­0`O=õéééìØ±gggBBBîkâ´2±VfNGLL ÜþÎ…vvväååqåÊ•j¯\uôå‘#GðôôäÑG­0¶;¯\Ó¦ÿ\ÒÕ¤I._.›7F§Óѯ_?åµððpJKK=z4FFFõ²?U*#FŒÀÝÝ&MšàëëËÅ‹HOOgË–-tëÖ (»§S§NdddpìØ±ûÒþŠôêÕ‹’’Ž9¢Äß©S'Ξ=KnnÙäËÅÅÅ”––âââR©c·nÝš‡zˆÒÒRºvíJHH|ðööö7¿°°+VЯ_?¥rQ­VÓ¬Y3eûõ>ª+ŒŒŒ $,,Œ%K–ðÔSOqìØ1^{í5æÏŸ¯¼ÿ„¨nkÖ,#**’Y³–Ö«G…¨ ž{nÍšµâÝwà J¥i4WYºô#ÆŸŽ§çí?BÔ<ÞÞñòËo±xñ$$ÄW¼A-! ÀZ¦°°ˆˆÂÂÂøý÷ßyì±ÇøüóÏyþùçoy©Y}Eaa!ŽŽŽzËËïTyúôieÙ—FvïÞ@9ñ422º¯w­¬L¬•Y'''­V{ÇDCy[333«5¾÷_®ª}™››Ë† ?~|¥æI¬èx3fÌ`РA³cÇ¢££•j5###åf,‡æÄ‰ 0€ÆÔËþT©Tz'Â...äääpþüy´Z-K—.eÉ’%,Y²„ôôtBBB03«|)|UÚ_‘æÍ›ãììÌîÝ»•e—.]B«ÕràÀ ¬ú®cÇŽU:vy?¸ººÞñøçÎ#==ý¦ù뮿Œù~½ê¢ п-ZÄ”)SHNNæÍ7ßdæÌ™7UU q/rs³™?#F¼Rçæó¢>066æí·ç²ÿö;Þh¡&ùøã·hØÐ…Q£^3t(Bˆ»ð “pttaîÜ醥ÚÈ€µ„N§cÇŽüðÃ1pà@úõëW/+ýn'9¹lRÔòäE9µZ¹¹ùMst]ÏÞÞ333ŠŠŠîkŒå*keÖ)OÝé2Âʬs7ñÝNUûò믿fÔ¨QØÚÞÝ]o<^ii)\¼x‘Ç{ ___¢¢¢ô¶),,ä믿ÆÑÑ‘ÿüç?ÊréOô’†W¯^ÅÜÜœ1cÆÜÕ¾ÊÝKûo_÷îÝY¿~=$&&âë닱±1{öì¡W¯^:tˆ°°°j?6”õ Üùæ ÷ë}T—DPPÇŽcÕªU¼ñÆôêÕ‹¡C‡bmmmèE-·fÍWäåå2~ü;†Eq—Ú·ïJ¯^Oðùçðè£ý*ÞÀ€®^½Ä¦MßñÉ'ß`f&çkBÔFææLœ8“)SF1qâ{¸»×þØI`-œœÌûï¿Ï²eËèܹ3 ÿŸ½;«)ÿ8þjßSD( ‘±$Kö%û¾3ÉØwƒÁXÇ0È>˜{vFR c²/ÙFöR(;Si×^·ûû£_ç+E·”[ú<rî9çóþœsûtϻϲn½zõÉ¿÷”+WàƒóHe.jð!***RO°Â¦H¬Šì£««‹†††4ô1'™‰##£ïc½–ÞÞÞ4lØ0ËðÉüÈ,O.—³dÉ^¾|ÉÔ©S±µµÍqÿƒΰaòü‰ë™•¦¦&‘‘‘DDDd{-66Váó|jýßתU+är9—.]ÂÛÛ›N:ѲeKñóó“’¦…Qvfâ/3±˜“Âz•ööö¬X±‚‘#GríÚ5¦M›Æ­[·”–PŒÉd2vìXG¿~Ã12*­ìpÅÇ¿UvJS’ëž›#¦rãÆ%nß¾¦ìP>jûö_111¥sç~ÊEPPIþ¹+ÉuÏM×®(W®;w®Sv(B$‹¸«W¯2mÚ4¢¢¢X´hC‡•V3²ªV­:::ÒÜ`™"""HNN¦~ýú<öÍ›7¤¥¥Ñ¸ñ§¯¨È‚ŠÄªÈ>***ØØØ|´Sll,:::yJn~Žkéãユ¦& 4ȲýÞ½{Ò÷Š\ËwË âÎ;Òbñà÷îy^¾|ÉÑ£G±··ÏRö7Äõ|……r¹œ={ödÙŠ···Âçù”úç¤lÙ²ØÚÚrüøq455166¦aÆhkk³víÚ,stÙ™óK^¾|9Ëöôôt©7_a½JUUUY¹r%5jÔ`É’%lݺ™L¦ìЄbèÂ…xùò C†|§ìP>ÙÅ‹'8}ú/e‡¡™,ë×/²rål.\PüwÆ®]¿1`@sz÷vøìe´“'½hÚ´AA í¯HÝ‹JÝ”¥aÃØÚÚ±oßFe‡òA©©)<èÆ!ßû…?D›S¸e4Ñæ<55u Ç[IKKUv8ŸL$‹°#GްzõjZ¶l‰««+VVVÊ©H300ÀÙÙ™dIz?~œ–-[JI!UUUâââ¤yáär9¤_¿~ õÊ<.§¡{ÇŽcøðáÒ0A@ê=õŠÄªh}š5kƃ>˜Üyøð!ÒÃ-[¶0wî\BBB>XǾ–·nÝâøñ㤥¥qòäIékË–-ÒµËéZ*ZÞùóçyþü9gÏžåÅ‹ÄÄÄðìÙ3bbbغu+êêê >\Ú_&“áëë[â®g\\ YV»‰‰!--äädj×®••>>>¬\¹’‹/âííͦM›èر#‰‰‰@Ö÷÷ûÛ­^´nÝš°°0:uꀖ–di+-;))‰ôôôl½öÞ¯‹ 5kÖäܹsœ8q‚ääd‚ƒƒ $66–K—.‘œœœç÷‘3\\\˜2e çÏŸgéÒ¥Ò=Ey{¢N†|õ•µ²Cù${öüÁóçÁ8:vSv( ¹{÷:ºñûï‹ Q|Á#'§1¼}ƒ\žÿ)ò[vAÓÑÑ£L™rhii+´¿"u/*uS¦nÝrêÔd²´ÜwV‚Ë—OMçÎý•Ê'mNá—]ÐD›S8ºww"66š+WÎ*;”O&æ,¢ÜÝÝñðð`èСÒ®»víÚaddÄ‘#G¸~ý:zzz1hÐ i'''<==Y±beË–ESS“zõêI‹|ÌÝ»w9þ<wîÜáÔ©SØÛÛK«…ªªª¢­­-Í£öäÉŽ9À¥K—¨T©õêÕCWWW¡XÙ§eË–>|˜GQ­Zµ,ñ¦¤¤ðàÁ-Z$m çáÇœ>}:Ëy>×µ fåÊ•¤¤¤d››OCCƒ7æx-)¯jÕª´k×6nÜÈÀqvvfýúõxxx`gg‡¿¿?fffüóÏ?@Fâ'00P.\R®ç… xøð!2™ŒÝ»w3`À®^½J`` r¹œ}ûöáììÌìÙ³qssãîÝ»`ggÇĉ100 ((Î;Gùòå100ȶÍÎÎN¡úçEãÆ ¦råÊÒ¶N:å¸pDneŸ>}š;wî —Ëqss£K—.T©R%ÇúÙÙÙ1}út¶oߎ»»;^^^´nÝ{{{d2†††hjjæù}$|œƒƒåÊ•ÃÕÕ•E‹1þü<-D#”\éééœ9s”aÃ&+;”Oröì1®\9ÃúõîÊEavvÑÒÒæàA·<§¦¦Ž©©¯^=ýìe´fÍÚѬY;…÷W¤îE¥nÊÔ¾}/\]gàëëC£F­”N6§NÁÖÖŽJ•ª(;”|mÎç)» ‰6§p˜™}Eu8yÒ‹æÍÛ+;œO"€Eеk×8xð £GÆÑÑQÙá; 4È6 ò]Lš”ûj\r¹CCÃ,Ûj×®MíÚµù‡uêÔ)K¶J•*Lš4éƒåå«"û¨©©áâ₇‡Ó§OÏòš§§'½zõ¢bÅŠÒ¶™3gÀÇ?Z®"eççZZYY±{÷î\yÿZ*ZÞ¨Q£5jT–m[·n•¾oݺõG/)׳E‹´hÑ"Ë>­[·Îv}ôõõ?ø~·¶¶æÇ̶=§m Øû]Q 6,˶ʕ+gI*Z¶££cŽmí‡ê§££Ã¸qã>_^ßGBî,--ùé§Ÿ˜={67nüàûRÞõôé#""Âòô@TÔÄÇ¿eæÌìÙsF١䙆†òõÊ,»°}ÉuSDåÊU13û _ßKE2xãÆ%Z¶,¾8D›SüÊ.l_rÝÕ´i;.^,þC E°ˆ‰gÆ ´oß^$ÿ”(sXdæE••Íš5ãØ±ctéÒÈš––F·nY»í'&&rãÆ úôéóYb+n×ÄõT†#FäºÏøñã±··ÿ ÑŒ¼¼ÅT¨Pï¿ÿž_~ù…† *Ôs[(Ùüýo¢¦¦Nµj_+;”|Û¿3ššZX[g]Ø*£·öFîàïƒR,Xð•+Wýèùüün°oßFâããxö,ˆþýGпÿÔÔòþXßsýóW¯žEKK›‡ý©UËž‰çf[-õÚµslذ”;wþ¥ví,\ø•*YʪUs¨XтׯŸÎÒ¥[02*£püÿþ{ñãû΄ ³™2%£göå˧;¶ÇÏäÉ >ZVhè+<=wáåµ›ýû/0yò7²{÷i®];ÏÑ£û¤¢èøñƒÔ­›=Ù½q£+ÚÚ:,\øîî— yÅÀ-HLLøà¹^¿~Î7ß´dܸY¬Y³+«Ì™3–Þ½X´èû<Å•ßs¹¹­aëÖUÌ™³š\ÁêÕ»9vìC‡vÈò3ñOÏ] 6™ æpõê9 h.ÕÏÅe qqo™8q.‹oæåË',\˜·¡Þ ¶`ìØ™|ýõÿ~Ç4jÔ[ÛºLšôS®e=xpC‡vðøq ;v¬¥cǾ”-[žW¯žñè‘?—.ʲ€‘"qçV÷œäö~pqÀ€#3f¿þºŸää¤<]«¢¦zõÚ<|è¯ì0²yüøii©ØØÔRv(ù&ÚÑæˆ6'»êÕk“––ÊÓ§rß¹ À"æòåË8::j2AÈ™ŽŽo߾姟~âï¿ÿfΜ9Åî>Ô©S‡úõëóÕW_Ñ´iÓ÷±±±AC£pW$û®%ˆë) EÞGBÞtëÖgÏžñôéSe‡"qQQá”)S|{L§§§sïžo¶ž¡¡¯qs[CÏžƒŒi:uêË›7!œ9óá;wî\O©R¥17¯ Àøñ³øæ›ÑÌ™³:O±åç\a¬Z5'§±Òê¨FFe?~×®çðáÿ­:¯¥¥ÅÒ¥[iÑ¢##FLaò䄆¾æÀ-@ÆŠë5jÔ‘ö¯Víkïæ©NNc12*¥ì ¼éÚu 4oîÇÊjÑ¢#ööM‘ÉdôìéÌÀ£8tèMš8Ò¶mlå)wnu_nï‡Ì‡V?¿hkë0räÔ<_«¢¤té²DE…+;Œl2c211Ur$ù#ÚÑæˆ6'g™Ÿ%¢¢"”ɧC€‹™LFHH––îj+===~þùge‡ñEײ`‰ë)YU©R---ž?N•*Åw’u¡ðÅÆFchh¤ì0ò-66Š´´TJ•2βýæÍˤ¥¥2gΘ,Û ‰–Ö‡ÿ@ú*KKKŒŒÊðúuö”r“Ÿsݺu•„„x*V´È²½M›®\½z–ž=Ð×Ï:s¯^ß²|ùÜ»—ñ@™9?Yrr‡ïáÎóÕ+XWW^½¾e×®ßˆŠ ÇØØ„cÇþdîÜ_¥}r+K]]55õl+M««gÔR$îÜêþ¾ÜÞêê4oÞ… 'óð¡?ü°TX\­ì0²yû6È~‹ ÑæühsD›ó®Ìϱ±QJŽäÓˆ`"“ÉÉd9þà ‚ ‚AEEUUÕ,C\!'éé騪ß/ªªjÙÞëÁÁèèè±xñæ<¯U«Î9²—Ë—OÓ¤‰#±±Ñ$&ÆÓ²eÇ<Ç–Ÿs½~ý €˜˜È,ÛMÐÑÑ%4ôõ55­ˆ¶¶Éɉ@Æ5Ù´i÷îù2dˆ uë6âÖ­«y®ÀÀ£qs[ƒ—×núôŠªªZ–HA–•Ÿs½_÷÷)ò~øýwfÍÅþý›ñöödýú88||a´¢L]]ôôtär¹Ôkª(HOOGEE•âÙîˆ6çD›#Úœw©ªª¡¢¢Rì?{ŠLS¢©©I¹råxñâE­”)EAbbb±²š””„¶vñ;ª()Îï¡h %11‘ *(;¡ˆ344âÙ³ e‡‘o¥ÐÒÒÎÖÓI[[——„„¼¤|yó,¯EF¾¡té²9ž¯GA$%%2mÚ·ôí;œÐÐWüúë>ìíó>=A~ÎenžÑc÷ùóÇ9¾neUý£eª¨¨P­Úפ§§3bDgÊ”)Çï¿{|p¨š"¬­kРAsÜÝ·¡­­Cÿ›«µ Ëú”seÖ='мÔÕÕY½z­[wañâ© Ú‘£Gocm]#_uQ¶èèH ŠTò2~får9qq±ÙzÑ¢ÍÉJ´9¢ÍÉ…\.ÇȨ´²Cù$ÅóO_0;;;Μ9Sì3ËBáËå;v ///\\\X·nééé^Î;w¸qãOž<áØ±cùžhßÛÛ›yóæ1{öç[ ­ b¿xñ" .ÄÅÅ%ß±Èd2Ø·owîÜùà¶âJѺœô˵÷–2XZÚ ªªÊ£GEo…bE‰6G´9¢ÍÉîÑ#TUU©\¹ª²Cù$"XÄ”)S†°{÷nüüü”ŽPÄœ8q‚²e3ºØW¯^3fè ´7oÞÄÏÏöíÛKÛjÖ¬‰ŽŽÞÞÞy>Ÿšš¥KžnÒ»ªªê'Ç^­Z5:vì˜ë¶âJѺ¨ªªR¦L™lÛ úž _¾øøx–-[F•*UpttTv8B1P£F]’’yüø²CÉ·Þ½¿ÅȨ ·oÿoÎ&ÜÜþ¡GAøúúðË/S¸}û+VìÄØØȘ#MGG/˃euyôè>“';áäÔšž=Ъ•%;~-Í…•Óq9Éí\wîü˺u ððØÁ¹s+`nØàɦMËX°à;V¯žGÙ²åY¶ÌM:÷?,ÁÁ¡5&ôaöì1,Zô=­Zuf„Œuë6ÂÉi,÷ïßföìј™}ÅÌ™ËÐÒÒfýú…ÝϱìÑÒÒfĈ)89Ͳ=·²þúk.x#—Ëùùgîß¿À•+gعslÅÇçd®çÒÕÕϵî9]WEß#GvaãFWæÏŸÈ?,ÁÎÎ!×ëRTܦFºÊ#]]},,¬ð÷¿¥ìPòM´9¢ÍmNv÷ïߦråªèêê+;”O¢"ÿ„®9ýúõƒøxÜׯϽ ++Öþ¹–.ý»ä·¸béØc¸ páÀ #—ËY·n7oÞdÊ”)Ô®]»#Š‹ÔÔT Ä AƒèÑ#ûïŸ*11æÏŸ¹yÖ¹’““™8q"‹-ÂÔÔ4Oç]¼x1oÞ¼aõêÕn…ûºuë¸}û6[·nÍwl/^¼`êÔ©Œ;–6mÚ|p[q¥h]~ýõWüýýÙ´)c8@aÝ3áË««+111,Y²„R¥J)|ìªU«067f½{îŸW¾4V*Vü¹v-ý»|üó×cÇàâB°<8×sNì7âõX¿Þ½ Â,4©©)Ô«—ÑÄÙy¼²ÃÉ·{÷|Y¿~!7þ¤óœ:u˜ÔÔTš6mKdä"#ßðß/yðà.r¹œ©SQʹ!/ÂÂþ£iSs6lðÂѱ[¾Ïce¥¢Ðóiæó\p°bÍS¦8òн{‹ïpGÑæBVNN­¨P¡+WîRhÿ¼¶/òàÜ?õ›8ôôpwÏÿç/±H¤¢¢Âøñãùã?X²d #GŽ=J¸óçÏs÷î]®\¹BHHåË——×®]ÃÏÏMMM^¼x¥¥%}úôÉÒ;0·}NŸ>††F¶d €–––––:tˆqãÆå«þþþxyy„µµ5#GŽ”’;111ìß¿ÂÃÉeìØ±0lØ06lˆ®®.Ñsì¿ÿþã»ï¾£yóæ…{tt4›7oæþýû”+WÌÌÌ€Œ„ý©S§xúô)OžŒ3†°°0¶oß.ߪU+ÆÇ!CèØ±#áááT¯^fÍšzì)))>|˜Aƒ±`Á^¿~ÍîÝ»¥×>Œ¦¦&£FbÑ¢EDFF2þ|©nŠRäå¦N:4nܾþúk4449r$eÊ”ÁÄÄ„š5k`bb‚‰‰ ÎÎÎ •ýüùsΟ?Ï«W¯8~ü8‘––óœZ>>>xyy1|øp D¿~ýxñâE–} óž _Žôôt>ÌÌ™3111aáÂ…¢W¨gíÚõäêÕsÒœHÅU—.¨Zµ&§NÉ×ñ· }Åo¿-Âßÿ&II‰DF¾áÌ™£¬Y3ŸþýG*å\‚WǤE‹Žèèè*;”µjÕ uu ¼½=•Ê'mŽ dðö>„ºº:-ZtPv(ŸL$‹0 Àܹsyüø1S¦LáìÙ³âXdöœk×®4¾½zõâþýûøøø(´\.'88}ýÏi`ddDBB/_¾ÌsœêêêŒ7ŽºuëÒµkWú÷ïOTTgΜ‘öùꫯ¤ï-,,xþü9ÑîsçÎÒkÛ¶m#==Q£F¡¢¢R豫©©áììLÅŠ±°°ÀÚÚšÇűcÇhÑ¢1׃ƒÑÑÑܸqCá2¹GŠjÛ¶-iii\¿~]ŠßÁÁ€€âã3&îMMM%==SSS…Ê®[·.666¤§§Ó¼ysY¼x1ÆÆÙWßKNNf×®]tîÜYê¹h``@5¤} ûž _†   æÎËèß¿?óçÏ—z B^´k׃RìÝ»AÙ¡|²æÍÛtÕÊéÞ}ß}7]»~£{w{4(ÇÈ‘]‰Š gΜÕyJ¦ä¹!/=òçÊ•3 Pt>zztîÜŸ;Ö~¶…ð ‹hsvíú®]ûùÿ@ .¾þúkV®\ɾ}ûØ´iGÅÙÙ;;;e‡&(Ù£GHNNÆÄ$ëð{{{üüüÐÑÑÉuŸºuë"“É>šÉ~›Ÿž¨™ÇfjÙ²%{÷î•ióçÏ2S/^$((HúÀ¤¢¢"-|òï¿ÿrëÖ-zôèA¥J•ˆ‹‹+ÔØÕÔÔ²¬4jjjÊÇxðà2™Lš×.“££#ššš —¡È}lÞ¼¹Bç²µµ¥\¹r\¸pAê!ùìÙ3d2W®\¡mÛ¶\½z•Få©ìÌëP¾|ù–HTTY¶¿;Œ¹°ï™P¼…„„°oß>®^½JõêÕquuÍq¨¸ (JGG—o¾ÍîÝ¿3zôt45µ”’R¨©©1yò&O^@bbÚÚ:Ò ‘Ê<— ä…›Û,-mhÑ¢h/¨6|ødºu«Çå˧iÚ´­²ÃQ Ñæ_Ÿ“ÞeÅŠ/c˜¹HÚÚÚ 6Œ:°wï^–,Y‚ Ý»w§~ýú¢,¡Þ¼ÉΗe»ZZZDEE)´Oæª[ë]ªÈ>Š266FSS“””震æñãÇtêÔ kkk=z”å˜ääd¶oߎ‰‰ }ûöÍS\û»?k¯^½BKK‹1cÆ|Ò9¹Gy‰¯eË–xxxMHHÖÖÖ¨ªªrñâEÚ¶m˵k×pqq)ð²!ãš@F¯ÏùÜ÷L(^¼xÁ_ý…¦¦¦L›6 (;,á 1xðDÜÜÖ°cÇZFúAÙá(]Aö–=o„Ïåñãxxì`áÂ?Šü³­­ÍšµcÕª94iâXäã-l¢ÍŠ£ôôtV®œM‹¨Q£Ž²Ã)bp1S±bE¦M›Æ¢E‹Ð××gÅŠ|ÿý÷œ>}:ÏsŽ Å_¹rå Íñu333…öÑÕÕECCC"š“Ì‘‘‘ѧ„,QQQ¡R¥JÈår–,YÂË—/™:u*¶¶¶9îðàAÂÃÃ6lZZÿ뽡ŒØ3ijjIDDD¶×bcc>"÷(/Zµj…\.çÒ¥Kx{{Ó©S'Z¶lI`` ~~~R¶0ÊÎLüe&s¢Ì{&-r¹???–,Y´iÓ bÔ¨Q¬X±B$ÿ„U®\FúuëòæMˆ²ÃÞ‘—ûN‚,Zô=U«ÚÒ§ÏPe‡¢9sVsïÞ ¼¼vç¾³ðÙˆ6GP”§çNüýo2sære‡R`D°˜ªV­3fÌ`ÕªUT¯^mÛ¶1f̶lÙ“'O”žð™T«V i¾·L$''S¿~}…öQQQÁÆÆæ£½½bccÑÑÑ‘†Þ~Š7oÞ––FãÆ âÎ;Ò2™,Ëœ)/_¾äèÑ£ØÛÛgI ܸqã³Çþ. är9{öìɲ=44ooo…Ï£È=Ê‹²eËbkkËñãÇÑÔÔÄØØ˜† ¢­­ÍÚµkiÕªU¡•9—ãåË—³lOOO—zó)óž ECLL ‡fòäÉüüóÏ$%%1}útV®\IëÖ­³ »„‚2jÔ”bÉ’iÊå‹"—ËÙ¶m56,¥M›ªL:™L–ëq‡ïaðà¶´ióᡊ’“'½hÚ´AAŸtžû÷oáæ¶¦ØÏ ÷¹y{âüùãÌ™³ºØüލZµ&ýú gÙ²DGgÿc±?¢ÍÉÑæäOTT8Ë—ÿÈÀ£±±©¥ìp ŒHsfffŒ;–?þøƒ¾}ûâïïÏŒ3˜1cÇŽ#22RÙ! $³—Ù»== pvvæÁƒÜ»wOÚ~üøqZ¶lIÍš5Ú Y³f|ˆƒƒƒ44sË–-Ì;—÷¤PUU%..NŠ[.—sðàAúõë—¥wÙùóçyþü9gÏžåÅ‹ÄÄÄðìÙ3bbbغu+êêê >\Ú_&“áëë[¨±ÇÅÅ‘eµÛ˜˜ÒÒÒHNN¦víÚXYYáããÃÊ•+¹xñ"ÞÞÞlÚ´‰Ž3æ¦ILL²Þ·÷·)zò¢uëÖ„……Ñ©S'´´´pppÀÀÀ+++i?EËNJJ"===[¯½÷ëbccCÍš59wî'Nœ 99™àà`‰åÒ¥K$''çùž Å_JJ W¯^eÅŠŒ;///êÖ­ËòåËY°`ööö%~ˆ”P¸tuõX²d GŽì寿ö);œ/ƺu?óøñƎɲen¼}CZZjŽû†…ý'}ßµë@d2ÙW”/jttô(S¦ZZÚy>öÝzÛÚÚQ£F\]gdx_´ÐÐ×Ìš5šGáàÐZÙáäÉŒ®¨«k0sfÑ]´¤¸mNîD›óéæÎ‡šš:S§þ¢ìP ”˜ð ahhH×®]éÚµ+œ={–ƒ²sçNjÔ¨A“&MpppÀÐÐPÙ¡ ùðäÉŽ9À¥K—¨T©õêÕCWW—víÚaddÄ‘#G¸~ý:zzz1hÐ éxEöiÙ²%‡æÑ£GT«V-Kù)))‡-:àì<žùó'P·®•*UQvHÅÞîÝ¿3|ø÷ԯߌúõ›å¸_LLS¦8³{÷i cRÿ ÌyøÐï³Åú)š5kG³fíò|Üûõpph¯¯»výÆàÁ 2Ì/ŽL&cúô¡•föìUÊ'Ï X¾|;ƒ·eÿþÍ 8JÙ!{¢Íù8Ñæ|º}û6âí}ˆÝ»ÏPª”±²Ã)P"øª^½:Õ«Wgäȑܽ{—+W®°g϶nÝJ•*U°··ÇÞÞKKKe‡*(¨J•*Lš4‰I“&åøzƒ r/+·}ÔÔÔpqqÁÃÃéÓ§gyÍÓÓ“^½zQ±bEiÛÌ™3 VÄý ‹ÆiÔ¨QŒ•õÑÖ­[¥ï[·þø_{ #ö-ZТE‹,ÛZ·n-}}}¾ûî»ÏammÍ?þ˜m{NÛ@±û¨( † –e[åÊ•³$-ÛÑÑGGÇlÛ?T?Æ÷ÑøòzÏ„âãíÛ·øùùqãÆ |}}ILL¤Zµj 0€&Mšˆy¥›9s97n\fôè_B__üq4¿’““ˆˆ˵÷nbb“& äŋǟ)²¢ácõ>| ­ZYÒªU'*UŸÉ?déÒðõõaÿþ èêê+;œ|qph͸q³øé§‰XYU§AƒæÊ©ØmÎlj6çÓ]»vž \˜0aµTv8N$¿`R²/99™Û·osóæM¼½½qwwÇÔÔ{{{êÔ©ƒ­­m–…„’ÉÊÊŠfÍšqìØ1ºtéÀ­[·HKK£[·nYöMLLäÆôéÓG¡fSœcWĈ#rÝgüøñØÛÛ†h F^î™PtÉår?~ÌÝ»w¹yó&=BMMš5kâääDýúõ)]º´²Ã‰¶¶›6¦W¯†Lšô ›6)6sŠ}nÿüãÁÕ«gÑÒÒæáCjÕ²gâĹhjjqèÐ.]:Àß»óìY_}e͘1Ù‡™8áIPP11QÌš5Š*Ul5ês1¾yœ9c¹~ýææUXµj7ÖÖ5þ¿×ýFîàïƒR,Xð•+x¯û÷o³}û¯XYUçæÍË$&&°sçIéõsçþæÌ™£¨«kp÷î¿ôí;œGú OÏ]xyífÿþ Lžü ÁÁìÞ}šk×Îsôè~ž@»v= ºÏáÃ{8q“;O1oÞx®_¿ÀW_Y3oÞZìì>Zo]]=jÕªÏo¿ýÂÒ¥[?T•mÿþM¸¹­aÍš½Ôª•·9‰‹šÉ“tŸñãûpèÐU‘€ùÑæˆ6G™ž= büø>´mÛI“~Rv8…B$K---5jD£FËå<|ø7npëÖ-Ž?ŽššÕªU£víÚÔªU +++1÷V Õ¤Iîܹƒ¯¯/eË–%!!!Ça¨ÏŸ?gÀ€Ej_qŽ=7ïöˆü’(zÏ„¢%44”{÷îq÷î]üüüˆ‹‹ÃØØ˜:uêеkWêÔ©ƒ¶vÞ笄ϥB…JlØà…³s¦OʲeÛEð=nnkøûowöí;‡ººÑÑôéÓ__öì9KïÞChÓ¦+^^»iß¾&Ìþà¹zôÄÑ£ûyøÐÅ‹7gy-99‘]™1ÕÔÔúömŒ«ët6oþ €])W® þL&£sçÚ Ø‚³gƒÑÑÑͱ<—¸ºnÃÞ¾)II‰ Ò^zÍÓsçÎýÍêÕ{PUUå÷ß3{öh¾úÊŠÔÔÚÁӧرc-;öåÀ-¼zõŒGü¹téNNc¥óìÝ»øø·lÙ²‚áÃ'Ó»÷·Ìž=†Áƒ9s&è£õ¨W¯1›7¯à—_6‰÷ß{Žû“yóÆãâ2Ÿ®]*;œO¦ªªÊŠ;ùæ›– Ü–}ûÎS¡‚Xèì]¢ÍmŽ2½~ýœo¿m‡……%Ë—ïøbç¥ À(sNœœœˆÅßߟ{÷îqêÔ)öïß––ÕªU“†W¯^½X%K„OS§NéûÌU]ßgccó¹ÂÉ“â{I¥È=”+44”ÀÀ@ÿ•aüøYLŸ>ŒÃ‡÷г§s”¥¦¦Î?.GM-㱤ví†øùÝ2ps[Õ+¯ÿ_5:uê˺u?sæÌ_té2 ÛùÒÒRyúô~~7°·oж¶#GN 2ò |Ç_Ý’îõ7ߌÆÏïeËVÀÚºöö ¤gOg¾úÊZš³MMMmÛVKåüðÃ^¿~Îß»K =@ƪ÷'öcïÞ Lž¼à£u/SÆ”·oc ºÿE­2ù©NœðdÊ”Á <—ùʧÀèêê±sçInË AmØ·ï<¦¦bºmŽhs”+<<”!CÚ£§g€›Ûñ&z¿"(`hhHãÆiܸ1¯^½Âßߟ€€N:…»»;šššT­Z¬¬¬°²²CºA¾@‰‰‰x¾æÍ;°pád>ô㇖Ү]O|}}HOOϲø‹±± ¿ÿî‘-ž¯¾²~ï¼ÙÛ;]ÔÔÔ¤q€öí{¢©©Åƒ÷r­»¡aÆœ¨áá¡âaüÿyzîbæÌ 8Š9sVç~@1Sª”1nnÇqrjÅ7ß´ÄÍíx¶÷ZI$ÚÑæ(ËÓ§6¬ššZìÚu #£2Ê©P‰OîB6fff˜™™Ñ¾}F×åÐÐPîß¿O@@×®]ÃÓÓ¹\ޱ±±” ´¶¶ÆÒÒ%G/‚ (*55•gÏžLPPÁÁÁ¼zõ ¹\Ž‘‘ÖÖÖtëÖ5j`ii)†‹_¤–-;áævœ±c{1thG6lðÄÀ ”²ÃRª×¯Ÿ™e»±± ::º„†¾.´²ßíI€ŽŽ^ŽCÙ>æ÷ß=˜5kû÷oÆÛÛ“õëààК‡ýHKKE.—Ze55uLM+"“¥åºof ôôôB‰¥¸Ù¾ýW~ùe ƒOdΜÕ_l¯ò2eÊñçŸ5ª;½{;°yóêÕk¢ì°”J´9ù'Úœü»{÷:#GvÅÌì+¶l9J™2å”R¡ @!W¦¦¦˜ššJ«Ÿ&%%ñôéS?~Ìãǹté’‚–––ÒWõêÕÑÓÓSr A„ôôt^½z%µÝ™_©©©èèè`aaA­ZµèÑ£–––Tª$æ&J‡ÖìÝ{Ž#:3p` 6lð,Ñ“ô››gôVyþ<ç4­¬ª–8´µu yIHÈKÊ—7ÏòZdäJ—.›ãqêêê¬^½‡Ö­»°xñT†íÈÑ£·Ñ×7$99‰  ûT­Z3Ë1))ÉhjÌ‚x‰‰ XZæ~¢£3’eË–/r‹«””d~úi"îîÛ˜;w ß~û²C*tFFeرÛ‰û3dH{\]ÝèܹŸ²ÃRÑæ|Ñæäݱc2sæ6lɺuÐÕ-9 ‘òL[[[š0Sll¬ÔƒäñãÇœ8q‚˜˜TUU©P¡•*UÊòU¾|yÑ“D¡DEEñâÅ ž?ÎË—/yþü9/^¼ 99---*W®Œ••íÛ·ÇÚÚšòå˱=-AQ¶¶uqw¿Ì¸q½éÑ£>«Wï¡eËNÊK)êÕkŒ¾¾!'OzIsY„„¼$11¶m»«e*JUU•øø¸<ÅacS ¹\Ž«ë V¯Þ#mþ</¯Ý9Η’’Ìþý›ùöÛ‰tï}SÚ´©ÊÕ«g©]»+WÎá÷ß=¤Þ0~~7xõê:ôÎS|9 ûÈÈ7têÔøx½£¢ÂÑ×7Ì–(IBB^2aB_‚‚øãC´mÛCÙ!}6ººúlÚt˜… 'ãâ2€{÷|™6mq‰|FmNþ‰6'od²4–-›ÉÖ­«þ¿·ñª,C¿t%§¦B¡244ÄÎÎ;;;i[xx8ÁÁÁ<}ú”/^pñâEÂÂÂHOOGCC333ÌÍͱ°° R¥J˜››S®\9ñ*‚  ·oßfIòeþ@©R¥°°° jÕª8::bmm¹¹y‰|¸E˜›WÆÝýsæŒaäÈ®Œ?—y%êá2z'͘áʼyã¹|ù4Mš8°}ûZz÷‚ƒCƨÿþ{ @RRB®ç,W®"QQáøùÝ >þ-µk7$**‚ØØhRSS¤¹#"ÂHII&11fÍÚQ»vŽÙKrríÛ÷"..oïC¬]»ÿƒe¹»ocdê4 IDATРq¨©©ajZƒRÔ¬Y;;Z¶ìÄÉ“^8;;Ò©S_^½zFLL$K–l !!Žôt±±ÑÒ|Yñ ½€Þ•’’L@ÀjÔÈXÐê·ßÑ»÷êÔiøÁzgN0óæe:vìSbÛä þaÚ´!›àéù/––%o‘6uu ,ø:u2wî8îÝóeåʘšš);´ÏJ´9¢ÍùBB^2eÊ`îܹÆòå;èÕk°²CúìJÖ§á³211ÁÄÄ„FIÛRRRxõêU–Õ“'OòæMFæ­­™™˜››cnnNùòå)[¶¬˜l^„+22’þûï?^¼x!}EGG §§'õ°nܸ1•*UÂÂÂBÌË*ù ­­ÃŠ;±³kÌâÅSññ9ÁÊ•»¨\¹ª²Cû¬œœÆR®\6mZÆÉ“^S¶lyfÌpÀßÿ&›7¯à¯¿öQµjMZ·îòÁù Ç™3G™<Ù‰©SáŸ<¸}û*2YK—NçûïæŸ<ðõõA.—³bÅ,fÎ\†›Û?üü³ >>'¹~ý"-[vbÅŠ›|0vuuuFŽì‚ƒCkž?Ì?,ÁÎ΀ß~;È’%Ó8uêë×/¤C‡ÞÌšµ€ýû7sá‚7r¹œŸvaøðï±µµãÊ•3ìܹ€¶baaE³fíþ¿, ÚAHÈKôõ 17¯Ìøñ³?XïÌñ¤¤DnÞ¼Œ»ûåO¹MÅRRR"K—þÀîݿӭÛ7,Z´=½’ýûªwï!ØØÔbÒ¤oèܹ6 n(qC‚E›#ÚœÂtìØŸÌ;SÜÝ/ck[WÙ!)…ŠOÏÌ›÷+ÖÖ¶ÊMP"Ñæ(æÑ#,páÚµs8;O`êÔEèë*;¬"A$…bKCCCzh®U+ë_âSSS %44”ððp"""ˆˆˆàå˗ܾ}›¨¨(éA\MM ###LLL([¶,eÊ”¡L™2”-[J—.-æÑ„.%%EjKÂÃÃ¥¯Ì¶åÍ›7¤¤¤Hû—*UJjKÌÍÍ©[·.&&&Ò"ôõõ•XAò£zõÚ8p‰={þ`Íšy:´ƒï¿_Hß¾ÃÄK ={þÀÇç$2™ŒÙ³GÓ»÷ê×o–ëq5jÔ‘&ïÿÒݾ}E‹¾çÎkôé3”™3—‰á¬y ¯oÈO?­§GgæÍOçÎupräI?}t>:áË$ÚœÜEF¾aÍšùìß¿™êÕkãî~™ºuå~` "€ÂICCCZDäC2çÛz·§NXXDGG&-5¯¡¡¾¾¾Ô{POOOúþÝm&&&èèè|®j ‚ð‰2{íÅÇÇKí@æ×»Û¢££³´¥K—ÆÈȈҥKS¥J•,½‹+T¨ ÚAøB©ªª2xðºuȯ¿.`Þ¼ñìÚµžiÓ‹¡E%Ì Aã4hœ²Ã(’ž>}Äš5ó9zt? ¶ÀËë:5kÖSvXÅ–‡ûâá±U«æpøðÆû‘Áƒ'~–¹Ì„¢A´9–ÏÎëØ¸Ñ]/ÞDïÞCÄœ½9 @¡ÄÒÓÓûàðbÈèñóæÍÂÃÃ‰ŽŽ–’111¼yó†àà`¢¢¢HHȺ ½¾¾>FFF”*UŠÒ¥Kchhˆ±±1¥J•ÂÈÈ###ôõõÑ××ó B“ÉdÄÅÅñöí[Þ¾}Ktt´ôELLŒôoLLŒ4Ud$öŒŒŒ¤Ÿ×Ò¥Kcii)m+S¦ &&&¢G° •aþüµ8;gÙ²ŒÙ•ºu˜2e!Mš8*;…M›–±rålÖ­[ÈàÁøöÛ‰”.]VÙ! B‹ˆcÇŽuìÙó;))) 0’1cfP¶lye‡VlˆO#‚ðeöä«P¡‚ÂÇ$%%I ‘ØØØlIø¸8‰ŒŒ$!!!ËWæB'ïÓÒÒÊ–$ÔÓÓ“¾×ÒÒBGGMMM444ÐÕÕE]]=×mBñ$—ËIHH %%…””‘ÉdY¶%%%‘––FBB©©©$%%eKæ½›è‹ÿ`y9%©µµµ©P¡ÕªUË–à{7©'‚P”Õ¯ß 7·¸ÿ6›6-cÚ´!¬X1‹AƒÆ1`ÀH±‚PìÉdix{{²k×zþý÷_mϯ¿î§C‡Þ¢ÇŸ˜šVdîÜ5|÷Ý\vî\ÏÎëÙ¼y9=z bðà‰%fñáËvÿþmvî\Ç‘#{ÑÓ3`ذÉ8;OÀȨ´²C+vDPŠ8mmm´µµ11É{—æÌdNfbæcÉš„„¢££yýú5ÉÉÉ$&&JÉŸ„„i„ÑÕÕEMM ]]]))¨££ƒººº”0Ìœ÷ðݤ¡¶¶6êêêÒ9TTTPQQAW7cbc555iQ…wÏ¡¥¥%÷î9r¢¦¦†¶öçŸ%--äääî'}Ÿ™8“Éd$%%ÃÌ3Ï‘’’"õ MLL”†»ÆÇÇ#—Ë¥dÞ»çx7±—–––åÞ¾{Žy÷>jjj¢©©)%Ž133Ë’Ð{÷ëýÞ¨‚ _:[Ûº¬Y³—©Sa×®õlذ”_ý‰îÝpvÏ×_Û+;DAÈ“ððPØÊž=öš6mº±k×)1çeadT—ùŒõ‡í`çÎõüùç6lÁàÁi×®šÊS–ššÂ‰^ìÚµžë×/RµjMæÌYM¯^ߊÅo>H Â,3QSªT©O>Wzzz¶¤`ZZIII$''“šššë¶Ì$#dôlÌ졘™`”ËåR,³¼ÏIO/ïó¦¼[ÏACCMÍŒpï&=ßÚYUUU) gllŒŽŽjjjèééI‰Ôw{™çÎi› ‚w•*UaÖ¬•Lú ÿ}€-[VÒ£G}¬­kйsúôйyee‡)9JIIæâÅxyíâÄ /tttéÝ{ÆM¦R¥*ÊOÈŽŽ®´Z¬¯¯;v¬eòd'ôôôqtìF¯^ßÒ¤‰£˜ŸT(²üünpèÐNþúkÑÑ4n܆M›ŽÐ¦MWñ¾-"(‚BTUU¥!ÌŸ[jjª4\fb²&ß>6ä4sß´´´¾þn»¼È-A¦¢¢’ë5ÓÖÖ–†Í¼Ÿ¼åõ^A †––6½z}K¯^ßJ7»wÿÎúõ ±³kLïÞßÒ½»ººúÊU¤÷èáÃ{xû6‡Ö,]º…úˆE&Š‘úõ›Q¿~3BC_sü¸;ÛùöÛv˜™}E×®8pVÊS yÉáÃ{pw߯“'±¶®³óxz÷"þØPÀDP„"OCCC.¬Œ¤ ‚ ”¯¿¶çë¯í™9s™Ô»ê§Ÿ¾cáÂÉ´iÓ•ž=ÓªU'±pˆðY½zõŒ£G÷³ÿfž?ÆÚÚ–Ñ£ wï!b‚ýbÎÔ´"C‡NbèÐIM©RÆtìØ—¥K·R¿~3e‡÷ÅŸ,AAá3ÓÔÔÂѱŽŽÝˆ‰‰âøqwÚɘ1=02*C«VhÓ¦-Zt@_ßPÙá _ GüùûowΜ9Š¿ÿMÊ•«@ÇŽ}éÓg5kÖSvxB!¨Zµ&Ó§/eêÔ_¸zõ,žž;Y±b˖ͤ~ýf´iÓ•zS¡B%e‡*|ÂÃC¹xÑ›¿ÿvÇÇç$ÍšµcíÚ?iß¾§XAü3 @AAAP¢R¥Œ8p4Ž&88'<9}ú“& DCC“ÆÛàèØ6mºR¾¼¹²ÃŠ©ÄÄ||NpæÌQΜ9Jxx(ææ•iÓ¦3f¸Ò¨Q+±’o ¡¦¦FÓ¦miÚ´-sçþÊ©S‡9}ú/V®œÃÂ…“±µµ£M›®´mÛ¯¿¶s¯ ù"—˹wÏ—S§ŽpöìQîß¿žžÍ›·ç—_6Ò¶m ”f‰"€‚ ‚ ‚PDXYUgܸ7îG""¤dÍ’%Ó˜7o<¶¶vR2°fÍzÒP‚“ÿþ{Á¹ssúô_\¾|šÔÔj×nÀ!.8:vÃÆ¦–²C”¬T)cúôJŸ>CIIIæêÕ³œ:uí¬_¿SÓŠ´nÝ•6mºÒ¸q1¤ðQ q\¾|†³g3~w……ýGÅŠ8:v㇖âàÐ MM-e‡Yb‰  ‚ ‚ AeÊ”£_¿áôë7œää$|}}8}ú/ÜÝ·±vítuõ±³s iÓ¶ØÛ7¥N†hhˆ•ÛK²ÐÐ×ܸq‰K—NáëëCPÐ}´µu°·oÊôéKéÔ©¦¦•¦PDijjÑ¢EGZü{wÕu÷ü3 30¬²¨,² . Џb‚&j´Æú¨M4©‰±šE³˜6mšÄhMlm’šFMm›ä1q÷-*F‹ˆ * . ìû200Ëï:÷çÈ6(0Ìðy¿^¼Îܹç{ÎÀÕû³Lš†µkÿ†ìì ?~ññÿ‡Ÿ~ú"‘C†„!"bùä°±±ExøXŒ‰ÐБ ‡——Ÿ±Ã§TYYŽŒŒó¸rå._>‹ÔÔD@&³AxøX,XðÆŒ™Œ#"™ð£.çêÚ¿øÅÓøÅ/žм¡ƒ.ùœ”tß~ûˆD" ÃÈ‘ã:C‡ŽD`àPŽL6QMMÈ̼Œôôæëιs§•• 99»cÆDs¤'…‹‹;f̘‡3æ*+ËšÚ<2ìâÅìÝûO(u°´” (hBCÃ…"‚ƒG@&³1r è^õõ \»–&$ûÒÓÏ#++*Ulll<ãÆÅàõ××bÔ¨‰Ë2=&{HMM±C ""ê“T*•±C êq~~Aðó ¯~µ PÔéÝÜ:u Û¶}þß›;; 2¡¡áÀÓÓ¡¡#1þ’ÿ&OFrjõJry?Lú$¦N} V«qófæ=Iì ø÷¿w£ººb±~~Á G`àPášããÀÑ«ÝL©lÀ­[Ù¸u+7of!;;ééçqóf¦ð!QHH8Æ{/¼°Jøˆ;„›&{È’%KŒQŸ5Ýgº±C 2*[Œ9#GŽÊîŸÞuùò98𿨬,м!À Aþðõ „O€Þw&¡ §PÔâæÍ,½›nÝÏ••åš×xôñ @`àP<÷ÜkB²ë©‘©‹Åð÷¿fÏ^(”çåÝ@zúy!)øý÷_¡  0ÀKïzãçŸxzú@,fúÂjµ yy7[\onÝÊF~~.´Z-,,,0p 7üü‚1uêlad8—‰0oü êfQ“£ð]üwƃˆˆ¨OëçÖÏØ!õ:‰C‡F`èнòæQiÙ¸uëÿß8&%Åwßý E-ÀÎÎzcÀ/¸ºöÇ€^psww¸»„›Û@¸¸¸›ýšš*ÝEaá]磠àJK ‘ŸŸ‡’’䡸¸ [ÂÓÓ¾¾‹_þòר§¼Ì¾¯ˆÀËË^^~˜>ý„2¥²·o_×KTee¥ãßÿÞÒÒ"€¥¥ƒàî>P¸îôïï 7·pw(\{¤R+c5­G46*QT”¢¢»(*º‹ââÞAqñÿ¿Þܽ{*U€æµuÉÔ ¦ê¶4÷¾¢–˜ìfýÜúaì£c‘Aäò~ï‡ðð¨ÏÝý晴läý÷†3iigPRR ŒhšoØ]\Ü1`€\\ÜѯŸ+ìíåppÃÞÞ±ÕïrØÚÚ÷ds4' jjªP]]‰êêJáqMMe‹²¢¢»())DAAêëÂ9¬­epw÷€›Û à…Q£&`À/a¥——/×ê#j…••5‡"0ph‹çjk«…‘l¹¹7„ÄúÍ›Y(*º‹ÒÒ"¨ÕjáxggWáC‰~ýÜô®-­]otפžNÀk4š×ýkÏÿÿ^VVŒ‚‚<””¢¼¼D8‡X,†‹‹;ú÷÷„«ë 2“&Mƒ··Ÿä³³sèÑvQïÆ ¤y”ÆŒ™Üêó õ¨Ý kAAJK‹—wSïÆ¶ººR¥r/ áÆ\çÞ©°¶¶ö°´l¾‘Él„Q,"‘Z­@óÍuMM•ðššš*h4@}}…Ç55UP*Zm££“0Ð% üýC0n\L‹QH\Ÿ¨ëÙÙ9´:RYG­V£´´……wPRR€üü<”–¢ àÊÊŠqóf–pÝ©©©„BQ×f=ööŽB’ÞÆÆVؽØÊÊÖÖ2Í#yíìZÿ¢¶¶juóºÃ õÂu¥©©Q¨W¥jBuu%êêZß#ÀÆÆ¶Å%Îή ‡«ë½ëŽ‹‹;׿£Naˆˆˆˆˆº„µµ ƒùcÐ ƒŽ¯¯W£`î§Kà©ÕjÔÖV ¯©®®} E­Dlll„DÒ|ónaa//_"€­­pcïͼµµ¬E‚ïÞo"êÝÄbñ§4èx•ªIïZsÿ—FÓ<š°¶¶ZYX_¯@c£@ó܆†æ‘¿ºçuI8W×þBÒP*µv:‹ÅÂH< ±pií‹ëRwâo…Lf™ÌÆà›w"¢‡ai)““ œœ\Œ QãJ³DDDDDDDDDfŒ @"""""""""3Æ ‘cˆˆˆˆˆˆˆˆÈŒõè& §ŽžBeYeOVIDDDDDDDDÔ§õXÐY.Ç‘]q8²+®§ª$"""êÓœårXYY; """"2²K–;×SUÑq @"""""""""3Ö£k‘é;zl?ÂÃåÆƒˆ¨ÇðšGD¦Ž @""""2ØSËžÂø)ãÑC>j¸AǬۼ®¢!"sbÈõ¥§1HDDDD?e<€DÔgxùyá©eO; "¢‡Æ5‰ˆˆˆˆˆˆˆˆÌ€DDDDDDDDDfŒ @"""""""""3Æ ‘cˆˆˆˆˆˆˆˆÈŒ1HDDDDDDDDdƘ$""""""""2c–Æ€ˆˆˆˆèA$M2vDDDÔK¸¸» xx°±Ã赘$""""“ôëÇ~m숈ˆ¨—˜þ?Ó±iç&c‡Ñk1HDDDD&ëÅ_Ddd¤±Ã """#úÛßþfìz=&‰ˆˆˆÈdY[[ÃÖÖÖØa‘‰Åbc‡Ðëq"""""""""3Æ ‘cˆˆˆˆˆˆˆˆÈŒ1HDDDDDDDDdƘ$""""""23 &}~"êZܘˆˆˆˆˆˆÚ¤ÕjqèÐ!455áøñãÀË/¿ ãŒ'IMMÅ·ß~‹wÞyF‰¡7KLLĉ'——‡­[·šÜù‰¨{p Q/VQQaÔóîÚµ ùùùxòÉ'ñÒK/A¡P@­VwKL­¹?N+++888@"‘ôX ¦düøñÐh4Ýöu÷ù‰¨{0HDDDDDÔKÕÕÕá¯ý«QÏ{äȸºº‚ƒƒñÖ[oõXò­µ8‡Ž>únnn=ƒ©±°°€³³³ÉžŸˆº€DDDDDD½R©ÄgŸ}†¢¢"£·©© UUU‰D]ƒ!º«ýDD}×$"""""z.\À¹sç ‹qýúu<ú裈‰‰žOIIAzz:¤R)òòòàç燹sçB"‘àÖ­[HLLDJJ 6lØ€mÛ¶!55nnnxíµ×àîîŽ3gÎàÎ;¨««Ã–-[0pà@Ìš5 Z­ÇŽí[·póæMØØØ`É’%0`nß¾mÛ¶áÊ•+ ÅÊ•+ñŸÿü?üðæÍ›‡Y³fµyÞû%$$àÒ¥K€ääd¢ÿþpqqÁÖ­[accƒ¯¾ú …‡ÆO?ý|ðÁµ¯£~l-ÎG})))8uê¦M›†Ñ£GwY·ÆÐ×UUUaÇŽpqqAii)ª««±|ùrØÛÛ#//III8}ú4V¯^Ÿþñññ°¶¶Æ’%Kˆ~øgÏž…J¥ÂòåË1bÄ!†öÞïŽTVVâ믿ƕ+Wàææ†•+W ë&¶³®í‡‚‡‡233¡T*ñî»ï|~"ê]8ˆˆˆˆˆ¨“Nž<‰“'ObÉ’%xî¹ç0jÔ(lÙ²éé逨ØXû õõõ˜;w.–-[†ââblß¾àè舲²2`×®]9r$6lØ™L†Í›7ãŸÿü'¦L™‚O>ù...øæ›oôbhïýnOcc#öïß… bÍš5ÈÏÏÇwß}gPÌð—¿ü111˜={6^{í5455uêüDÔ»0HDDDDDÔ ÕÕÕøöÛoñÔSO Sc§L™‚1cÆÀÉÉIY5uêTˆÅb€½½=æÌ™ƒ+W® )) r¹þþþ€ùóçÃÓÓ>>>°`Á|ûí·8vì¦NÚêyýýýõ6ñÐÑ<»ÇYoooœ>}{÷îŸqãðŸÿü*• eee¸yó&|}};lŸ!ýØZœº)½ºpööö]Öß­1ôu ð÷÷GNNòòòPUU…Û·oC.—£¾¾ô¦º²ššše*• 0|øðßïÖÔÖÖB¡P@¥RÁÒÒRˆY¥RéÅÝV̶¶¶øùçŸñØcÁÂÂÎÎΰ±±ŸŸŸAçoíw‰ˆŒKüþûï¿ÿ /Þ¹s'ÐÔ„ù3ftaHDDDDÔ“2²³±ëða¼úþ«Æ¥S¾XóÆŽ ///c‡B}Pdd$êêê••…ŒŒ ôïß‹-‚D"Ð<ºÍÇLJÂõëב••,\¸"‘—/_Æ P( T*áïï””;vLñ6dȸººâüùó8{ö,áãヨ¨(”——###/^„••–,YGGG¤§§ã‹/¾@PP†‘H„‚‚œ9s—.]‚üýýáââÒâ¼÷»yó&8€¼¼<”——ÃÁÁ...H$ðõõE^^‘™™‰éÓ§£²²ýû÷‡‹‹ ÊÊÊ jߘ1cÚíÇû㬭­ÅþýûQZZŠªª*¸¹¹ÁÝݽËúûþuì yÝøñãQUU…K—.!;;‘‘‘:t(Î;‡ÒÒR888àСC¨ªªBCC|||››‹ ²²•••4h ±oß>TVVB©TÂÇÇvvví¾ß­9yò$âãã¡T*Q__   $%%áøñãhll„R©Dtt4ª««ÛŒy̘1HJJ™3gPQQÄÄDDGGcÔ¨QèÐ¡ÂÆ'D=!992fÌ7üTvF6ï:Œ÷_íøÿ_;¤RÌŸ?ÿëib\ò¼yó€º:ìÜ´é """"ãú)6 V®DŽ6ÇØ¡tÊ`Ñ`¼þúë;v¬±C!"""#Ú¸q#œ<°i§éä§bŠÅÊ+¡Íéøÿ_ó^y°µmˆ÷€˜’'""""""""2cL™1&‰ˆˆˆˆˆˆˆˆÌ€DDDDDDDDDfŒ @"""""""""3Æ ‘cˆˆˆˆˆˆˆˆÈŒ1HDDDDDDDDdƘ$""""""""2cL™1&‰ˆˆˆˆˆˆˆˆÌ€DDDDDDDDDfŒ @"""""""""3Æ ‘cˆˆˆˆˆˆˆˆÈŒ1HDDDDDDDDdƘ$""""""""2cL™1&‰ˆˆˆˆˆˆˆˆÌ˜¥± """""¢Þ¡®® P( ÑhÐÐеZÆÆF455A¥R¡¡¡õõõ½æ~ºãîwï9ï§T*¡R©Z}®±±R©´Õ笭­!‹[”‹D"ØØØ´ú©TÚêùd2,,,`eeKKKá8±X kkkXXX@&“ììì z QOaˆˆˆˆˆÈĨÕj444 ®® —îçúúzá»B¡hqL}}=[$ó uoÍÆÆB²M—ì’H$°²²jñZ¹\‰DÒêymmm[-·´´ìtÂL«Õ ÉÉû©T*(•ÊVŸ«¯¯o5AYZZ ­V+$8uIÉÆÆF466 ïIgÜŸ4”J¥°¶¶†µµ5ììì„ÇÖÖÖ°±±L&ƒµµµðÝÖÖ¶Å1DD­aˆˆˆˆˆÈêêêPSSƒššÔÖÖ¢¶¶Vx¬û~oòN¡P?·6²NG— ²²²ÒKY[[ÃÑÑQHI¥RÈd²N%óîMXQë´Z- €æ÷ؤ¡î5÷&j ***„ŸïMæ¶÷þßû~Ëd2áý¶±±ììì`oo¯÷¥+o-aKDæ @"""""¢‡ÐØØ¨—´«­­Euuu« ½{ß?ÊL"‘è%hììì —Ë[¦KîØØØèkkõ‘H$¼Ýõ~臵µµ-’†ºÑŸ÷' kkkQPP ÷»¨ÕjõÎ+‘HZ$Z$ïÿ=µ°àöD½€DDDDDD­hllDEEE‡_­M3Õ%ólmmakk ;;;xyyÁÉÉI¯ìÞÇr¹"‘È-%S#‹…ß‡ÑØØˆºº:ÔÖÖ¢®®NïwZWV^^ŽÜÜ\ÔÕÕ¡®®•••­&!—ËÛüngg''§‡Š—ˆ€DDDDDÔg¨T*TUU¡¼¼UUU¨¨¨@ee¥øÐ=®ªª‚Z­^'•J!—Ëáää¹\„„„ÀÙÙ¹ÕRLä‘)ÐmJÒ™Ä\SSS‹©ëº¿ÝßÕµk×PYY‰ªª*½‘®VVVBBP÷åää$ü]999ÁÑÑŽŽŽÝÑ\¢> @"""""2555())AII JKK…Ç%%%(//GuuµÞñ¶¶¶BÒÁÙÙ @¿~ýààà —¨àÔZ¢fºÑ~ÎÎΫÑhPUU¥—X×% ËËËqãÆ ”——£²²Ro]C±X ¹\¸¹¹ÁÅÅ®®®puu·µó3µŽ @"""""2z‰½{ ;»ŠD"Èår!i0bÄôë×ÎÎÎB²¯½Ýh‰èáYXX#ü|}}Û=V7Y—¬¬¬þ¶Ï;‡’’½Ýª[$]]]…„!7«!ÒÇ õõõõÈÏÏGAAŠ‹‹[$ùt£„,,,àìì,Üôûúúê%\\\˜Ü#2!º5 ===Û<¦®®NoT¯îú‘‘’’ÔÔÔÇÚÙÙé%ÝÜÜàææ†ÂÝÝb±¸'šEÔk0HDDDDD=J­V£¸¸ùùùÂWAAòóóQYY °´´nÜ]\\$$ø\]]áììÌx¢>F—$ôññiõy¥R)|ppï‡ÙÙÙHNNFEE€æ)ƺdàÀ1`À 0äF%d¶˜$""""¢nQWW‡¢¢"äååáÎ;(..~Öä³µµ…»»;<==777xzzbàÀLðQ§XYYÁËË ^^^­>ßÔÔ„ÂÂB½ëQVVâãã¡P(4¯qèîî///¸¹¹ ×§AƒqZ1™4&‰ˆˆˆˆèi4!77yyy¸{÷. PPP ¬×%“É„Q6£F¬Y³„Ÿ­­­Ü"ê+$I› ÂÊÊJ½ÑÈùùù8sæ Š‹‹…Áu 8ðòò‚··7w-&“À ¤¢¢¹¹¹B²/77wîÜAcc#D"ÜÝÝáááÐÐPL:U¸Q–ËåÆˆ¨]º¿CBBôÊï_²@— LIIvwtt„···\4h<==ùõ*L‘­V‹‚‚ܼySøºuë–°À¾““¼¼¼‚ÇÞÞÞðôô„•••‘#'"êZb±XX#0""B﹪ª*½E233¥R)|(âëë øùùÁÇLJ£Éh˜$""""êÃÔj5îܹƒ7nɾ۷o£¡¡b±žžžðõõŨQ£àíí oooØÛÛ;l""£sttİaÃ0lØ0¡L«Õ¢¸¸·o߯íÛ·qëÖ-=z¥¥¥š§ë’‚¾¾¾Ø9vì~üñGˆÅbøúú"$$¡¡¡2dw¦v1HDDDDd"*++…„_FF !‘Hˆèèh# €7DDfÂÂÂ>>>ðññÁ´iÓeee¸zõ*233qîÜ98pb±þþþ:t(†ŠÀÀ@H$#GO½ €DDDDD½TSS®]»†‹/ââÅ‹ÈËËnòÆ'Üäq£"¢¾£_¿~˜0a&L˜ yù‡ŒŒ ddd )) »wï†D"App0ÂÂÂ///#GMÆÆ Q/RTT$$üÒÓÓ¡T*áéé‰ððp,Z´ˆÓ¼ˆˆH“““^B°¤¤HKKþ}ûð¯ý ...B2pذaÉdFŽšz€DDDDDF¤Õj‘ÔÔT¤¦¦"??2™ C‡ųÏ>‹°°0¸ºº;̦[¼ž^O÷¥¹¾wìÇ®Åþì}\]]1yòdLž<999¸xñ".\¸€øøxXXX $$£FBdd$úõëgì©0HDDDDÔÃT*ÒÓÓ‘ššŠ³gÏ¢¢¢îîî=z4–.]Š   XZšöÕãââpêÔ)ÔÖÖbãÆmWZZŠ÷Þ{O=õ&Nœ¨÷\]]þïÿþO?ý´AõjµZ:tMMM8~ü8ðòË/ÃÂÂâ¡Ús¿´´4¨T*8;;ãÊ•+˜1cD"Q—Ö¡ÓÓ}ih}]ýصL©?“’’pðàAÀÃÃóæÍCxxøC×וz²?»‹……€y󿡦¦iiiHMMÅŽ;°}ûvøúúbôèшŒŒäTa3fÚÿ« """"2jµ—/_FRRRSSQ_____<öØc=z4¼½½b—š2e Ž= ­VÛîq–––pttl1­ùܹs8yò$’““……ï ±k×.TVVbéÒ¥Æþýû¡V«»4xäÈÀc=P(øþûï±hÑ¢.«ã^=Ý—†Ö÷°Ø]Ë”ú366—.]¤I“P\\ŒøøxüùÏÆ;#aÆ=T}]¥§û³§ØÛÛ Ó…›šš„£âââðã?bÀ€7n&L˜c‡K]ˆ @""""¢n¢Õj‘••…¤¤$$''£¦¦þþþøÕ¯~…Ñ£GÃÅÅÅØ!v±X ggg”””´{œ\.Çúõë[”GDD $$ÉÉɪ÷È‘#xâ‰'ÁÁÁîÔë;rþüy¤§§ã7ÞÊBCCqíÚ5ÄÅÅáñÇïÒú€žïKCë{ìÇ®eJýÙÐЀsçÎáÝwßFÓ7ï¾û.8ÐfÐÜûÓ$ ÂÃÃŽ¥K—"++ )))øù矱{÷nøúúb„ 7n§ ›&‰ˆˆˆˆºXii)~þùgœ8q%%%ðòòÂŒ30~üx¸»»;<“ÑÙiÐMMM¨ªªê¶)zõõõøê«¯°zõêÏÍœ9¯¼ò ÂÂÂzå{Ü›¦”³»–©õgvv6.\¨÷w___1²f¦ÖŸ]E$!((AAAxæ™gpåÊ$%%aïÞ½øî»ï0dÈ<úè£ˆŠŠâÎó&ª÷]½ˆˆˆˆˆLZ­ÆÙ³g´´4888 ::'NÄ AƒŒ^·HIIAzz:¤R)òòòàç燹sçB"‘è—‘‘}ûöáúõëð÷÷Ç /¼www¨Õj\ºt IIIËåxæ™g8–„„\ºt œœŒÂÂBôïß³gÏ68ÖŽŽ‰‡D"§§g‹ú­¬¬àçç‡={öàÅ_ìtü½©/ ©ªªª°cǸ¸¸ ´´ÕÕÕX¾|9ìííQ^^Ž“'O"11kÖ¬Á矎»wïâ£>BBBûÑÀ~€çž{‘‘‘°±±Ð<:­  +V¬ÀĉMî÷²­~666B;bªýi*D"BCCŠ%K–àâÅ‹HHHÀæÍ›±mÛ6Lœ8111fûo›¹êÚ•p‰ˆˆˆˆú˜òòrüðÃX¾|¹°0ýo¼Í›7cÑ¢Ef{ƒ‹ƒbñâÅxæ™g°bÅ $''ãƒ>Ð[£«ºº'OžÄO<¹sç"##ï½÷”J%ÊÊÊPVV†ÄÄD444øì³ÏP__¹sçbÙ²e(..ÆöíÛ¹¹¹HHHÀÝ»wqøðaDEEA.—C¥R±;Ñ0yòd¼øâ‹øõ¯iÓ¦¡´´ÁÁÁ˜0aóø½Ôh4ÈÍÍm±éJkLµ?M•¥¥%F…U«V᫯¾Âœ9spéÒ%üö·¿Åþðœ8qMMMÆ“ À Ѹyó&þú׿âå—_Ɖ'0uêT|ùå—xûí·1f̈Åbc‡Ømt#l¦N*´ÓÞÞsæÌ¦éXZZâÅ_DXXfΜ‰ùó磢¢Ç‡››bbbºµ¯ ‰Õc´Z-rrr`gg×f]r¹ …wîÜéÒøtzº/Û«OçÞ···7rssaaa ‚F£F }øá‡ËåìÇNô£V«ÅŒ3„ç¾ýö[h4,]º"‘Èl~/SSSáããƒÉ“'w›)ö§¹pttÄ/~ñ üå/Áš5k0pà@lݺ¯¼ò öìÙƒššc‡Hí`ˆˆˆˆ¨.\¸€µk×â­·ÞÂíÛ·±lÙ2|ùå—˜?¾Yoêq¯ììl(•Ê툈¤§§ e÷O鋎Žܸq@óT³®Ü¡÷Ab5ä˜ÚÚZ¨Õêvº¶VUUui|÷Ÿ_§»û²£úV¯^9sæ ©© ÇÇõë×…QX@ó¦ b±ýû÷ÊØëG‘HWWWÀ™3gpáÂÌœ9^^^Ì£?ëêê°gϬX±Â õ;M±?ÍH$Â!C°bÅ |ùå—ˆŽŽÆÁƒñÒK/á믿FQQ‘±C¤Vp @"""""\ºt ?þø#²³³†wÞyÆ ë¶ 'z3Ý.œµµµzåööö°²²BEEE›¯urr‚T*Eccc·Æ¨cH¬†£K`´7íÏc$¾¶ôt_Þ_ŸF£ÁþýûqãÆ LŸ>þþþÈÎÎn÷ìÇëG¥R‰íÛ·ÃÅÅÿó?ÿ#”›Cnß¾‹/†£££ÁñµW_oìOsæää„§Ÿ~sçÎʼn'pðàA?~“'OÆÜ¹sûÌc¦€ @""""¢v\¹r?þø#®^½Š°°0|øá‡ð÷÷7vXFåææmŽòðððh÷õ"‘HqÓÝ ‰Õclll ‘HPWW×f]ºd‰\.ïÒøÚÓ“}yo}Z­ëׯ‡ƒƒV­Z y“”ްõë3´wíÚ…ÒÒRüö·¿…•••Pnêý‡ÈÈH 2ÄàØÚ«¯·ög_`ee…ÇS¦LÁÉ“'±{÷n$$$ &&sæÌ³³³±Cìó8˜ˆˆˆˆ¨eeeØ´iÞÿ}h4¬^½o¿ývŸOþÍ‹àËd2¤¦¦ê•—••A©TbÔ¨Qm¾¶¤¤*• cÇŽ}è8î݈àab5ä‘H„   vG>UWWC&“u*‘dJ}yo}ׯ_GZZBCC…çÕju‡ça?v¾ïܹƒƒ"""£GÊÏ;gÒý™””©Tª×&¸|ù²ðØ\ú³/‹Åxä‘GðùçŸcÙ²e¸xñ"^}õUìܹ“›…€DDDDD÷hjj®]»ðꫯ"''üãñ§?ýI¯³··Ç¢E‹™™©w³~øðaDGG }eaaÚÚZaý-­V‹]»vaÞ¼yŽ  ¼®µ©v±±±xþùçq÷î]¡¬¬¬Lïu†Æjh{&L˜€ÌÌÌ6“YYYˆŠŠ¦~óÍ7x÷ÝwQXXØf{k_Z_BBrssñóÏ?#//UUU¸}û6ªªªÐÐÐFÓb4û±sýø÷¿ÿ–––xþùç…ãÕj5Ξ= À4ûóÂ… 8|ø0T*Ž=*|}óÍ7Bÿ™SöEb±ÑÑÑØ¸q#~ùË_âÀXµj.\¸`ìÐú,N&""""ú¯k×®áË/¿DUUæÍ›‡'žx––ü/sk¦N ¹\Ž 55¶¶¶ËåX¸p¡pÌÓO?½{÷âÓO?…««+¤R)Fމ1cÆtxþK—.!!!––†cÇŽ!""NNNšÖÖÖÂŒ7oÞħN‚——FŽ ƒb5ä˜èèhìß¿ÙÙÙ Ô‹·±±™™™X·nPVZZЬ¬,ÄÇÇë§·÷¥!õ`êÔ©HJJ–-[ð«_ý ‹-¦M›°{÷n!-- Z­Û¶mÃO<___öc'û1<<ðððÀ¿ÿýo@CC®]»†“ìÏœœlذ-Öæ“H$زe‹Yög_%‘H0gÎLš4 ÿüç?±~ýzŒ3¿ùÍo`oooìðú‘Ö1µm˜7oµg÷é IDATPW‡›6ueLDDDDÔƒ~ŠÅ‚•+‘£Í1v(2X4¯¿þz—LÛS«Õعs'öíÛ‡ððp,]º”ëõ §Ÿ~³gÏÆ‚ ŒJ‡rrr°{÷nüîw¿Ó+ÿñÇaccƒY³fé•_½zYYY˜={vÄg*}É~ìZìÏ®ÕÛûÓ”]¾|›7o†J¥ÂK/½„#FtÉy7nÜ'O'lÚi:ù©ØŸb±rÁJhs:þÿ×¼W^lm±sçήS€‰ˆˆˆ¨O+**Â»ï¾‹ØØX,Y²o½õ“=H¡P@¥R ›ôvƒÆ„ +”]¸p*•ªER ¾¾çÎÃc=Ö#±™R_²»û³kõæþ4uÆ ÃÇŒ|øá‡øÇ?þµZmì°ú&‰ˆˆˆ¨ÏÊÎÎÆ;ï¼µZ>úS§N5vH}Bii)>ù䡬¬ ¶¶¶ˆŠŠ2vX7n<==qöìYܾ} …¢Õ©¹¹¹X°`d2Y·ÅbÊ}É~ìZìϮ՛úÓÜØÚÚâÕW_Å+¯¼‚øøxüùÏF}}½±Ã2{\Єˆˆˆˆú¤ÔÔT|ñÅÆo¼Á›·$“ÉPSSƒ÷ßaaaxçwL®ÿï¶6hРV êö8L½/Ù]‹ýÙµzKš«‰'bРAX¿~=Þyçüဋ‹‹±Ã2[LQŸ“œœŒÏ?ÿ111xþùç!‹RŸbkk‹µk×; ³À¾ììÇ®Åþ$Cy{{cíÚµX¿~=V¯^uëÖ ëP×â`""""êS®^½ŠM›6áñÇÇÒ¥K™ü#""2"WWW¬Y³R©~ø!§w&‰ˆˆˆ¨Ï¸{÷.>þøcŒ9‹/6v8DÝ®§o¤z´>sÆ$õ%öööxûí·Q]] 6@£Ñ;$³Ã)ÀDDDDÔ'hµZlÞ¼ýû÷ÇÊ•+!‰ŒQ·‰‹‹Ã©S§P[[‹7¶y\ii)Þ{ï=<õÔS˜8q¢Pž””„ƒ¢  ˜7oÂÃÃÛ|8¾ýö[ÄÇÇcÀ€øÍo~ƒÐÐP”––â/ù œ±jÕªë.//ÇÉ“'‘˜˜ˆ5kÖàóÏ?ÇÝ»wñÑGµKjj*Ο?;;;(•JTTTè=‰DOOϯµ²²‚ŸŸöìÙƒ_|±=HÔ{7Û·oÇéÓ§cìpÌG‘Ykjj•+WeìPˆºLll,<ˆÅ‹ã™gžÁŠ+œœŒ>ø@oÝ¿êêjœP¬÷& ïÞ½ +++,[¶ìÎ¥ó0ío-¾èèhìÞ½•••(,,„¿¿?,,,˜˜ˆ)S¦ %%+W®ìòºæ>ÚßD— moz¯!Ç™š¦¦¦N}8@íã@""""2kº‘:=±ƒ)QOpss€6w}Õm¸Ñ‘H//¯뉋‹Cdd¤ÞtÔ‡!•JQ^^޲²²ÏUWW|ž‡mÿý&Ož ­V‹S§N!..Ó§OGtt4®]»†ôôt!iÚuëºÄbklll ‘HÚÝ U—”Ë媟¨7+,,l1Ú–€DDDDdÖáîîŽË—/;¢.™L&¬[§SVV¥R‰Q£FµùÚ’’¨T*Œ;¶Ý:’’’ •J1zôh½ò{ÿŽ ÝlCÇÛÛZ­ßÿ½^yQQâââ >Ïô¿5®®® ÁáÇ!•Jáää„ÈÈHX[[ã‹/¾Ð[û°«ëÖ­MúŸÿüG¯\£Ñ£ùD"‚‚‚Ú]X]] ™LfPb—ÈhµZ¤§§#00ÐØ¡˜ &‰ˆˆˆÈìM˜0 œGfÁÞÞ‹-Bff¦^BîðáÃˆŽŽv¯µ°°@mm­°Ã®V«Å®]»0oÞ¼vGª]¸p‡†J¥ÂÑ£G…¯o¾ùF˜²‹çŸ^øh…¦P(ô¦ÛWUUA¥RA©Tbøðá}:òòò:Ý÷ñññHKKƒV«Å¶mÛðÄOÀ××·Õö…‡‡ãw¿û¶oߎ;wbß¾}xä‘GµZ H¥RDGGcÿþýÈÎÎn1"ª±±™™™X·nݵ¨7:räüýý[lCN¤íì¸í{Ì›7¨«ÃÎM›º2&""""êA?ÅÆbÁÊ•ÈÑæ;”N,Œ×_½Ã©Œ:Ÿþ9²²²°qãFXYYustD¦ãé§ŸÆìÙ³±`Ác‡BíÈÉÉÁîÝ»ñ»ßýN¯üÇ„ fÍše¤ÈˆºÖåË—±nÝ:¼ùæ›-–!hËÆáäé„M;M'?ûS,V.X mNÇÿÿš÷Ê+€­-vîÜùÀõq õ ‹/Æk¯½†Ÿ~ú Ï<óŒ±Ã!êtSvu›[˜‹%K–txÌK/½„ˆˆˆˆ¦k <&L@ll,žxâ ÍÓµU*“d6ñõ×_#22Òàä† @""""êñì³ÏbóæÍð÷÷7xä ‘¹)--ŶmÛðì³Ï¢±±¶¶¶ˆŠŠ2vX]êïÿ»±CèãÆCZZΞ= WWW(ŠžöLÔÛhµZlÚ´ 555xþùçŽÙaˆˆˆˆúŒGy¹¹¹øë_ÿ ;;; 6ÌØ!õ8™L†šš¼ÿþû Ã;ï¼™Lfì°È@#FŒëv&2ÿüç?qöìYüဓ““±Ã1;LQŸòì³Ï¢¼¼7nÄ[o½…àà`c‡DÔ£lmm±víZc‡AD yäßO?ý„C‡áµ×^ã‡sÝ„{„QŸ"‰ðÊ+¯ 44úÓŸœœl숈ˆú$•J…¿ýíoØ·o–-[Æå9º€DDDDÔçH$¬Zµ S§NÅgŸ}†ýû÷C«Õ;,¢vÕ××;„N3ŘuŒ‚Ù0åßê>555X¿~=Μ9ƒßÿþ÷xôÑG’Yã`""""ê“D"/^ 777üë_ÿBzz:^zé%®;ÔhµZ:tMMM8~ü8ðòË/âkÇG¤¥¥A¥RÁÙÙW®\ÁŒ3 ‰:}ž¸¸8œ:u µµµØ¸qc—Æx?SŒY§«bOLLĉ'——‡­[·>P,jµYYY¸xñ"BBB0bĈVËL•¡m9zô(’’’PTT„Í›7·x¾«Þ32=—.]—_~ ±XŒµk×r=ËÀ€DDDDԧ͘1ëÖ­Cqq1Þ|óM¤¦¦;$êf»víB~~>ž|òI¼ôÒKP(P«Õ]ZÇ‘#GPTT„ˆˆøúúÂÇÇßÿýkÊ”)P(Ý>JÕcÖéÊØÇFóP¿9998qâöîÝ‹²²²6ËL•¡m‰‰‰V«…F£iñ\W¾gd:šššðý÷ßãƒ>@PP>þøc&ÿz€DDDDÔç <Ÿ|ò &L˜€O>ù}ôJJJŒu“#GŽÀÕÕŒ·Þz ‰¤ËÎþüy¤§§ã±ÇÊBCC!“É×éó‰Åb8;;wY|­1Řuº:v ‹‡Ž=00Ó¦Më°ÌTÚ ôëׯEyW¿gdÒÓÓñÖ[oáÈ‘#Xºt)Þxã ØÙÙ;¬>ƒ @"""""R©Ï=÷Þ~ûmäççã7ÞÀÞ½{ÑÔÔdìШ 555¡ªªªÛ¦Ö××㫯¾Âüùó[<7sæLìÚµ EEEÝR÷ƒ2Řuzsì––-WÜj­ÌT=h[zó{FÝ£¤¤Ÿ|ò Ö®] lذS¦L1vX}Žù\}ˆˆˆˆˆº@XX>ýôSý’¿ß---úç?ÿ©={öÈn·kÅŠJKK“tr.É?üP‡ÒÁƒ«%K–(%%¥ßÇ æÚ;—;vè±Ç“ÉdÒC=¤ââb½ð *//WJJŠ~ñ‹_hìØ±jjjÒßþö7 6L>øày}®ëæl¾þúk}óÍ72dˆ¼^¯š››¿Ôç G{{»Þyç•••)99YøÃT\\ê²®XôNc±X4þ|=õÔS5j”V®\©‡zH›7ofµàAlÆŒºë®»$I“&MÒÒ¥K𯬬Lï¾û®îºë.ýô§?Õý÷߯M›6é/ù‹q΃ióå—_*??ÿ¬5Œ5J›7oîsN´óikkÓ§Ÿ~ªn¸A ,PEE…þô§?ÉëõJ’žzê)y<-X°@K—.UCCƒ^|ñEIRMM>ùä}÷ÝwZ·n¦L™¢ÄÄDuwwØš%iæÌ™Z¶l™î¼óNÍ™3GMMM*,,Ô´iÓ$]Ú÷»³³So½õ–/^¬G}TuuuúÏþc<þÖ[o)**J÷Þ{¯þüç?Ëåréá‡6^[°‚¹®Î窫®Rii©L&“Æ'‹Å¢{î¹GÇWrr²ÆŽ+IJNNVrr²î¸ãŽ Ž}®ë¦/7nÔ›o¾©»ï¾[‹/ÖÂ… åt:Ú\Ês†¡££C«W¯ÖòåËõá‡jÑ¢EzüñÇ ÿBŒ8‹áÇkùòåzâ‰'”‘‘¡'Ÿ|R¿ûÝïørfü½Ð®½öZ™ÍfIR||¼æÏŸ¯={öhãÆAµñù|Ú¿ÿ9ç´JLLTGG‡jkkû]gdd¤–-[¦ñãÇëÆoÔm·Ý¦ææf}ôÑGF›S'ÓÏÌÌTMM¤“=[ ÔÛÛ«éÓ§ëšk®Ñ_ÿúW%&&Øš}>Ÿ®¿þzã±^xA½½½º÷Þ{e2™.ùûm6›uÇw(55U™™™ÊËËÓ$IÍÍÍ*++ÓøCI'纛2eŠZZZ´uëÖ Ìu¬Ù³g«»»ÛXÈÈl6kÊ”)úöÛoåv»%ßÛÛ+‡ÃÔ±ÏvÝôµZº×ëÕË/¿¬ë¯¿Þ蹯ѣGm.õ9Chµ··ë7ÞÐòå˵víZÝtÓMZ¹r¥nºé¦°þ>Xq€óHKKÓ< ùóçkÍš5zòÉ'e·Û5wî\Íš5KV«5Ô%â{Ø·oŸ¼^¯’““¶—””H:9q½Õj=o›ñãÇ«§§çœá†(kkkk¿ëôïë7cÆ ý÷¿ÿ5B©‡~XÒÉç³Ï>Suuu@2³Ù,³Ù¬#FÛÚÛÛlÍ&“ÉX¬å«¯¾Ò¶mÛ4oÞ=öØcJHH0w(//?ïþƒ¡æ5kÖ¨©©I¿ùÍoÒÚý¢¢¢är¹tìØ1 ><à±¶¶6%$$õ<Á\Wý1sæL­Y³FŸþ¹ª««õ³ŸýL»wïÖÊ•+µ{÷n#€½ÇöÿרØxÖ…PByÎpq´´´è“O>ÑG}¤úúz=Z>ø &NœÈBYs(..N7ß|³žyæ=ôÐC2›Ízê©§´lÙ2½üò˪«« u‰¨Q£dµZ¹ÓüŽ;&¯×««¯¾:¨6&“Içì9ÕÖÖ&«Õj cý>ÕÝÝ­ÒÒRUWWkÇŽÆb’ÔÓÓsÞE$z͵µµz÷ÝwURR¢‰'Û·nÝzÙk?Uff¦|>Ÿ^yå•€íGÕûï¿ôós]õ‡ÍfÓ˜1c´nÝ:EEE)))I“&MRLLŒž~úiÍœ9ó’Û?—ã_|°½··×èÍÊs† ×ÛÛ«mÛ¶é‰'žÐ²eËôæ›ojüøñzüñÇõè£jÒ¤I„ƒ ð=™ÍfMœ8Q¿ÿýïõ÷¿ÿ]7Þx£6oÞ¬x@¿þõ¯µzõj544„ºLèd¸!)`¥ÖøøxÝqÇÚ»w¯víÚel_·nf̘¡±cÇÕF’¦M›¦½{÷ž5x«ªªÒ”)SŒaŽÿú׿ôÇ?þQGŽ9gÝjoo7êöù|Z³f.\ÐSë“O>QMM>þøc9Nµ¶¶êðáÃjmmÕ‰'ÔÛÛ{Fï«\óóÏ?¯ÈÈHÝ}÷ÝFûžžmÙ²å’ÖÞÞÞ®ŽŽŽ€Õn[[[ÕÝÝ-¯×«ââbåææjãÆzâ‰'ôÙgŸéý÷ß×sÏ=§9sæH’<¤ÀkíômÁ^Wý1kÖ,544hîܹ’¤èèhM™2EñññÊÍÍ5Ú{ì³]7§¿–‚‚;V6lÐúõëåõzµÿ~UVVª­­MŸþ¹¼^o¿ÏBÇétê•W^ѲeËôØc©±±QK–,ѳÏ>«ŸÿüçgÌ÷ˆÍüÈ#­Y·N¿|ä—¡.¥_ž~ôi•––¸^"V«U………š;w®F-·Û­ 6èí·ßÖž={ÔÓÓ#»ÝÞ¯…pqÅÇÇK’Ö¯_¯ŠŠ EEE©¸¸ø¬µgffÊåriÆ ª¬¬ÔîÝ»U\\¬k¯½VÒÉ«[ZZ´sçNíÛ·O“&MÒ¸qã´uëV555©££C6l×ëUss³l6›±’ë@­Y’Ö®]«#FÈívkçÎúꫯ´jÕ*9M˜0á’Ôþé§Ÿª¼¼\^¯WGÚ¸q£>úè#uvvÊëõª¨¨HS§N•ËåREE…¶oß®èèh-Y²DC‡UuuµÖ¬Y£#GލµµUÉÉÉr»ÝglKII êºêÔÔTy<Íš5ËØf³Ù”šš°â²tþkº¼¼\~øá×M_¯/%%E“'OVKK‹ÊËËõÁ(&&FIIIÊÊÊR~~¾RRR”••Õ¯s†ËËétjݺuúÇ?þ¡7ÞxCíííš9s¦–.]ª›o¾Y999rŠ‹M›6Éš`Õõ· ž|j_Å>­[³NüòüÿþZ½v­¥Ûn»í‚gò¯Oø9,\¸Pr»µzåÊ .¡õZY™­X¡ý¾ý¡.¥_rM¹úÕ¯~¥ÒÒÒP—r^]]]Ú¹s§6mڤ͛7«««Kùùù*--ÕäÉ“ÏøŒÁmÿþýzýõ×õÛßþ6`û«¯¾ªØØXÝtÓMÛ¿ýö[UUUiÞ¼y—³Ìƒ±f¿Á\û•ª¿ç —–ÓéÔ—_~©M›6©¶¶VÆ ÓäÉ“UZZªÂÂÂP—”'Ÿ|RIéIZ¹zðäSe¯•iÅ¢òí?ÿ¿¿._.ÅÅìˆw¿êç;IDAT^l „‹Å¢’’•””hÉ’%Úºu«¾þúkýïÿÓK/½¤œœMš4I“&Mê÷Äûxrss5mÚ4•••é†n$mÛ¶MÝÝÝgG[·nÕ‚ BQªa0Öì7˜kÆ’%KÎÛæ¾ûîSIIÉe¨æâèÏ9ÃÅ×ÓÓ£ŠŠ }õÕWÚ²e‹\.—ìv»&Nœ¨{ï½W………Ì醀ËÈjµjÚ´iš6mšºººTYY©-[¶è½÷ÞÓªU«d·ÛU\\¬¢¢"?^V«5Ô%ãL:U;vìЖ-[d³ÙÔÑѡŋŸÑ®¦¦F‹-’Åb A•cÍ~ƒ¹öóyþùçC]Â%ì9ÃÅÑÚÚªíÛ·ë›o¾ÑÎ;åv»•žž®éÓ§«¤¤D„~aދŢ¢¢"éÎ;ïTUU•¶mÛ¦íÛ·«¼¼\‘‘‘=z´Æ¯ñãÇ+===Ô%£®ºê*ãçÓç^ó+((¸\åe0Öì7˜k¿RsÎpaüÿÁ´}ûvmß¾]N§SÑÑÑ3fŒ-Z¤ &Èn·‡ºL\F€À¡ÂÂBê'?ù‰ÚÚÚTQQ¡]»véí·ßÖ¿ÿýo :TcÆŒQQQ‘ Ü(€ÐèííÕ¡C‡´k×.UVVjÏž=òx¬]»viçÎzöÙgÕÕÕ¥¤¤$ª  @999ÊÏÏ—Ùluù€ïÉëõêàÁƒÚ»w¯*++UYY)·Û­øøx5J·Ür‹ŠŠŠ4räHæòCŸ€AÆl6+''G999š7ož¼^¯ª««/…¯¾úª<¬V« TPP Q£F)77W±±±¡.p Ú·oŸªªªTYY©ššõôôÈf³©°°P·ß~» •žžNà‡ ƒ\tt´ÆŽ«±cÇJ:94Ìétª²²R{÷îUyy¹^}õU™L&¥¦¦*//ϸeggÓKBÈív«ºº:àÖÚÚ*³Ù¬¬¬,êæ›oVaa¡’’’B].)@ ÌDDD(++KYYYºîºë$IÍÍÍ_.W­Z¥ŽŽY,egg+77W#GŽTvv¶222ÉW¸ØÜn·<¨ƒêСCª®®Ö‘#Gäóùd³Ù”ŸŸ¯yóæ)//O#GŽTttt¨KF˜à·:pHJJÒĉ5qâDI’ÏçS]]îß¿_ü±¼^¯Ìf³233•­ììl9RYYY²Z­!~0x;vL‡ ü%I‰‰‰9r¤¦NjôÈ:thˆ+F8#®@&“IiiiJKKÓŒ3$:ÜØØ¨ÚÚZ8p@Ðo¼¡ÖÖVI'CÄôôt¥§§+''GÊÈÈÅb åK€òx<ª¯¯—ÓéTmm­jkka¼ÒÉ¿;srr4sæLåää(==]‡#ÄUãJC@ÒɡÇC‡C%%%ÆöÆÆF:tH555ª©©ÑÎ;µ~ýzõôô(22RiiiÊÌÌTff¦RSS•šš*‡ÃÁ0baÅív«¾¾^uuuª­­UMMœN§Ñ«Ïjµ*##C™™™ºå–[”••¥ììl_€Àodçd³Ùd³ÙŒáÃ’ÔÝÝ­ÚÚZ9N#\¿~½šššäóùd6›e³Ù”’’b„‚)))JIIÑðáÃCøjà캻»uôèQÕÕÕ©®®NGŽ1~ö÷è³X,JMMUzzº®½öZ#ô³Ùl!®8;@ýiÌxªîîn;vÌwôèQÕÔÔhãÆ_ž‡222d·Ûåp8”žžÎ<ƒ.·Û­£GÃvät:U__¯žžIR\\œÒÓÓ•‘‘¡ &Èn·+==]iiiŠˆˆñ+ú‡ÀEÙç0bIjmm5zÒÔ×׫¾¾^_ýµÔÝÝ-éä\YþÞ‚v»]6›MÉÉɲÛíJLL”Éd ÅË0Ètuu©©©IMMMjllTcc£Ñ›¯¾¾^'Nœ$ÅÆÆ=•ðƒôVf^„@—ÅСC5tèP=:`{OOÂÁºº:íØ±C.—Ëèc±X”œœlÜüC“ív»’““5lØ0™ÍæP¼4—™×ë5‚=ÿíÔ°¯¥¥E>ŸO’#›Í&‡Ã¡¢¢"ýèG?2¦&`å]\)„”ÙlÖˆ#4bÄM˜0!à±ÞÞ^¹\®>¿äWVVª©©I]]]Æó 6, ôßü¡!+ƒƒÛíôþ|üøq£m\\œñYÏËËSiiiÀg?>>>„¯ XFxwzÏA?ÿ\^GUss³š››ÕÐРmÛ¶©¾¾^Çhk±X4lØ0%&&žq?dÈ%%%)))‰áÆÀ%âv»år¹äv»Ïëé7—Ë¥ŽŽcŸ¸¸89%%%)''G¥¥¥Æü¡‡Cqqq!|EÀà@`P‹‹‹SNNŽrrrú|¼­­Mr¹\r¹\jmmUss³ZZZ´wï^577«­­Íj,IÑÑÑFè¿÷‡…C‡5î>œ\ü§µµU.—K---ÆçËßÒÒ"—ËÕççìÔÏXzzºÆg„òþÞ¼QQQ!|u@x Ö” ÜÜܳ¶ñù|jmm5BŒÓï8`Æ~f³Y 2dˆq‹×!CŒíþ?Ÿúxd$_Å008qBííí:~ü¸qkoo7n§oóÿ|ª!C†{©©©g„çÆ cÕoà2â·€+žÉd2zùeee³mGG‡Ñ³Éår„ Ç×±cÇtèÐ!cÛ©Cý¬V«ž úÃBÿ}ll¬bbb£¸¸8ÅÄÄâ¼Nœ8aÜ:::äñxÔÑÑqF€×WÀçŸSÓÏd2q]&$$(55UñññŠWbb¢Œž{̵ <üæ€~ˆUll¬ÒÒÒ‚jßÓÓ¸ôÕ“êøñãr:ŸÄøEFF‚111²Z­²Z­¡¿Õj hçß/**ŠXDoo¯<ÏÁ]GGGÀ¶ööö>ý'N÷n·û¬ÇñÏþànÈ!ÊÌÌ<#à;µ óëá.!³Ùlô.ìSCÇ#·Û}Þ€¨¡¡¡Ï6>ŸïœÇò‡<±±±ŠˆˆPtt´"##%‹Åb„ŽFhxú>1112›Ígìs:ÿã§;õ¹Ow¶žþ㞺`Äéïaww÷ÛýÛÙö9už:?ÿ0WÇ£ÞÞ^y½^uww«³³Sêîî–×ë xî`÷9‹ÅÒgÀkµZåp8ÎðúoþÀÚjµÒ{¸‚ñé€Èà\ ^¯×ýA¢›Ïç34ï±ŽŽŽ€Àª««Ë­ÚÚÚ‚Ú'\tFEE)22RÑÑÑF€i2™ŒpÔn·Ëd2½¿Wæ©=<Ífsˆ_1€p@a.::ZÑÑÑ—uÕbhx:Pxºsõ†;Û°V(éï…x:ÐÖ—³ mõ‡t§‹•ÉdêsèÅbés¨/sÊÀåwæ‘€@# ƀ0F„1@ ŒE†ºàBUUU…ºbÇŽSRzR¨ËÐ0h•••©¬¬,Ôe€Ë»*/Ô% h€”ª{«C] L&S¨KÐ0(ñ}€à°Æ€0F„1@ ŒaŒc€@‹ uî¿íþP—pE¨¯­¿¬Ç#¸Âe¤¦jÁœ9ÒñÞP—pEÈêPΜ9—íx€W¸Òÿû?­yæ™P—€K„9€0F„1@ ŒaŒc€@# Æ"¿ï¬Y·N¦ÜÜ‹Q €ÓÜzë­ßk“Ïçó]èÎ_|ñ…œNç÷*ÀÙeddhêÔ©¼ÿ÷  lÌ„1@ ŒaŒc€@# ƀ0F„1@ ŒaŒc€@‹”´:ÔE¸4þ…]§^ùbGwIEND®B`‚fungw-1.2.0/doc/lang_funlisp.html0000644000175100017510000000103213337417125015156 0ustar svnsvn

fungw language bindings - funlisp

datasheet

tested on Debian ( funlisp 1.1.0)
known bugs/limitations none
API stability stable
configuration requirements nothing special

Details

Funlisp is a minimalistic lisp interpreter implemented in about 3k sloc. Its focus is simplicity and portability. fungw-1.2.0/doc/example/0000755000175100017510000000000014047742763013256 5ustar svnsvnfungw-1.2.0/doc/example/15_c_call_script/0000755000175100017510000000000014047742763016364 5ustar svnsvnfungw-1.2.0/doc/example/15_c_call_script/Readme.txt0000644000175100017510000000024714034053644020312 0ustar svnsvnDemonstrates different ways to call a fungw registered (potentially script) function from C. Build considerations: static linking, as explained in example 10_script. fungw-1.2.0/doc/example/15_c_call_script/avg.fawk0000644000175100017510000000026314032643074020001 0ustar svnsvn# Calculate the average of a and b function avg(a, b) { return (a + b) / 2; } function main(ARGV) { # register avg(), so it can be called from the C code fgw_func_reg(avg); } fungw-1.2.0/doc/example/15_c_call_script/Makefile0000644000175100017510000000041014034223674020006 0ustar svnsvnall: calls #CFLAGS_DBG = -g -Wall include ../Makefile.conf calls: calls.o $(CC) -o calls calls.o $(LDFLAGS) $(LIB_FGW) $(LDLIBS) $(FUNGWBIND_SRCLIBA) calls.o: calls.c $(CC) -c $(CFLAGS) $(CFLAGS_DBG) -o calls.o calls.c clean: -rm calls calls.o 2>/dev/null fungw-1.2.0/doc/example/15_c_call_script/calls.c0000644000175100017510000000724614034053752017624 0ustar svnsvn#include #include /*#include #include */ #include #include #include double wrap_avg(fgw_ctx_t *ctx, double a, double b); /* Import fawk init; this relies on two things: - fawk is always available in fungw, no external dep (well, the user can disable it in ./configure that'd lead to link error here) - fawk is in the big static lib (.a) of all languages and if we call its init, all relevant objects will get linked */ extern int pplg_init_fungw_fawk(void); int main(int argc, char *argv[]) { fgw_ctx_t ctx; fgw_arg_t res, arg[8]; fgw_error_t err; fgw_obj_t *sobj; fgw_func_t *func_avg; char *sres; /* Initialize and register the script language engine fawk */ pplg_init_fungw_fawk(); /* initialize a context */ fgw_init(&ctx, "host"); /* create objects for the script: loads script and runs its main() */ sobj = fgw_obj_new(&ctx, "avg_script", "fawk", "avg.fawk", NULL); assert(sobj != NULL); /* Get a function handle knowing the short name of the function */ func_avg = fgw_func_lookup(&ctx, "avg"); if (func_avg == NULL) { fprintf(stderr, "Error: failed to find global function avg\n"); return 1; } /* Manual call: no helper, just raw call to the function pointer */ arg[0].type = FGW_FUNC; arg[0].val.argv0.func = func_avg; arg[0].val.argv0.user_call_ctx = NULL; arg[1].type = FGW_DOUBLE; arg[1].val.nat_double = 42; arg[2].type = FGW_DOUBLE; arg[2].val.nat_double = 71; err = func_avg->func(&res, 3, arg); printf("direct: err=%d rt=%x rv=%.2f\n", err, res.type, res.val.nat_double); /* Dtring based call. The callee gets each argument as a string (in this particular case the callee will convert them to double within fawk by doing arithmetics on them). Result is always an allocated (char *) string (so fungw will convert the script returned double to a newly allocated string). This slow calling convention may be useful if input and output is string based for some reason, e.g. when implementing a CLI. */ sres = fgw_scall(&ctx, "avg", "42", "71", NULL); printf("scall: sres='%s'\n", sres); free(sres); /* Type/data based call, using generic vararg wrapper; this saves all the unnecessary string conversions in this example. Of course any argument may be passed as a different type, even a string, if that's what is available at a time - the script binding code or the script will do the necessary conversion. Since this is a script, we shouldn't rely on the return value type being double - rather convert it. If it is a double, the conversion is a noop. Note: in vararg calls, numeric args shall be explicitly casted: without the (double) cast here, these numbers could easily pass as integers causing wrong readout on the other side! */ err = fgw_vcall(&ctx, &res, "avg", FGW_DOUBLE, (double)42, FGW_DOUBLE, (double)71, 0); fgw_arg_conv(&ctx, &res, FGW_DOUBLE); printf("vcall: err=%d rt=%x rv=%.2f\n", err, res.type, res.val.nat_double); /* Use local wrapper so the call feels native in C */ printf("wrap: %.2f\n", wrap_avg(&ctx, 42, 77)); /* clean up and exit */ fgw_uninit(&ctx); fgw_atexit(); return 0; } /* Emulate a native C function: use the vararg based shorthand and convert the result to double. The drawback of this overly simple API is the inability of error reporting. Of course the code could check the return value of fgw_vcall() and fgw_arg_conv() and report error. */ double wrap_avg(fgw_ctx_t *ctx, double a, double b) { fgw_arg_t res; fgw_vcall(ctx, &res, "avg", FGW_DOUBLE, a, FGW_DOUBLE, b, 0); fgw_arg_conv(ctx, &res, FGW_DOUBLE); return res.val.nat_double; } fungw-1.2.0/doc/example/Makefile.ex0000644000175100017510000000025114034216102015302 0ustar svnsvnEXAMPLES=\ 00_hello \ 10_script \ 15_c_call_script \ 30_lnk_sys_pup \ 31_lnk_sys_dl \ 32_lnk_sys_dyn \ 60_engine_c \ 80_multicall \ 82_ucc \ 87_custom_type \ fungw-1.2.0/doc/example/index.sh0000755000175100017510000000055314034216102014702 0ustar svnsvn#!/bin/sh first_para() { awk ' /^[ \t]*$/ { exit } { print $0 } ' } gen_index() { cat index_pre.html echo "EXAMPLES=\\" >&3 for n in * do if test -f $n/Readme.txt then echo " $n

" first_para < $n/Readme.txt echo " $n \\" >&3 fi done cat index_post.html echo "" >&3 } gen_index > index.html 3>Makefile.ex fungw-1.2.0/doc/example/00_hello/0000755000175100017510000000000014047742763014660 5ustar svnsvnfungw-1.2.0/doc/example/00_hello/hello.tcl0000644000175100017510000000023314032255146016451 0ustar svnsvnproc hello {how_many who} { puts "Hello $how_many $who" puts "atoi: [atoi 42]" return 66 } puts "hello world from init [atoi 32]" fgw_func_reg "hello" fungw-1.2.0/doc/example/00_hello/hello.js0000644000175100017510000000050214032255146016302 0ustar svnsvn// Public function; atoi() is provided by another objects through fungw function hello(how_many, who) { print('Hello', how_many, who) print('atoi:', atoi("42")) return 66 } print('hello world from init', atoi('33')) // register hello(), to be called from other objects and the host application fgw_func_reg('hello'); fungw-1.2.0/doc/example/00_hello/hello.pl0000644000175100017510000000055714032255146016313 0ustar svnsvn# Public function; atoi() is provided by another objects through fungw sub hello { my($how_many, $who) = @_; print("Hello " . $how_many . " " . $who . "\n"); print("atoi: " . atoi("42") . "\n"); return 66; } print("hello world from init " . atoi("33") . "\n"); # register hello(), to be called from other objects and the host application fgw_func_reg("hello"); fungw-1.2.0/doc/example/00_hello/hello.bas0000644000175100017510000000053214032255146016436 0ustar svnsvnREM Public function; atoi() is provided by another objects through fungw function hello(how_many, who) fawk_print("Hello", how_many, who) fawk_print("atoi:", atoi("42")) hello = 66 end function fawk_print("hello world from init", atoi("33")) REM register hello(), to be called from other objects and the host application fgw_func_reg(hello) fungw-1.2.0/doc/example/00_hello/hello.stt0000644000175100017510000000036114032255146016503 0ustar svnsvn;; Public function; atoi() is provided by another objects through fungw (defun hello (how_many who) (princln "hello" how_many who) (princln "atoi:" (atoi "42")) 66 ) (princln "hello world from init" (atoi "33")) (fgw_func_reg "hello") fungw-1.2.0/doc/example/00_hello/hello.sh0000644000175100017510000000024414032255146016303 0ustar svnsvn#!/bin/sh hello() { echo "Hello $1 $2" echo "atoi:" `fgw atoi "42"` fgw_retval=66 } ### init echo "hello world from init" `fgw atoi "33"` fgw_func_reg "hello" fungw-1.2.0/doc/example/00_hello/Readme.txt0000644000175100017510000000066314034065067016612 0ustar svnsvnThe same example script written in each supported scripting language. Useful for example apps in other examples. The script has an init part that makes a call to atoi(), which should be an external function (provided by the a host app or another engine loaded by the host app). The script then registers a function called 'hello'. When hello() is called, it prints its two arguments, making one more call to atoi() and returns 66. fungw-1.2.0/doc/example/00_hello/hello.py0000644000175100017510000000052614032255146016324 0ustar svnsvn# Public function; atoi() is provided by another objects through fungw def hello(how_many, who): print "Hello " + str(how_many) + " " + str(who) print "atoi: " + str(atoi("42")) return 66 print "hello world from init " + str(atoi("33")) # register hello(), to be called from other objects and the host application fgw_func_reg("hello") fungw-1.2.0/doc/example/00_hello/hello.lua0000644000175100017510000000055114032255146016453 0ustar svnsvn-- Public function; atoi() is provided by another objects through fungw function hello(how_many, who) io.write("Hello ", how_many, " ", who, " \n"); io.write("atoi: ", atoi("42"), "\n"); return 66; end io.write("hello world from init", atoi("32"), "\n"); -- register hello(), to be called from other objects and the host application fgw_func_reg("hello"); fungw-1.2.0/doc/example/00_hello/hello.pcl0000644000175100017510000000023314036463635016455 0ustar svnsvnproc hello {how_many who} { puts "Hello $how_many $who" puts "atoi: [atoi 42]" return 66 } puts "hello world from init [atoi 32]" fgw_func_reg "hello" fungw-1.2.0/doc/example/00_hello/hello.rb0000644000175100017510000000051514032255146016275 0ustar svnsvn# Public function; atoi() is provided by another objects through fungw def hello(how_many, who) print("Hello #{how_many} #{who}\n") print("atoi:", atoi("42"), "\n") return 66 end print("hello world from init ", atoi("42"), "\n") # register hello(), to be called from other objects and the host application fgw_func_reg("hello") fungw-1.2.0/doc/example/00_hello/hello.pas0000644000175100017510000000060114032255146016451 0ustar svnsvn{ Public function; atoi() is provided by another objects through fungw } function hello(how_many; who); begin fawk_print('Hello', how_many, who); fawk_print('atoi:', atoi('42')); hello := 66; end; function main(ARG); begin fawk_print('hello world from init', atoi('33')); { register hello(), to be called from other objects and the host application } fgw_func_reg(hello); end; fungw-1.2.0/doc/example/00_hello/hello.py30000644000175100017510000000053114032255146016403 0ustar svnsvn# Public function; atoi() is provided by another objects through fungw def hello(how_many, who): print("Hello " + str(how_many) + " " + str(who)) print("atoi: " + str(atoi("42"))) return 66 print("hello world from init " + str(atoi("33"))) # register hello(), to be called from other objects and the host application fgw_func_reg("hello") fungw-1.2.0/doc/example/00_hello/hello.fawk0000644000175100017510000000055314032255146016624 0ustar svnsvn# Public function; atoi() is provided by another objects through fungw function hello(how_many, who) { fawk_print("Hello", how_many, who); fawk_print("atoi:", atoi("42")); return 66; } function main(ARG) { fawk_print("hello world from init", atoi("33")); # register hello(), to be called from other objects and the host application fgw_func_reg(hello); } fungw-1.2.0/doc/example/00_hello/Makefile0000644000175100017510000000005514034223674016307 0ustar svnsvnall: #nothing to do clean: #nothing to do fungw-1.2.0/doc/example/00_hello/hello.c0000644000175100017510000000246214032255146016117 0ustar svnsvn#include /* call a funciton defined in an object */ int my_atoi(fgw_ctx_t *ctx, const char *str) { fgw_arg_t res, arg[2]; fgw_func_t *f; f = fgw_func_lookup(ctx, "atoi"); if (f != NULL) { arg[0].type = FGW_FUNC; arg[0].val.func = f; arg[1].type = FGW_STR; arg[1].val.cstr = str; f->func(&res, 2, arg); if (res.type != FGW_INT) { fprintf(stderr, "ERROR: atoi() returned non-int\n"); return 0; } } else { printf("ERROR: atoi() not found.\n"); return -1; } return res.val.nat_int; } /* Public function; atoi() is provided by another objects through fungw */ static fgw_error_t c_hello(fgw_arg_t *res, int argc, fgw_arg_t *argv) { fgw_ctx_t *ctx = argv[0].val.func->obj->parent; if (argc != 3) return FGW_ERR_ARGC; if (fgw_arg_conv(ctx, &argv[1], FGW_INT) != 0) return FGW_ERR_ARG_CONV; if (fgw_arg_conv(ctx, &argv[2], FGW_STR) != 0) return FGW_ERR_ARG_CONV; printf("Hello %d %s\n", argv[1].val.nat_int, argv[2].val.str); printf("atoi: %d\n", my_atoi(ctx, "42")); res->val.nat_int = 66; res->type = FGW_INT; return FGW_SUCCESS; } void hello_load(fgw_obj_t *obj) { printf("hello world from init %d\n", my_atoi(obj->parent, "33")); /* register hello(), to be called from other objects and the host application */ fgw_func_reg(obj, "hello", c_hello); } fungw-1.2.0/doc/example/00_hello/hello.awk0000644000175100017510000000051214032255146016451 0ustar svnsvn# Public function; atoi() is provided by another objects through fungw function hello(how_many, who) { print "Hello", how_many, who print "atoi:", atoi("42") return 66 } BEGIN { print "hello world from init", atoi("33") # register hello(), to be called from other objects and the host application fgw_func_reg("hello"); } fungw-1.2.0/doc/example/30_lnk_sys_pup/0000755000175100017510000000000014047742763016126 5ustar svnsvnfungw-1.2.0/doc/example/30_lnk_sys_pup/Readme.txt0000644000175100017510000000130214034212173020037 0ustar svnsvnDemonstrate app build option: dynamic script plugin loading with puplug. Otherwise does the same as 10_script (loads and runs a script calling function forth and back). This example requires puplug installed on the system. Puplug can be downloaded from: svn://svn.repo.hu/puplug/trunk The advantage of this setup is that it loads only that script language plugin (.so) that is required for running the script, which is specified run-time. This means less dependencies are dynamic linked on app startup. (Note: puplug loads the required fungw language binding .so plugin, e.g. fungw_lua.so using dlopen(); then fungw_lua.so depends on system installed liblua which is recursively loaded by dlopen.) fungw-1.2.0/doc/example/30_lnk_sys_pup/Makefile0000644000175100017510000000160514034223674017557 0ustar svnsvn# User configuration for this example: change default language and script # file name; these can be overriden runtime from the command line # Prefix is where fungw is installed PREFIX=/usr/local/lib SCLANG=fawk SCRIPT_FN=../00_hello/hello.fawk #CFLAGS_DBG = -g -Wall FUNGW=$(PREFIX)/fungw # You have to have genht installed too LDFLAGS_FUNGW=-lfungw -lpuplug -ldl -lgenht # this is how language and script file name is passed in for the example code # so it doesn't need to do any parsing or run-time decision CFLAGS_LANG = \ -DSCLANG=$(SCLANG) \ -DSCLANG_STR=\"$(SCLANG)\" \ -DSCRIPT_FN=\"$(SCRIPT_FN)\" \ -DPREFIX=\"$(PREFIX)\" all: hello hello: hello.o $(CC) -o hello hello.o $(LDFLAGS) $(LDFLAGS_SCRIPT) $(LDFLAGS_FUNGW) -lm $(LDLIBS) hello.o: hello.c Makefile $(CC) -c $(CFLAGS) $(CFLAGS_LANG) $(CFLAGS_SCRIPT) $(CFLAGS_DBG) -o hello.o hello.c clean: -rm hello hello.o 2>/dev/null fungw-1.2.0/doc/example/30_lnk_sys_pup/hello.c0000644000175100017510000000610114034053752017360 0ustar svnsvn#include #include #include #include #include #include /* Handle runtime fungw errors - inform the user */ void async_error(fgw_obj_t *obj, const char *msg) { printf("Async error: %s: %s\n", obj->name, msg); } /* Script calls C function: wrapper for atoi() */ fgw_error_t fgwc_atoi(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; /* setup for the FGW macro shorthands, initializes fgw_ctx_t *ctx */ FGW_ARGC_REQ_MATCH(1); /* require exactly one argument */ FGW_ARG_CONV(&argv[1], FGW_STR); /* make sure argv[1] is a string */ /* call the function, prepare the result in res */ res->val.nat_int = atoi(argv[1].val.str); res->type = FGW_INT; return FGW_SUCCESS; } /* concat two strings into a newly allocated string */ static char *str_concat2(const char *s1, const char *s2) { long l1 = strlen(s1), l2 = strlen(s2); char *res = malloc(l1+l2+1); assert(res != NULL); memcpy(res, s1, l1); memcpy(res+l1, s2, l2+1); return res; } int main(int argc, char *argv[]) { pup_context_t plugins; pup_plugin_t *script_plugin; const char *plugin_dirs[] = { PREFIX "/puplug", NULL }; const char *lang_name = SCLANG_STR; const char *script_fn = SCRIPT_FN; char *plugin_name; fgw_ctx_t ctx; fgw_obj_t *hobj, *sobj; fgw_arg_t res; int rv; pup_init(&plugins); if (argc == 3) { lang_name = argv[1]; script_fn = argv[2]; } else if (argc != 1) { fprintf(stderr, "ERROR: wrong number of arguments; must be %s lang script\n", argv[0]); return 1; } rv = 0; plugin_name = str_concat2("fungw_", lang_name); script_plugin = pup_load(&plugins, plugin_dirs, plugin_name, 0, &rv); if (script_plugin == NULL) { fprintf(stderr, "ERROR: failed to load puplug plugin %s for script language %s\n", lang_name, plugin_name); free(plugin_name); return 1; } free(plugin_name); /* initialize a context */ fgw_init(&ctx, "my_context"); ctx.async_error = async_error; /* create a host object and register our own functions there */ hobj = fgw_obj_reg(&ctx, "host_app"); fgw_func_reg(hobj, "atoi", fgwc_atoi); /* create objects (load script) */ sobj = fgw_obj_new(&ctx, "hello", lang_name, script_fn, NULL); if (sobj == NULL) { fprintf(stderr, "ERROR: failed to load script %s (language %s)\n", SCRIPT_FN, SCLANG_STR); return 1; } /* Call a script function */ printf("\n### Call hello()\n"); rv = fgw_vcall(&ctx, &res, "hello", FGW_INT, (int)12, FGW_STR, "blobbs", 0); if (rv == 0) { int cr = fgw_arg_conv(&ctx, &res, FGW_INT); /* convert result to int if it is not an int already */ if (cr == 0) printf("hello() result=%d\n", res.val.nat_int); else printf("hello() result=\n"); } else fprintf(stderr, "ERROR: calling hello() failed\n"); fgw_uninit(&ctx); /* uninit the context; new contexts could still be created */ pup_uninit(&plugins); /* unload plugins before the final fungw atexit call, so plugin engines can unregister from fungw */ fgw_atexit(); /* free all memory used by the lib */ return 0; } fungw-1.2.0/doc/example/32_lnk_sys_dyn/0000755000175100017510000000000014047742763016116 5ustar svnsvnfungw-1.2.0/doc/example/32_lnk_sys_dyn/Readme.txt0000644000175100017510000000110114034062212020021 0ustar svnsvnDemonstrate app build option: script plugins dynamic linked with -l's. Otherwise does the same as 10_script (loads and runs a script calling function forth and back). This example is about the same as static linking: the set of script languages available runtime is determined compile time. The main drawback compared to puplug or dlopen is that if a new fungw binding .so is installed later, the app will not use it unless the app is recompiled with a new -l. This build option is normally not recommended outside of bootstrapping a project or experimenting with the code. fungw-1.2.0/doc/example/32_lnk_sys_dyn/Makefile0000644000175100017510000000223014034223674017542 0ustar svnsvn# User configuration for this example: change language and script file name # Prefix is where fungw is installed PREFIX=/usr/local/lib SCLANG=fawk SCRIPT_FN=../00_hello/hello.fawk #CFLAGS_DBG = -g -Wall # this variable-in-variable needs a fairly recent make implementation # should work with GNU make and any modern BSD make. In a more portable # project the available libs are detected in ./configure and these can # be generated. LDFLAGS_SCRIPT = -lfungw_$(SCLANG) $(LDFLAGS_fungw_$(SCLANG)) CFLAGS_SCRIPT = $(CFLAGS_fungw_$(SCLANG)) include $(PREFIX)/puplug/fungw_$(SCLANG).mak FUNGW=$(PREFIX)/fungw # You have to have genht installed too LDFLAGS_FUNGW=-lfungw -lgenht # this is how language and script file name is passed in for the example code # so it doesn't need to do any parsing or run-time decision CFLAGS_LANG = \ -DSCLANG=$(SCLANG) \ -DSCLANG_STR=\"$(SCLANG)\" \ -DSCRIPT_FN=\"$(SCRIPT_FN)\" all: hello hello: hello.o $(CC) -o hello hello.o $(LDFLAGS) $(LDFLAGS_SCRIPT) $(LDFLAGS_FUNGW) -lm $(LDLIBS) hello.o: hello.c Makefile $(CC) -c $(CFLAGS) $(CFLAGS_LANG) $(CFLAGS_SCRIPT) $(CFLAGS_DBG) -o hello.o hello.c clean: -rm hello hello.o 2>/dev/null fungw-1.2.0/doc/example/32_lnk_sys_dyn/hello.c0000644000175100017510000000474114034053752017360 0ustar svnsvn#include #include #include /* Macro wizardy for making the example language-independent; in a real app a ./configure step would detect available languages and generate the initialization code */ #define SCLANG_INIT_(LANG) pplg_init_fungw_ ## LANG () #define SCLANG_INIT(LANG) SCLANG_INIT_(LANG) extern int SCLANG_INIT(SCLANG); /* Handle runtime fungw errors - inform the user */ void async_error(fgw_obj_t *obj, const char *msg) { printf("Async error: %s: %s\n", obj->name, msg); } /* Script calls C function: wrapper for atoi() */ fgw_error_t fgwc_atoi(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; /* setup for the FGW macro shorthands, initializes fgw_ctx_t *ctx */ FGW_ARGC_REQ_MATCH(1); /* require exactly one argument */ FGW_ARG_CONV(&argv[1], FGW_STR); /* make sure argv[1] is a string */ /* call the function, prepare the result in res */ res->val.nat_int = atoi(argv[1].val.str); res->type = FGW_INT; return FGW_SUCCESS; } int main(int argc, char *argv[]) { fgw_ctx_t ctx; fgw_obj_t *hobj, *sobj; fgw_arg_t res; int rv; /* Initialize the language engine; in a real app a ./configure step would detect available languages and generate the initialization code. It is safe to call them all - if they are already -l'd to the executable, calling the init function won't have much overhead */ SCLANG_INIT(SCLANG); /* initialize a context */ fgw_init(&ctx, "my_context"); ctx.async_error = async_error; /* create a host object and register our own functions there */ hobj = fgw_obj_reg(&ctx, "host_app"); fgw_func_reg(hobj, "atoi", fgwc_atoi); /* create objects (load script) */ sobj = fgw_obj_new(&ctx, "hello", SCLANG_STR, SCRIPT_FN, NULL); if (sobj == NULL) { fprintf(stderr, "ERROR: failed to load script %s (language %s)\n", SCRIPT_FN, SCLANG_STR); return 1; } /* Call a script function */ printf("\n### Call hello()\n"); rv = fgw_vcall(&ctx, &res, "hello", FGW_INT, (int)12, FGW_STR, "blobbs", 0); if (rv == 0) { int cr = fgw_arg_conv(&ctx, &res, FGW_INT); /* convert result to int if it is not an int already */ if (cr == 0) printf("hello() result=%d\n", res.val.nat_int); else printf("hello() result=\n"); } else fprintf(stderr, "ERROR: calling hello() failed\n"); fgw_uninit(&ctx); /* uninit the context; new contexts could still be created */ fgw_atexit(); /* free all memory used by the lib */ return 0; } fungw-1.2.0/doc/example/60_engine_c/0000755000175100017510000000000014047742763015332 5ustar svnsvnfungw-1.2.0/doc/example/60_engine_c/eng_math.h0000644000175100017510000000034714032626661017261 0ustar svnsvn/* The only public function of an engline is its initialization; once this is called, before an fgw_atexit() call, the engine is known to fungw and is available for instantiation in any context. */ void eng_math_init(void); fungw-1.2.0/doc/example/60_engine_c/test_math.fawk0000644000175100017510000000064314032626661020167 0ustar svnsvnfunction main(ARGV) { pi = 3.141592653589793238462643; # a few simple calls to all three example functions our math engine declared # to demonstrate the proper value is calculated and returned fawk_print("sin(0): ", my_sin(0)); fawk_print("cos(0): ", my_cos(0)); fawk_print("tan(0): ", my_tan(0)); fawk_print("sin(pi): ", my_sin(pi)); fawk_print("cos(pi): ", my_cos(pi)); fawk_print("tan(pi): ", my_tan(pi)); }fungw-1.2.0/doc/example/60_engine_c/Readme.txt0000644000175100017510000000043014034062212017241 0ustar svnsvnExample engine written in C, implementing a simple math lib with my_sin(), my_cos() and my_tan(). The app registers the math lib engine and loads and runs an example fawk script that calls these functions. Build considerations: static linking, as explained in example 10_script. fungw-1.2.0/doc/example/60_engine_c/test_math.c0000644000175100017510000000374614032626661017470 0ustar svnsvn#include #include #include #include #include "eng_math.h" /* Import fawk init; this relies on two things: - fawk is always available in fungw, no external dep (well, the user can disable it in ./configure that'd lead to link error here) - fawk is in the big static lib (.a) of all languages and if we call its init, all relevant objects will get linked */ extern int pplg_init_fungw_fawk(void); /* Handle runtime fungw errors - inform the user */ void async_error(fgw_obj_t *obj, const char *msg) { printf("Async error: %s: %s\n", obj->name, msg); } /* Script calls C function: wrapper for atoi() */ fgw_error_t fgwc_atoi(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; /* setup for the FGW macro shorthands, initializes fgw_ctx_t *ctx */ FGW_ARGC_REQ_MATCH(1); /* require exactly one argument */ FGW_ARG_CONV(&argv[1], FGW_STR); /* make sure argv[1] is a string */ /* call the function, prepare the result in res */ res->val.nat_int = atoi(argv[1].val.str); res->type = FGW_INT; return FGW_SUCCESS; } int main(int argc, char *argv[]) { fgw_ctx_t ctx; fgw_obj_t *mobj, *sobj; /* Initialize and register the script language engine fawk */ pplg_init_fungw_fawk(); /* Initialize and register the math engine */ eng_math_init(); /* initialize a context */ fgw_init(&ctx, "my_context"); ctx.async_error = async_error; /* create an object called trigonometry, an instance of the math engine, with no instance argument (our local math engine is not configurable) */ mobj = fgw_obj_new(&ctx, "trigonometry", "math", NULL, NULL); assert(mobj != NULL); /* create objects for the script: loads script and runs its main() */ sobj = fgw_obj_new(&ctx, "test_script", "fawk", "test_math.fawk", NULL); assert(sobj != NULL); fgw_uninit(&ctx); /* uninit the context; new contexts could still be created */ fgw_atexit(); /* free all memory used by the lib */ return 0; } fungw-1.2.0/doc/example/60_engine_c/Makefile0000644000175100017510000000075714034223674016772 0ustar svnsvnall: test_math #CFLAGS_DBG = -g -Wall include ../Makefile.conf # $(FUNGWBIND_SRCLIBA) is explained in 10_script test_math: test_math.o eng_math.o $(CC) -o test_math test_math.o eng_math.o $(LDFLAGS) $(LIB_FGW) $(LDLIBS) $(FUNGWBIND_SRCLIBA) test_math.o: test_math.c eng_math.h $(CC) -c $(CFLAGS) $(CFLAGS_DBG) -o test_math.o test_math.c eng_math.o: eng_math.c eng_math.h $(CC) -c $(CFLAGS) $(CFLAGS_DBG) -o eng_math.o eng_math.c clean: -rm test_math test_math.o eng_math.o 2>/dev/null fungw-1.2.0/doc/example/60_engine_c/eng_math.c0000644000175100017510000000407314042222576017252 0ustar svnsvn#include #include #include #include static fgw_error_t fgwc_sin(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; FGW_ARGC_REQ_MATCH(1); FGW_ARG_CONV(&argv[1], FGW_DOUBLE); res->val.nat_double = sin(argv[1].val.nat_double); res->type = FGW_DOUBLE; return FGW_SUCCESS; } static fgw_error_t fgwc_cos(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; FGW_ARGC_REQ_MATCH(1); FGW_ARG_CONV(&argv[1], FGW_DOUBLE); res->val.nat_double = cos(argv[1].val.nat_double); res->type = FGW_DOUBLE; return FGW_SUCCESS; } static fgw_error_t fgwc_tan(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; FGW_ARGC_REQ_MATCH(1); FGW_ARG_CONV(&argv[1], FGW_DOUBLE); res->val.nat_double = tan(argv[1].val.nat_double); res->type = FGW_DOUBLE; return FGW_SUCCESS; } /* Called when an object instance is created from this engine. This engine does not have any internal state, just pure math functions: just register those functions in the object being created. */ static int on_load(fgw_obj_t *obj, const char *filename, const char *opts) { fgw_func_reg(obj, "my_sin", fgwc_sin); fgw_func_reg(obj, "my_cos", fgwc_cos); fgw_func_reg(obj, "my_tan", fgwc_tan); return 0; } static const fgw_eng_t fgw_math_eng = { "math", /* name of the engine; third arg for fgw_obj_new() */ fgws_c_call_script, /* this C function is called when an engine function is called from the outside; fgws_c_call_script just calls a C function by pointer (stored on fgw_func_reg() calls) */ NULL, /* init(), used in script engines so fungw can register some "system" functions in an empty script context before really loading the script */ on_load /* called when an object instance is created from this engine */ }; void eng_math_init(void) { fgw_eng_reg(&fgw_math_eng); } fungw-1.2.0/doc/example/82_ucc/0000755000175100017510000000000014047742763014341 5ustar svnsvnfungw-1.2.0/doc/example/82_ucc/Readme.txt0000644000175100017510000000144414034062212016256 0ustar svnsvnExample on using the User Call Context: the app can create a context struct at the initial call into fungw and the pointer of that context is passed through the call stack so when an engine or a function of the app is called back it can look at the context. A typical use case is when the app is dealing with multiple independent set of data, calling a script while working on a specific one of those. Then the script calls back an utility function of the app that has to touch the data in the right data set. With UCC the app can place an opaque context pointer that identifies the data set in question and at any call from the script this context pointer is available. Implementation details: the UCC is stored in argv[0] and script bindings guarantee to preserve it through further function calls. fungw-1.2.0/doc/example/82_ucc/test_script.c0000644000175100017510000000576514034053752017052 0ustar svnsvn#include #include #include /* User configuration for this example: change language and script file name */ #define SCLANG fawk #define SCLANG_STR "fawk" #define SCRIPT_FN "../00_hello/hello.fawk" /* Macro wizardy for making the example language-independent */ #define SCLANG_INIT_(LANG) pplg_init_fungw_ ## LANG () #define SCLANG_INIT(LANG) SCLANG_INIT_(LANG) extern int SCLANG_INIT(SCLANG); /* Handle runtime fungw errors - inform the user */ void async_error(fgw_obj_t *obj, const char *msg) { printf("Async error: %s: %s\n", obj->name, msg); } /* Script calls C function: wrapper for atoi() */ fgw_error_t fgwc_atoi(fgw_arg_t *res, int argc, fgw_arg_t *argv) { char *end; int ten = 10, *base = FGW_USER_CALL_CTX; FGW_DECL_CTX; /* setup for the FGW macro shorthands, initializes fgw_ctx_t *ctx */ FGW_ARGC_REQ_MATCH(1); /* require exactly one argument */ FGW_ARG_CONV(&argv[1], FGW_STR); /* make sure argv[1] is a string */ /* call the function, prepare the result in res */ if (base == NULL) base = &ten; /* this happens in the init part of the script - since that's not a function call from the app, there's no user call context */ res->val.nat_int = strtol(argv[1].val.str, &end, *base); res->type = FGW_INT; return *end == '\0' ? FGW_SUCCESS : FGW_ERR_ARG_CONV; } int main(int argc, char *argv[]) { fgw_ctx_t ctx; fgw_obj_t *hobj, *sobj; fgw_arg_t res; int rv, base = 16; if (argc > 1) base =atoi(argv[1]); if ((base < 2) || (base > 36)) { fprintf(stderr, "ERROR: invalid base '%s': must be between 2 and 36\n", argv[1]); return 1; } /* Initialize the language engine; in a more advanced setup the .so is loaded using dlopen() which automates this step; in a static link setup, like this example, the script lang engine needs to be linked and initialized manually. */ SCLANG_INIT(SCLANG); /* initialize a context */ fgw_init(&ctx, "my_context"); ctx.async_error = async_error; /* create a host object and register our own functions there */ hobj = fgw_obj_reg(&ctx, "host_app"); fgw_func_reg(hobj, "atoi", fgwc_atoi); /* create objects (load script) */ sobj = fgw_obj_new(&ctx, "hello", SCLANG_STR, SCRIPT_FN, NULL); if (sobj == NULL) { fprintf(stderr, "ERROR: failed to load script %s (language %s)\n", SCRIPT_FN, SCLANG_STR); return 1; } /* Call a script function; pass a pointer to base for the UCC */ printf("\n### Call hello()\n"); rv = fgw_uvcall(&ctx, &base, &res, "hello", FGW_INT, (int)12, FGW_STR, "blobbs", 0); if (rv == 0) { int cr = fgw_arg_conv(&ctx, &res, FGW_INT); /* convert result to int if it is not an int already */ if (cr == 0) printf("hello() result=%d\n", res.val.nat_int); else printf("hello() result=\n"); } else fprintf(stderr, "ERROR: calling hello() failed\n"); fgw_uninit(&ctx); /* uninit the context; new contexts could still be created */ fgw_atexit(); /* free all memory used by the lib */ return 0; } fungw-1.2.0/doc/example/82_ucc/Makefile0000644000175100017510000000177614034223674016003 0ustar svnsvnall: test_script CFLAGS_DBG = -g -Wall include ../Makefile.conf # $(FUNGWBIND_SRCLIBA) contains all static linkable script languages. # Linking them all is suboptimal for a real application because: # - it hardwires language support to whatever is available at the time the # app is compiled # - it brings in a lot of unnecessary dependencies, e.g. if you use lua only # but you had python available at app compialtion time, every time you # start the app it will dynamic link python as well # # We use this suboptimal method here because it's just a quick example and # this makes it easier to change scripting languages while keeping the build # simple. There are other exmaples further down that demonstrate different # build/link strategies. test_script: test_script.o $(CC) -o test_script test_script.o $(LDFLAGS) $(LIB_FGW) $(LDLIBS) $(FUNGWBIND_SRCLIBA) test_script.o: test_script.c $(CC) -c $(CFLAGS) $(CFLAGS_DBG) -o test_script.o test_script.c clean: -rm test_script test_script.o 2>/dev/null fungw-1.2.0/doc/example/index_post.html0000644000175100017510000000003114034067666016310 0ustar svnsvn fungw-1.2.0/doc/example/31_lnk_sys_dl/0000755000175100017510000000000014047742763015722 5ustar svnsvnfungw-1.2.0/doc/example/31_lnk_sys_dl/Readme.txt0000644000175100017510000000104214034212173017634 0ustar svnsvnDemonstrate app build option: dynamic script plugin loading with dlopen(). Otherwise does the same as 10_script (loads and runs a script calling function forth and back). The advantage of this setup is that it loads only that script language plugin (.so) that is required for running the script, which is specified run-time. This means less dependencies are dynamic linked on app startup. The drawback not using puplug is more boilerplate around dlopen and potential portability problems on exotic targets where dlopen may work differently. fungw-1.2.0/doc/example/31_lnk_sys_dl/Makefile0000644000175100017510000000157414034223674017360 0ustar svnsvn# User configuration for this example: change default language and script # file name; these can be overriden runtime from the command line # Prefix is where fungw is installed PREFIX=/usr/local/lib SCLANG=fawk SCRIPT_FN=../00_hello/hello.fawk #CFLAGS_DBG = -g -Wall FUNGW=$(PREFIX)/fungw # You have to have genht installed too LDFLAGS_FUNGW=-lfungw -ldl -lgenht # this is how language and script file name is passed in for the example code # so it doesn't need to do any parsing or run-time decision CFLAGS_LANG = \ -DSCLANG=$(SCLANG) \ -DSCLANG_STR=\"$(SCLANG)\" \ -DSCRIPT_FN=\"$(SCRIPT_FN)\" \ -DPREFIX=\"$(PREFIX)\" all: hello hello: hello.o $(CC) -o hello hello.o $(LDFLAGS) $(LDFLAGS_SCRIPT) $(LDFLAGS_FUNGW) -lm $(LDLIBS) hello.o: hello.c Makefile $(CC) -c $(CFLAGS) $(CFLAGS_LANG) $(CFLAGS_SCRIPT) $(CFLAGS_DBG) -o hello.o hello.c clean: -rm hello hello.o 2>/dev/null fungw-1.2.0/doc/example/31_lnk_sys_dl/hello.c0000644000175100017510000000760014034053752017161 0ustar svnsvn#include #include #include #include #include #include /* Handle runtime fungw errors - inform the user */ void async_error(fgw_obj_t *obj, const char *msg) { printf("Async error: %s: %s\n", obj->name, msg); } /* Script calls C function: wrapper for atoi() */ fgw_error_t fgwc_atoi(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; /* setup for the FGW macro shorthands, initializes fgw_ctx_t *ctx */ FGW_ARGC_REQ_MATCH(1); /* require exactly one argument */ FGW_ARG_CONV(&argv[1], FGW_STR); /* make sure argv[1] is a string */ /* call the function, prepare the result in res */ res->val.nat_int = atoi(argv[1].val.str); res->type = FGW_INT; return FGW_SUCCESS; } /* concat two strings into a newly allocated string */ static char *str_concat3(const char *s1, const char *s2, const char *s3) { long l1 = strlen(s1), l2 = strlen(s2), l3 = strlen(s3); char *res = malloc(l1+l2+l3+1); assert(res != NULL); memcpy(res, s1, l1); memcpy(res+l1, s2, l2); memcpy(res+l1+l2, s3, l3+1); return res; } int main(int argc, char *argv[]) { const char *lang_name = SCLANG_STR; const char *script_fn = SCRIPT_FN; char *plugin_soname, *hook_name; void *script_plugin; void (*hook)(void); fgw_ctx_t ctx; fgw_obj_t *hobj, *sobj; fgw_arg_t res; int rv; if (argc == 3) { lang_name = argv[1]; script_fn = argv[2]; } else if (argc != 1) { fprintf(stderr, "ERROR: wrong number of arguments; must be %s lang script\n", argv[0]); return 1; } rv = 0; plugin_soname = str_concat3(PREFIX "/puplug/fungw_", lang_name, ".so"); script_plugin = dlopen(plugin_soname, RTLD_NOW); if (script_plugin == NULL) { fprintf(stderr, "ERROR: failed to load puplug plugin %s for script language %s\n", lang_name, plugin_soname); free(plugin_soname); return 1; } free(plugin_soname); /* resolve and call the init hook of the plugin so the script engine gets registered in fungw and objects can be instantiated from it */ hook_name = str_concat3("pplg_init_", "fungw_", lang_name); hook = dlsym(script_plugin, hook_name); if (hook == NULL) { fprintf(stderr, "ERROR: failed to load puplug plugin for script language %s: no init hook\n", lang_name); free(hook_name); } hook(); free(hook_name); /* initialize a context */ fgw_init(&ctx, "my_context"); ctx.async_error = async_error; /* create a host object and register our own functions there */ hobj = fgw_obj_reg(&ctx, "host_app"); fgw_func_reg(hobj, "atoi", fgwc_atoi); /* create objects (load script) */ sobj = fgw_obj_new(&ctx, "hello", lang_name, script_fn, NULL); if (sobj == NULL) { fprintf(stderr, "ERROR: failed to load script %s (language %s)\n", SCRIPT_FN, SCLANG_STR); return 1; } /* Call a script function */ printf("\n### Call hello()\n"); rv = fgw_vcall(&ctx, &res, "hello", FGW_INT, (int)12, FGW_STR, "blobbs", 0); if (rv == 0) { int cr = fgw_arg_conv(&ctx, &res, FGW_INT); /* convert result to int if it is not an int already */ if (cr == 0) printf("hello() result=%d\n", res.val.nat_int); else printf("hello() result=\n"); } else fprintf(stderr, "ERROR: calling hello() failed\n"); fgw_uninit(&ctx); /* uninit the context; new contexts could still be created */ /* resolve and call the uninit hook of the plugin so the script engine gets unregistered from fungw; then unload the plugin before the final fungw atexit call, so plugin engine can unregister from fungw */ hook_name = str_concat3("pplg_uninit_", "fungw_", lang_name); hook = dlsym(script_plugin, hook_name); if (hook == NULL) { fprintf(stderr, "ERROR: failed to unload puplug plugin for script language %s: no uninit hook\n", lang_name); free(hook_name); } hook(); free(hook_name); dlclose(script_plugin); fgw_atexit(); /* free all memory used by the lib */ return 0; } fungw-1.2.0/doc/example/10_script/0000755000175100017510000000000014047742763015062 5ustar svnsvnfungw-1.2.0/doc/example/10_script/Readme.txt0000644000175100017510000000221014034212173016772 0ustar svnsvnScript embedding example: register a function scripts can call, load a script, run the init part of the script then call a function of the script. Script is loaded from ../00_hello/. By default the example uses fawk because that is available without external dependencies. To change the language, modify the three defines SCLANG, SCLANG_STR and SCRIPT_FN at the top of test_script.c. Build considerations: this example uses static linking, which is not recommended for production code. $(FUNGWBIND_SRCLIBA) contains all static linkable script languages. Linking them all is suboptimal for a real application because: - it hardwires language support to whatever is available at the time the app is compiled - it brings in a lot of unnecessary dependencies, e.g. if you use lua only but you had python available at app compilation time, every time you start the app it will dynamic link python as well We use this suboptimal method here because it's just a quick example and this makes it easier to change scripting languages while keeping the build simple. There are other examples further down that demonstrate different build/link strategies. fungw-1.2.0/doc/example/10_script/test_script.c0000644000175100017510000000500414034053752017555 0ustar svnsvn#include #include #include /* User configuration for this example: change language and script file name */ #define SCLANG fawk #define SCLANG_STR "fawk" #define SCRIPT_FN "../00_hello/hello.fawk" /* Macro wizardy for making the example language-independent */ #define SCLANG_INIT_(LANG) pplg_init_fungw_ ## LANG () #define SCLANG_INIT(LANG) SCLANG_INIT_(LANG) extern int SCLANG_INIT(SCLANG); /* Handle runtime fungw errors - inform the user */ void async_error(fgw_obj_t *obj, const char *msg) { printf("Async error: %s: %s\n", obj->name, msg); } /* Script calls C function: wrapper for atoi() */ fgw_error_t fgwc_atoi(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; /* setup for the FGW macro shorthands, initializes fgw_ctx_t *ctx */ FGW_ARGC_REQ_MATCH(1); /* require exactly one argument */ FGW_ARG_CONV(&argv[1], FGW_STR); /* make sure argv[1] is a string */ /* call the function, prepare the result in res */ res->val.nat_int = atoi(argv[1].val.str); res->type = FGW_INT; return FGW_SUCCESS; } int main(int argc, char *argv[]) { fgw_ctx_t ctx; fgw_obj_t *hobj, *sobj; fgw_arg_t res; int rv; /* Initialize the language engine; in a more advanced setup the .so is loaded using dlopen() which automates this step; in a static link setup, like this example, the script lang engine needs to be linked and initialized manually. */ SCLANG_INIT(SCLANG); /* initialize a context */ fgw_init(&ctx, "my_context"); ctx.async_error = async_error; /* create a host object and register our own functions there */ hobj = fgw_obj_reg(&ctx, "host_app"); fgw_func_reg(hobj, "atoi", fgwc_atoi); /* create objects (load script) */ sobj = fgw_obj_new(&ctx, "hello", SCLANG_STR, SCRIPT_FN, NULL); if (sobj == NULL) { fprintf(stderr, "ERROR: failed to load script %s (language %s)\n", SCRIPT_FN, SCLANG_STR); return 1; } /* Call a script function */ printf("\n### Call hello()\n"); rv = fgw_vcall(&ctx, &res, "hello", FGW_INT, (int)12, FGW_STR, "blobbs", 0); if (rv == 0) { int cr = fgw_arg_conv(&ctx, &res, FGW_INT); /* convert result to int if it is not an int already */ if (cr == 0) printf("hello() result=%d\n", res.val.nat_int); else printf("hello() result=\n"); } else fprintf(stderr, "ERROR: calling hello() failed\n"); fgw_uninit(&ctx); /* uninit the context; new contexts could still be created */ fgw_atexit(); /* free all memory used by the lib */ return 0; } fungw-1.2.0/doc/example/10_script/Makefile0000644000175100017510000000051214034223674016507 0ustar svnsvnall: test_script #CFLAGS_DBG = -g -Wall include ../Makefile.conf test_script: test_script.o $(CC) -o test_script test_script.o $(LDFLAGS) $(LIB_FGW) $(LDLIBS) $(FUNGWBIND_SRCLIBA) test_script.o: test_script.c $(CC) -c $(CFLAGS) $(CFLAGS_DBG) -o test_script.o test_script.c clean: -rm test_script test_script.o 2>/dev/null fungw-1.2.0/doc/example/80_multicall/0000755000175100017510000000000014047742763015553 5ustar svnsvnfungw-1.2.0/doc/example/80_multicall/Readme.txt0000644000175100017510000000215714034212173017475 0ustar svnsvnDemo on registering multiple functions under the same short name within a context and using them as "event handlers" in a multi-call where a single call is directed to each function with matching name. A typical real-life application would be that different scripts and engines would register their event handler function using the event name. The application, upon generating the event, would do a multi-call to the short name. Fungw then looks at each object in the context and see if the object has a function with matching name; if so, it is called. Thus the event is delivered to any object in the context that has an event handler function with the required function name. Limitations: - order of calls within a multi-call is unspecified (it is _not_ the order of function registration or object creation) - a multicall has no return value (each function call would have its own return value and the dispatcher wouldn't know which one to return to the caller) - it is not possible to cancel a multi-call before every matching function is called (e.g. if one of the callees wants to announce "the event is handled") fungw-1.2.0/doc/example/80_multicall/multicall.c0000644000175100017510000000334314032606702017672 0ustar svnsvn#include #include #include /* Two "event handler" functions that will be bound to the same function name from two different objects */ fgw_error_t event_cb1(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; printf(" event_cb1: '%s'\n", argv[1].val.str); return FGW_SUCCESS; } fgw_error_t event_cb2(fgw_arg_t *res, int argc, fgw_arg_t *argv) { FGW_DECL_CTX; printf(" event_cb2: '%s'\n", argv[1].val.str); return FGW_SUCCESS; } int main(int argc, char *argv[]) { fgw_ctx_t ctx; fgw_obj_t *obj1, *obj2; fgw_arg_t res; fgw_init(&ctx, "host"); /* create two objects, emulating e.g. two scripts in different languages */ obj1 = fgw_obj_reg(&ctx, "o1"); obj2 = fgw_obj_reg(&ctx, "o2"); /* register obj1.event to event_cb1 and obj2.event to event_cb2; they have the same name, so there will be only one prefix-less event() in ctx */ fgw_func_reg(obj1, "event", event_cb1); fgw_func_reg(obj2, "event", event_cb2); /* call event() directly, as a plain function: only obj1.event handler will run (it was the first registered with the name "event"), but we have a return value in res */ printf("two objs, fgw_vcall:\n"); fgw_vcall(&ctx, &res, "event", FGW_STR, "single", 0); /* call every event() from context; this means *.event() is called with the same argument. (If a callee decides to convert the argument, that affects only that single call, the other calls will still get the argument with the original type). There is no &res return value as each function could return something different. */ printf("two objs, fgw_vcall_all:\n"); fgw_vcall_all(&ctx, "event", FGW_STR, "multi", 0); /* free all resources */ fgw_uninit(&ctx); fgw_atexit(); return 0; } fungw-1.2.0/doc/example/80_multicall/Makefile0000644000175100017510000000043614034223674017205 0ustar svnsvnall: multicall #CFLAGS_DBG = -g -Wall include ../Makefile.conf multicall: multicall.o $(CC) -o multicall multicall.o $(LDFLAGS) $(LIB_FGW) $(LDLIBS) multicall.o: multicall.c $(CC) -c $(CFLAGS) $(CFLAGS_DBG) -o multicall.o multicall.c clean: -rm multicall multicall.o 2>/dev/null fungw-1.2.0/doc/example/Makefile.conf.in0000644000175100017510000000070614032757570016246 0ustar svnsvnprint [~ # when fungw is installed, this should be /usr/lib/puplug/ FUNGWBIND=../../../libfungwbind include $(FUNGWBIND)/libfungwbind.mak CFLAGS = ~/local/fungw/cflags~ ~/target/cc/fpic~ -I../../.. -I../../../src_3rd LDFLAGS = ~/local/fungw/ldflags~ ~/target/cc/fpic~ LIB_FGW = ../../../libfungw/libfungw.a ../../../src_3rd/genht/genht_std.a LDLIBS = $(FUNGWBIND_LDLIBS) -lm ../src_3rd/genht/genht_std.a: cd ../src_3rd/genht && make genht_std.a ~] fungw-1.2.0/doc/example/index_pre.html0000644000175100017510000000056214034067666016122 0ustar svnsvn

Example programs for fungw

Fungw examples are ordered: the ones on top are the easier to understand ones demonstrating features more commonly needed in practice. Any application developer should read and understand examples numbered lower than 50. The more advanced, exotic use case examples are above 50.

fungw-1.2.0/doc/example/87_custom_type/0000755000175100017510000000000014047742763016147 5ustar svnsvnfungw-1.2.0/doc/example/87_custom_type/Readme.txt0000644000175100017510000000212414034212173020063 0ustar svnsvnDemonstrates how an app can register custom data types that then can be used as arguments and return values in fungw registered functions. Script bindings and stock engines know the standard "base" fungw types only. The app can register a new data type, FGW_MM for millimeter in our example, and provide converters between the new type and the base types. This lets the app deal with native data types, having the conversion coordinated by fungw. There are two implementations shown: - The lazy implementation has only a string <-> FGW_MM converter and lets fungw do a second conversion between the string and whatever base type that was requested. This is less code but runs slower. Can not write unit when converting to string because in many cases fungw will try to convert the string further to a number where the unit would cause an error. - The proper implementation attempts to convert between FGW_MM and any base type where the conversion makes sense. Which is all numeric types and string. This variant can write unit in string target as it is not used as an intermediate format. fungw-1.2.0/doc/example/87_custom_type/custype_lazy.c0000644000175100017510000000637414032634105021040 0ustar svnsvn#include #include #include #include #include #include #include #include /* millimeter type ID; store distance in mm in the ->long field */ fgw_type_t FGW_MM; /* Convert from numeric to string without unit (see later) */ static char *mm_dup_str(long val) { char *res = malloc(32); sprintf(res, "%ld", val); return res; } /* Convert (parse) a string, potentially user supplied, into mm */ static long mmtol(const char *s, const char **end) { char *e; long res = strtol(s, &e, 10); if (*e != '\0') { while(isspace(*e)) e++; if (strcmp(e, "mm") == 0) { *end = e+2; } else if (strcmp(e, "km") == 0) { *end = e+2; res *= 1000l*1000l; } else if (strcmp(e, "m") == 0) { *end = e+1; res *= 1000; } else if (strcmp(e, "dm") == 0) { *end = e+2; res *= 100; } else if (strcmp(e, "cm") == 0) { *end = e+2; res *= 10; } else *end = e; } else *end = e; return res; } /* Lazy approach: convert only to/from string */ int mm_arg_conv_lazy(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target) { if (target == FGW_MM) { /* convert from anything to mm */ long tmp; const char *end; /* first ask fungw convert to string... */ if (fgw_arg_conv(ctx, arg, FGW_STR) != 0) return -1; /* ... then parse the string (it will have no unit if input was numeric) */ tmp = mmtol(arg->val.str, &end); if (arg->type & FGW_DYN) free(arg->val.str); if (*end != '\0') return -1; arg->type = FGW_MM; arg->val.nat_long = tmp; return 0; } if (arg->type == FGW_MM) { /* convert from mm to anything */ long tmp = arg->val.nat_long; /* Cheat: whatever type fungw asked for, just return string; all we are required to do is return a base (non-custom) fungw type. In case the type we return here doesn't match the type fungw requested, it will automatically do a base->base type conversion. For example: fungw wanted double; we return string; fung will do a string->double conversion. This way we do not need to handle each type or type group separately -> less code, worse runtimes. Note: we can't print unit here, because if target type is not string, the conversion would fail. E.g. if we return "15 mm" and fungw wanted a double, it will do an strtod() which will fail at the first 'm'. */ arg->val.str = mm_dup_str(tmp); arg->type = FGW_STR | FGW_DYN; return 0; } /* if this function is ever called with neither side being an mm type, that's a bug in libfungw */ fprintf(stderr, "Neither side of the conversion is mm\n"); abort(); } void test(fgw_ctx_t *ctx) { fgw_arg_t a, b; int res; a.val.str = "124cm"; a.type = FGW_STR; res = fgw_arg_conv(ctx, &a, FGW_MM); printf("str to mm: %d: 0x%x %ld\n", res, a.type, a.val.nat_long); b = a; res = fgw_arg_conv(ctx, &a, FGW_STR); printf("mm to str: %d: 0x%x %s\n", res, a.type, a.val.str); res = fgw_arg_conv(ctx, &b, FGW_DOUBLE); printf("mm to double: %d: 0x%x %f\n", res, b.type, b.val.nat_double); } int main(int argc, char *argv[]) { fgw_ctx_t ctx; fgw_init(&ctx, "host"); FGW_MM = fgw_reg_custom_type(&ctx, 0, "mm", mm_arg_conv_lazy, NULL); assert(FGW_MM != FGW_INVALID); test(&ctx); fgw_uninit(&ctx); fgw_atexit(); return 0; } fungw-1.2.0/doc/example/87_custom_type/custype_prop.c0000644000175100017510000001007514032637151021036 0ustar svnsvn#include #include #include #include #include #include #include #include /* millimeter type ID; store distance in mm in the ->long field */ fgw_type_t FGW_MM; /*** Low level conversion utilities ***/ /* Allocate a string and convert val in it in decimal format with best fitting unit (which is normally mm, but over 1 meter it's m or over 1 kilometer it's km */ static char *mm_dup_str(long val) { char *res = malloc(32); if (val > 1000l*1000l) sprintf(res, "%f km", (double)val/(1000.0*1000.0)); else if (val > 1000) sprintf(res, "%f m", (double)val/1000.0); else sprintf(res, "%ld mm", val); return res; } /* Convert (parse) a string, potentially user supplied, into mm */ static long mmtol(const char *s, const char **end) { char *e; long res = strtol(s, &e, 10); if (*e != '\0') { while(isspace(*e)) e++; if (strcmp(e, "mm") == 0) { *end = e+2; } else if (strcmp(e, "km") == 0) { *end = e+2; res *= 1000l*1000l; } else if (strcmp(e, "m") == 0) { *end = e+1; res *= 1000; } else if (strcmp(e, "dm") == 0) { *end = e+2; res *= 100; } else if (strcmp(e, "cm") == 0) { *end = e+2; res *= 10; } else *end = e; } else *end = e; return res; } /* Conversion with error handling: return out of the parent function with -1 on error */ #define conv_mmtol(dst, src) \ do { \ const char *end; \ dst = mmtol(src, &end); \ if (*end != '\0') \ return -1; \ } while(0) /*** High level entry point for fungw ***/ /* Called when converting to or from custom type mm */ static int mm_arg_conv(fgw_ctx_t *ctx, fgw_arg_t *arg, fgw_type_t target) { if (target == FGW_MM) { /* convert from anything to mm */ long tmp; switch(FGW_BASE_TYPE(arg->type)) { ARG_CONV_CASE_LONG(tmp, conv_assign) /* numeric types can't have unit, take them as mm */ ARG_CONV_CASE_LLONG(tmp, conv_assign) ARG_CONV_CASE_DOUBLE(tmp, conv_assign) ARG_CONV_CASE_LDOUBLE(tmp, conv_assign) ARG_CONV_CASE_STR(tmp, conv_mmtol) /* a string is expected to have unit, parse */ ARG_CONV_CASE_PTR(tmp, conv_err) /* can't convert from anyting else */ ARG_CONV_CASE_CLASS(tmp, conv_err) ARG_CONV_CASE_INVALID(tmp, conv_err) } arg->type = FGW_MM; arg->val.nat_long = tmp; return 0; } if (arg->type == FGW_MM) { /* convert from mm to anything */ long tmp = arg->val.nat_long; switch(target) { ARG_CONV_CASE_LONG(tmp, conv_rev_assign) /* numeric types can't have unit, just save bare mm */ ARG_CONV_CASE_LLONG(tmp, conv_rev_assign) ARG_CONV_CASE_DOUBLE(tmp, conv_rev_assign) ARG_CONV_CASE_LDOUBLE(tmp, conv_rev_assign) ARG_CONV_CASE_PTR(tmp, conv_err) /* can not convert to these */ ARG_CONV_CASE_CLASS(tmp, conv_err) ARG_CONV_CASE_INVALID(tmp, conv_err) case FGW_AUTO: /* to whatever base type our implementation prefers; string fits best because it can use a natural unit; used e.g. when sending data to a script */ case FGW_STR: /* to string conversion: return a dynamically allocated string render */ arg->val.str = mm_dup_str(tmp); arg->type = FGW_STR | FGW_DYN; return 0; } arg->type = target; return 0; } /* if this function is ever called with neither side being an mm type, that's a bug in libfungw */ fprintf(stderr, "Neither side of the conversion is mm\n"); abort(); } void test(fgw_ctx_t *ctx) { fgw_arg_t a, b; int res; a.val.str = "124cm"; a.type = FGW_STR; res = fgw_arg_conv(ctx, &a, FGW_MM); printf("str to mm: %d: 0x%x %ld\n", res, a.type, a.val.nat_long); b = a; res = fgw_arg_conv(ctx, &a, FGW_STR); printf("mm to str: %d: 0x%x %s\n", res, a.type, a.val.str); res = fgw_arg_conv(ctx, &b, FGW_DOUBLE); printf("mm to double: %d: 0x%x %f\n", res, b.type, b.val.nat_double); } int main(int argc, char *argv[]) { fgw_ctx_t ctx; fgw_init(&ctx, "host"); FGW_MM = fgw_reg_custom_type(&ctx, 0, "mm", mm_arg_conv, NULL); assert(FGW_MM != FGW_INVALID); test(&ctx); fgw_uninit(&ctx); fgw_atexit(); return 0; } fungw-1.2.0/doc/example/87_custom_type/Makefile0000644000175100017510000000105614034223674017600 0ustar svnsvnall: custype_prop custype_lazy CFLAGS_DBG = -g -Wall include ../Makefile.conf custype_prop: custype_prop.o $(CC) -o custype_prop custype_prop.o $(LDFLAGS) $(LIB_FGW) $(LDLIBS) custype_prop.o: custype_prop.c $(CC) -c $(CFLAGS) $(CFLAGS_DBG) -o custype_prop.o custype_prop.c custype_lazy: custype_lazy.o $(CC) -o custype_lazy custype_lazy.o $(LDFLAGS) $(LIB_FGW) $(LDLIBS) custype_lazy.o: custype_lazy.c $(CC) -c $(CFLAGS) $(CFLAGS_DBG) -o custype_lazy.o custype_lazy.c clean: -rm custype_prop custype_prop.o custype_lazy custype_lazy.o 2>/dev/null fungw-1.2.0/doc/example/index.html0000644000175100017510000000512414034212173015234 0ustar svnsvn

Example programs for fungw

Fungw examples are ordered: the ones on top are the easier to understand ones demonstrating features more commonly needed in practice. Any application developer should read and understand examples numbered lower than 50. The more advanced, exotic use case examples are above 50.

00_hello

The same example script written in each supported scripting language. Useful for example apps in other examples.

10_script

Script embedding example: register a function scripts can call, load a script, run the init part of the script then call a function of the script.

15_c_call_script

Demonstrates different ways to call a fungw registered (potentially script) function from C.

30_lnk_sys_pup

Demonstrate app build option: dynamic script plugin loading with puplug. Otherwise does the same as 10_script (loads and runs a script calling function forth and back).

31_lnk_sys_dl

Demonstrate app build option: dynamic script plugin loading with dlopen(). Otherwise does the same as 10_script (loads and runs a script calling function forth and back).

32_lnk_sys_dyn

Demonstrate app build option: script plugins dynamic linked with -l's. Otherwise does the same as 10_script (loads and runs a script calling function forth and back).

60_engine_c

Example engine written in C, implementing a simple math lib with my_sin(), my_cos() and my_tan(). The app registers the math lib engine and loads and runs an example fawk script that calls these functions.

80_multicall

Demo on registering multiple functions under the same short name within a context and using them as "event handlers" in a multi-call where a single call is directed to each function with matching name.

82_ucc

Example on using the User Call Context: the app can create a context struct at the initial call into fungw and the pointer of that context is passed through the call stack so when an engine or a function of the app is called back it can look at the context.

87_custom_type

Demonstrates how an app can register custom data types that then can be used as arguments and return values in fungw registered functions.

fungw-1.2.0/doc/user_call_ctx.txt0000644000175100017510000000556513561515437015222 0ustar svnsvnUser call context ~~~~~~~~~~~~~~~~~ This is an optional feature, which is always available but is not always used. The application normally calls a function of an engine (e.g. a script) that will then often calls back an utility functions implemented in the application. The second callee, implemented in the app, may need to know in what application context the first call happened. A trivial way to do that would be to pass on an extra argument to the first call and then get the code there to pass it down to the second call. But keep on passing such arguments, most often fully opaque to the the first call, results in boilerplate. On the other hand, this pattern is very common. So fungw has an optional feature for a more transparent handling of this, called the "user call context", or UCC for short. Each fungw arg is normally 2 pointers wide. This is the convenient size for hosting real wide types, such as long double. In a function call argv[0] is always a pointer to the fungw function being called. But this leaves the second pointer of argv[0] empty, and that is where UCC is stored. Thus in the calling convention: - argv[0].val.argv0.func is the (fgw_func_t *) function being called - argv[0].val.argv0.user_call_ctx is the (void *) UCC When UCC is not required, user_call_ctx is left NULL. On the caller side, the simple call API without 'u' in the name, such as fgw_scall() or fgw_vcall() will do that. If the application depends on UCC, it should initiate calls using the 'u' version of the API, e.g. fgw_uscall() or fgw_uvcall(). The fungw function called should do two things about this: - if it is a function implemented by the app, it could retrieve the UCC from argv[0].val.argv0.user_call_ctx - if any fungw function calls further fungw functions, it should pass on argv[0].val.argv0.user_call_ctx unchanged (except for app functions that have a good reason to switch context) - script engines are implemented in a way that the UCC is set as a global object state while the (blocking) script function is running and any call initiated from the script function will automatically set the UCC from that state. The last implementation detail means UCC is incompatible with threading or async external event handling done by a script library. If the script library decides to run the script asynchronously, without the app starting the chain of calls, the UCC will be one of two values: - if the script was not running at the moment: NULL - if the script was running: the UCC value of the current function call (which, from the app's point of view, when called back from the async script function, is often a random/unrelated context) Generally speaking every call chain should originate in the application, with any threading and async event handling done by the application, keeping in mind that fungw objects have non-thread-local internal states. fungw-1.2.0/doc/index.html0000644000175100017510000000607714046417634013625 0ustar svnsvn

fungw - function gateway

Fungw is a tiny, portable library written in C (C89) that manages dynamic function calls across different programming languages. For this, fungw provides:

The main use of fungw is to provide the host application a framework where dynamic parts of the code (e.g. plugins) can register their calls, allowing app-plugin, plugin-app, and plugin-plugin calls.

The second main use is doing all this in a language-agnostic way: any part of the code can be implemented in any of the numerous supported scripting languages (such as awk or lua). The caller of a function doesn't need to know what language the function is implemented in.

The main property of fungw is simplicity. The only API between objects is the plain old function call mechanism.

What fungw can do

  • register your named functions
  • convert data types if the caller and callee don't use (or don't even support) the same types
  • embed and run scripts written in various scripting languages using a simple, unified, function call based interface - for an application developer this means code for scripting once and be able to use any of the languages available now or in the future
  • scripting without having to generate language-dependent wrappers
  • multiple scripts can be loaded and ran in parallel without interference

What fungw can not do

  • it does not offer anything else but function calls - no access to variables or advanced features of OOP scripts
  • fungw is not OOP - it manipulates flat list of objects and function calls; there's no inheritance, friend classes or any other OOP concept
  • it doesn't load, unload or manage plugins; that should be done by a different library, e.g. puplug

Download & contact

Developer documentation

fungw-1.2.0/scconfig/0000755000175100017510000000000014047742763012651 5ustar svnsvnfungw-1.2.0/scconfig/src/0000755000175100017510000000000014047742764013441 5ustar svnsvnfungw-1.2.0/scconfig/src/default/0000755000175100017510000000000014047742764015065 5ustar svnsvnfungw-1.2.0/scconfig/src/default/log.h0000644000175100017510000000066011400625735016005 0ustar svnsvn#include #include #define max_spaces 64 extern char *spaces; #define logprefix(n) (((n) > max_spaces) ? spaces : (spaces+max_spaces-(n))) void logprintf(int depth, const char *format, ...); void error(const char *format, ...); void report(const char *format, ...); void log_merge(int logdepth, const char *fn); extern FILE *logfile; extern void log_init(void); void log_uninit(void); extern char *fn_log; fungw-1.2.0/scconfig/src/default/dep.c0000644000175100017510000001425113513536120015764 0ustar svnsvn/* scconfig - dependencies Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include "dep.h" #include "db.h" #include "log.h" #include "libs.h" typedef struct { int (*fn)(const char *name, int logdepth, int fatal); } fn_wrap_t; static ht_t *deps = NULL; static const char *USER_WITHOUT = ""; /* find name_ and decide if it was a wildcard request; NOTE: there are requests and servers, both can be wildcard independently. - if a request ends with a / *, it is an explicit wildcard request (*wild=1) - if a request names a "directory" that is wildcard-server, that's a wildcard request (*wild=1) - else the request is a normal request (*wild=0). For normal requests, a required node was explicitly named; if that node is not created by the detection function, that's a failure. For wildcard requests we don't look for any specific node to be created. TODO: we may still check if at least the directory is created */ fn_wrap_t *get_wrap(const char *name_, int *wild, int *missing) { fn_wrap_t *w; char *name, *sep, *tmp; int len, n; len = strlen(name_); *wild = name_[len-1] == '*'; if (*wild) { char *pres; pres = malloc(len+16); memcpy(pres, name_, len-1); strcpy(pres+len-1, "presents"); *missing = get(pres) == NULL; if (*missing) { /* if there's no /presents, it may be a non-directory node with an actual non-empty string value */ const char *val; pres[len-2] = '\0'; val = get(pres); if (val != NULL) *missing = !strlen(val); } free(pres); if (!(*missing)) /* already detected, won't be detected again */ return NULL; } *missing = 1; /* check for global --without disable */ tmp = str_concat("", "/arg/without", db_cwd, "/", name_, NULL); sep = tmp + strlen(tmp) - 1; for(n = 0; n < 4; n++) { const char *d = get(tmp); if (sep < tmp+14) break; if (istrue(d)) { free(tmp); return (fn_wrap_t *)USER_WITHOUT; } while(*sep != '/') sep--; *sep = '\0'; } free(tmp); /* try full match first */ w = ht_get(deps, name_); if (w != NULL) return w; /* try substituting the last part of the path with * for wildcard matches */ name = malloc(len+3); /* worst case: ends in a / and we need to append *\0; allocate a bit more */ memcpy(name, name_, len+1); /* make a copy we can modify */ if (name[len-1] != '/') { name[len] = '/'; /* append a / - if name_ was a "directory", this will result in name/ * */ name[len+1] = '\0'; } *wild = 1; /* if we append a / *, then it's a wildcard request */ for(;;) { sep = str_rchr(name, '/'); if (sep == NULL) goto error; sep[1] = '*'; sep[2] = '\0'; w = ht_get(deps, name); if (w != NULL) { free(name); return w; } *sep = '\0'; *wild = 0; /* cutting back the second layer - not wildcard request anymore, but a request to a specific node served by a wildcard */ } /* no match, exit with error */ error:; *wild = 0; free(name); return NULL; } int require(const char *name, int logdepth, int fatal) { fn_wrap_t *w; int wild, missing; if (get(name) == NULL) { w = get_wrap(name, &wild, &missing); if (w == (fn_wrap_t *)USER_WITHOUT) { if (fatal) { error("Node %s is required by the software but disabled by the user using --without\n", name); abort(); } else { logprintf(logdepth, "(disabled using --without)"); return 1; } } if (!missing) return 0; if ((w == NULL) || (w->fn == NULL)) { error("Node %s is required but I don't know how to detect it.\n", name); abort(); } logprintf(logdepth, "(Required node: '%s')\n", name); if (w->fn(name, logdepth+1, fatal) != 0) { if (fatal) { error("Node %s is required but provided detection callback fails to find that feature on that system.\n", name); abort(); } else { logprintf(logdepth, "(Feature not found, but it is not fatal)"); return 1; } } if ((!wild) && (get(name) == NULL)) { error("Node %s is required but provided detection callback didn't create it (looks like an internal error in scconfig). (db_cwd='%s')\n", name, db_cwd); abort(); } } return 0; } const char *dep_add(const char *name, int (*finder)(const char *name, int logdepth, int fatal)) { fn_wrap_t *w; w = malloc(sizeof(fn_wrap_t)); w->fn = finder; return ht_set(deps, name, w); } int asked_for(const char *cando, const char *needtodo) { int len; /* foo/bar/baz matches /foo/bar/baz */ if (strcmp(cando, needtodo) == 0) goto yes; len = strlen(needtodo); if (len == 0) return 0; /* foo/bar/baz matches /foo/bar/ * */ if ((needtodo[len-1] == '*') && (strncmp(cando, needtodo, len-1) == 0)) goto yes; return 0; yes:; /* asked for it, but have to see if it's already detected */ if (get(cando) != NULL) return 0; return 1; } int is_dep_wild(const char *path) { int len = strlen(path); if (len == 0) return 0; return (path[len-1] == '*'); } const char *det_list_target(const char *path) { const char *res; if (path == NULL) goto unk; res = strrchr(path, '/'); if (res == NULL) goto unk; return res + 1; unk:; return ""; } void dep_init(void) { deps = ht_resize(ht_alloc(0), 128); } void dep_uninit(void) { ht_free(deps); } int is_dep_known(const char *name) { return (ht_get(deps, name) != NULL); } void require_all(int fatal) { ht_entry_t *h; for(h = ht_first(deps); h != NULL; h = ht_next(deps, h)) require(h->key, 0, fatal); } fungw-1.2.0/scconfig/src/default/deps_default.h0000644000175100017510000000003611260021745017653 0ustar svnsvnvoid deps_default_init(void); fungw-1.2.0/scconfig/src/default/find_str.c0000644000175100017510000000663413422277426017044 0ustar svnsvn/* scconfig - detection of standard library features: strings Copyright (C) 2017 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" int find_strcasecmp(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " if ((strcasecmp(\"foo\", \"FoO\") == 0) && (strcasecmp(\"foo\", \"bar\") != 0))" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for strcasecmp()... "); logprintf(logdepth, "find_fs_strcasecmp: trying to find strcasecmp...\n"); logdepth++; if (try_icl(logdepth, "str/strcasecmp", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "str/strcasecmp"); } int find_strncasecmp(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " if ((strncasecmp(\"foo1\", \"FoO2\", 3) == 0) && (strncasecmp(\"foo1\", \"bar2\", 3) != 0))" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for strncasecmp()... "); logprintf(logdepth, "find_fs_strncasecmp: trying to find strncasecmp...\n"); logdepth++; if (try_icl(logdepth, "str/strncasecmp", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "str/strncasecmp"); } int find_stricmp(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "#include " NL "int main() {" NL " if ((stricmp(\"foo\", \"FoO\") == 0) && (stricmp(\"foo\", \"bar\") != 0))" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for stricmp()... "); logprintf(logdepth, "find_fs_stricmp: trying to find stricmp...\n"); logdepth++; if (try_icl(logdepth, "str/stricmp", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "str/stricmp"); } int find_strnicmp(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "#include " NL "int main() {" NL " if ((strnicmp(\"foo1\", \"FoO2\", 3) == 0) && (strnicmp(\"foo1\", \"bar2\", 3) != 0))" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for strnicmp()... "); logprintf(logdepth, "find_fs_strnicmp: trying to find strnicmp...\n"); logdepth++; if (try_icl(logdepth, "str/strnicmp", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "str/strnicmp"); } fungw-1.2.0/scconfig/src/default/main_lib.h0000644000175100017510000000025412661010062016764 0ustar svnsvnint main_init(void); int main_process_args(int argc, char *argv[]); void main_uninit(void); /* internal */ void init(void); void uninit(void); void run_custom_reqs(void); fungw-1.2.0/scconfig/src/default/arg.c0000644000175100017510000001157313646327140016000 0ustar svnsvn/* scconfig - command line argument processing Copyright (C) 2009..2012 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include "db.h" #include "arg.h" #include "dep.h" #include "log.h" #include "libs.h" argtbl_t main_argument_table[] = { {"import", NULL, import_args, "Import saved config (sub)tree"}, {"target", "/arg/sys/target", NULL, "set cross compilation target (prefix)"}, {"target-name", "/arg/sys/target-name", NULL, "set cross compilation target (system name)"}, {"target-shell","/arg/sys/target-shell",NULL, "set the shell on cross compilation target"}, {"emu", "/arg/sys/emu", NULL, "emulator for testing cross compiled executables with"}, {"pkg-config", "/arg/sys/pkg-config", NULL, "path to pkg-config to use"}, {"pkg-config-zap","/arg/sys/pkg-config-zap",NULL, "ignore pkg-config results by this regex pattern"}, /* wildcard rules for icl() control */ {"^ldflags/", NULL, import_icl, "force LDFLAGS for a node"}, {"^cflags/", NULL, import_icl, "force CFLAGS for a node"}, {"^includes/", NULL, import_icl, "force #includes for a node"}, {"^prefix/", NULL, import_icl, "force using prefix path for detecting the node"}, /* the followings are autoconf compatibility translations */ {"CC", "/arg/cc/cc", NULL, "Force using a C compiler (command line)"}, {"CFLAGS", "/arg/cc/cflags", NULL, "Force using a CFLAGS for C compilation"}, {"LDFLAGS", "/arg/cc/ldflags", NULL, "Force using a LDFLAGS for linking"}, {"LDL", "/arg/libs/ldl", NULL, "Force using -ldl string"}, {"gpmi-prefix", "/arg/gpmi/prefix", NULL, NULL}, {NULL, NULL, NULL, NULL} }; void process_args(int argc, char *argv[]) { int n; char *key, *value; argtbl_t *a; int found, tainted = 0; db_mkdir("/arg"); logprintf(0, "CLI arg 0: '%s'\n", argv[0]); for(n = 1; n < argc; n++) { key = argv[n]; logprintf(0, "CLI arg %d: '%s'\n", n, key); while(*key == '-') key++; value = str_chr(key, '='); found = 0; if (value != NULL) { *value = '\0'; value++; if (strcmp(key, "without") == 0) { char *tmp; if (*value != '/') { const char **r, *roots[] = {"target", "host", "runtime", NULL}; for(r = roots; *r != NULL; r++) { tmp = str_concat("/", "/arg/without", *r, value, NULL); put(tmp, strue); free(tmp); } } else { tmp = str_concat("/", "/arg/without", value+1, NULL); put(tmp, strue); free(tmp); } found = 1; } else { /* Look in the argument translate table */ for(a = main_argument_table; a->arg != NULL; a++) { if (((a->arg[0] == '^') && (strncmp(a->arg+1, key, strlen(a->arg+1)) == 0)) || (strcmp(a->arg, key) == 0)) { found = 1; if (a->callback != NULL) { if (a->callback(key, value) != 0) { error("Processing argument '%s' failed in the callback\n", argv[n]); abort(); } } if (a->path != NULL) put(a->path, value); } } } /* Look in known deps table or /arg */ if (found == 0) { if ((is_dep_known(key)) || (strncmp(key, "/arg/", 5) == 0)) { put(key, value); found = 1; } } } if (found == 0) { if (custom_arg(key, value) == 0) { error("Unknown argument '%s'\n", key); tainted++; } } } if (tainted) exit(1); } void help_default_args(FILE *f, const char *prefix) { argtbl_t *a; if (prefix == NULL) prefix = ""; fprintf(f, "%sscconfig generic command line arguments:\n", prefix); for(a = main_argument_table; a->arg != NULL; a++) { char *tmp; if (a->help == NULL) continue; if (*a->arg == '^') { tmp = str_concat("", a->arg+1, "...=...", NULL); fprintf(f, "%s --%-22s %s\n", prefix, tmp, a->help); } else { tmp = str_concat("", a->arg, "=...", NULL); fprintf(f, "%s --%-22s %s\n", prefix, tmp, a->help); } free(tmp); } } fungw-1.2.0/scconfig/src/default/hooks.h0000644000175100017510000000233413367045120016345 0ustar svnsvn/* Runs when a custom command line argument is found returns true if no further argument processing should be done */ int hook_custom_arg(const char *key, const char *value); /* If any of the int hooks return non-zero, that means failure and stops the whole process */ /* Runs before anything else */ int hook_preinit(void); /* Runs after initialization */ int hook_postinit(void); /* Runs after all arguments are read and parsed */ int hook_postarg(void); /* Runs when things should be detected for the host system (tools compiled for and/or run on compilation host) */ int hook_detect_host(void); /* Runs when things should be detected for the target system (tools compiled on the compilation host but running on the target)*/ int hook_detect_target(void); /* Runs when things should be detected for the runtime system (tools that will run only on the target, production runtime, not during compilation or installation) */ int hook_detect_runtime(void); /* Runs after detection hooks, should generate the output (Makefiles, etc.) */ int hook_generate(void); /* Runs before everything is uninitialized */ void hook_preuninit(void); /* Runs at the very end, when everything is already uninitialized */ void hook_postuninit(void); fungw-1.2.0/scconfig/src/default/find_target.c0000644000175100017510000000351613334271760017513 0ustar svnsvn/* scconfig - glue layer for proper crosscompiling to target Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include "db.h" #include "libs.h" int find_target(const char *name, int logdepth, int fatal) { const char *target = get("/arg/sys/target"); const char *emu = get("/arg/sys/emu"); (void) logdepth; /* to suppress compiler warnings about not using logdepth */ (void) fatal; /* to suppress compiler warnings about not using fatal */ /* Does target differ from host? */ if (target == NULL) { db_link("/host", "/target"); #ifdef RUNTIME db_link("/host", "/runtime"); #endif put("/target/sys/cross", sfalse); put("/target/sys/cross_blind", sfalse); return 0; } else db_mkdir("/target"); put("/target/sys/target", target); put("/target/sys/cross", strue); if (emu != NULL) put("/target/sys/emu", emu); /* If so, check if emulator is provided */ cross_blind = ((emu == NULL) || (*emu == '\0')); put("/target/sys/cross_blind", cross_blind ? strue : sfalse); return 0; } fungw-1.2.0/scconfig/src/default/regex.h0000644000175100017510000000036312562533767016353 0ustar svnsvn#ifndef REGEX_H #define REGEX_H extern const char *bopat[]; extern const char *eopat[]; extern char *re_comp(const char *); extern int re_exec(const char *); extern void re_modw(char *); char *re_subs_dup(char *sub); #endif /* REGEX_H */ fungw-1.2.0/scconfig/src/default/find_fstools.c0000644000175100017510000004755713640533627017736 0ustar svnsvn/* scconfig - detection of file system tools Copyright (C) 2009..2012 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" #include "dep.h" static int test_cp_ln(int logdepth, const char *command, int link) { char *src, *dst, *src_esc, *dst_esc; char *cmd, *result; char *test_string = "works."; int ret; logprintf(logdepth, "trying '%s'\n", command); src = tempfile_dump(test_string, ""); dst = tempfile_new(""); if (link) unlink(dst); src_esc = shell_escape_dup(src); dst_esc = shell_escape_dup(dst); cmd = malloc(strlen(command) + strlen(src_esc) + strlen(dst_esc) + 32); sprintf(cmd, "%s %s %s", command, src_esc, dst_esc); run_shell(logdepth, cmd, NULL); free(cmd); free(src_esc); free(dst_esc); result = load_file(dst); ret = !strcmp(result, test_string); logprintf(logdepth+1, "result: '%s' == '%s' (%d)\n", result, test_string, ret); free(result); unlink(src); free(src); result = load_file(dst); if (link) { if (strcmp(result, test_string) == 0) { report("Warning: link is copy (or hard link). "); logprintf(logdepth+1, "Warning: link is copy (or hard link).\n"); } } else { if (strcmp(result, test_string) != 0) { report("Warning: copy is symlink. "); logprintf(logdepth+1, "Warning: copy is symlink.\n"); } } free(result); if (ret) { if (link) put("fstools/ln", command); else put("fstools/cp", command); report("OK (%s)\n", command); } unlink(dst); free(dst); return ret; } static int test_mv(int logdepth, const char *command) { char *src, *dst, *src_esc, *dst_esc; char *cmd, *result; char *test_string = "works."; int ret; logprintf(logdepth, "trying '%s'\n", command); src = tempfile_dump(test_string, ""); dst = tempfile_new(""); unlink(dst); src_esc = shell_escape_dup(src); dst_esc = shell_escape_dup(dst); cmd = malloc(strlen(command) + strlen(src_esc) + strlen(dst_esc) + 32); sprintf(cmd, "%s %s %s", command, src_esc, dst_esc); run_shell(logdepth, cmd, NULL); free(cmd); free(src_esc); free(dst_esc); result = load_file(dst); ret = !strcmp(result, test_string); logprintf(logdepth+1, "result: '%s' == '%s' (%d)\n", result, test_string, ret); free(result); if (file_size(src) > 0) { report("Warning: mv is copy. "); logprintf(logdepth+1, "Warning: mv is copy.\n"); } if (ret) { put("fstools/mv", command); report("OK (%s)\n", command); } unlink(dst); unlink(src); free(dst); free(src); return ret; } static int test_mkdir(int logdepth, const char *command) { char *dir, *file; char *dir_esc; char *cmd, *result; char *test_string = "works."; int ret = 0, had_p; FILE *f; logprintf(logdepth, "trying '%s'\n", command); dir = tempfile_new(""); dir_esc = shell_escape_dup(dir); unlink(dir); had_p = is_dir("-p"); cmd = malloc(strlen(command) + strlen(dir_esc) + 32); sprintf(cmd, "%s %s", command, dir_esc); run_shell(logdepth, cmd, NULL); free(cmd); file = malloc(strlen(dir) + 32); sprintf(file, "%s/test", dir); f = fopen(file, "w"); if (f != NULL) { fputs(test_string, f); fclose(f); result = load_file(file); if (strcmp(result, test_string) == 0) ret = 1; free(result); } unlink(file); unlink(dir); cmd = malloc(strlen(dir) + 32); sprintf(cmd, "rmdir %s", dir_esc); run_shell(logdepth, cmd, NULL); free(cmd); free(file); free(dir); free(dir_esc); /* This is a bit ugly, but on win32 or other systems where mkdir works but -p doesn't have an effect, a directory called -p may be left over... */ if ((!had_p) && (is_dir("-p"))) { unlink("-p"); return 0; } if (ret != 0) { put("fstools/mkdir", command); report("OK (%s)\n", command); } return ret; } static int test_rm(int logdepth, const char *command) { char *src, *src_esc, *cmd, *test_string = "works."; int ret; logprintf(logdepth, "trying '%s'\n", command); src = tempfile_dump(test_string, ""); if (file_size(src) < 0) { report("error: can't create temp file\n"); free(src); return 0; } src_esc = shell_escape_dup(src); cmd = malloc(strlen(command) + strlen(src_esc) + 32); sprintf(cmd, "%s %s", command, src_esc); run_shell(logdepth, cmd, NULL); free(cmd); free(src_esc); ret = file_size(src) < 0; if (ret) { put("fstools/rm", command); report("OK (%s)\n", command); } else unlink(src); free(src); return ret; } static int test_ar(int logdepth, const char *command) { char *src, *dst, *src_esc, *dst_esc; char *cmd, *result, *expected; char *test_string = "works."; const char *path_sep; int ret = 0; logprintf(logdepth, "trying '%s'\n", command); path_sep = get("sys/path_sep"); src = tempfile_dump(test_string, ""); dst = tempfile_new(""); unlink(dst); src_esc = shell_escape_dup(src); dst_esc = shell_escape_dup(dst); cmd = malloc(strlen(command) + strlen(src_esc) + strlen(dst_esc) + 32); sprintf(cmd, "%s ru %s %s", command, dst_esc, src_esc); run_shell(logdepth, cmd, NULL); sprintf(cmd, "%s t %s", command, dst_esc); run_shell(logdepth, cmd, &result); free(cmd); free(dst_esc); free(src_esc); if (result != NULL) { expected = str_rchr(src, *path_sep); if (expected == NULL) expected = src; else expected++; logprintf(logdepth, "test_ar path_sep='%s' expected='%s' result='%s'\n", path_sep, expected, result); ret = strncmp(expected, result, strlen(expected)) == 0; if (ret) { put("fstools/ar", command); report("OK (%s)\n", command); } free(result); } unlink(src); unlink(dst); free(src); free(dst); return ret; } static int test_ranlib(int logdepth, const char *command, const char *obj) { char *cmd, *archive, *archive_esc, *obj_esc; const char *ar; int ret; ar = get("fstools/ar"); logprintf(logdepth, "trying '%s'\n", command); archive = tempfile_new(".a"); archive_esc = shell_escape_dup(archive); obj_esc = shell_escape_dup(obj); cmd = malloc(strlen(command) + strlen(obj_esc) + strlen(archive_esc) + 64); sprintf(cmd, "%s r %s %s", ar, archive_esc, obj_esc); unlink(archive); ret = run_shell(logdepth, cmd, NULL) == 0; if (!ret) goto fin; sprintf(cmd, "%s %s", command, archive_esc); ret = run_shell(logdepth, cmd, NULL) == 0; if (ret) { put("fstools/ranlib", command); report("OK (%s)\n", command); } fin:; unlink(archive); free(archive); free(cmd); free(archive_esc); free(obj_esc); return ret; } static int test_awk(int logdepth, const char *command) { char cmd[1024]; char *out; int ret = 0; char *script, *script_esc; /* For some reason windows awk doesn't like the code with NLs */ char *test_awk = "BEGIN {" " gsub(\"b\", \"B\", t);" " print t;" "}"; logprintf(logdepth, "trying '%s'\n", command); script = tempfile_dump(test_awk, ".awk"); script_esc = shell_escape_dup(script); sprintf(cmd, "%s -v \"t=blobb\" -f %s", command, script_esc); free(script_esc); run_shell(logdepth, cmd, &out); unlink(script); free(script); if ((out != NULL) && (strncmp(out, "BloBB", 5) == 0)) { put("fstools/awk", command); report("OK (%s)\n", command); ret = 1; } free(out); return ret; } static int test_cat(int logdepth, const char *command) { char cmd[1024]; char *out; int ret = 0; char *fn, *fn_esc; const char *test_str = "hello world"; logprintf(logdepth, "trying '%s'\n", command); fn = tempfile_dump(test_str, ".txt"); fn_esc = shell_escape_dup(fn); sprintf(cmd, "%s %s", command, fn_esc); run_shell(logdepth, cmd, &out); unlink(fn); free(fn); free(fn_esc); if ((out != NULL) && (strncmp(out, test_str, strlen(test_str)) == 0)) { put("fstools/cat", command); report("OK (%s)\n", command); ret = 1; } free(out); return ret; } static int test_sed(int logdepth, const char *command) { char cmd[1024]; char *out; int ret = 0; char *fn, *fn_esc; const char *test_str_in = "hello world"; const char *test_str_out = "he11o wor1d"; logprintf(logdepth, "trying '%s'\n", command); fn = tempfile_dump(test_str_in, ".txt"); fn_esc = shell_escape_dup(fn); sprintf(cmd, "%s \"s/l/1/g\" < %s", command, fn_esc); run_shell(logdepth, cmd, &out); unlink(fn); free(fn); free(fn_esc); if ((out != NULL) && (strncmp(out, test_str_out, strlen(test_str_out)) == 0)) { put("fstools/sed", command); report("OK (%s)\n", command); ret = 1; } free(out); return ret; } static int test_chmodx(int logdepth, const char *command) { char *cmd, *tmp, *tmp_esc, *out, *s; int ret; logprintf(logdepth, "trying '%s'\n", command); tmp = tempfile_dump("#!/bin/sh\necho OK\n", ".bat"); tmp_esc = shell_escape_dup(tmp); cmd = malloc(strlen(command) + strlen(tmp_esc) + 16); sprintf(cmd, "%s %s", command, tmp_esc); ret = run_shell(logdepth, cmd, NULL) == 0; free(cmd); if (!ret) { free(tmp_esc); return ret; } ret = run(logdepth+1, tmp_esc, &out); free(tmp_esc); if (ret == 0) { for(s = out; s != NULL; s = str_chr(s, '\n')) { logprintf(logdepth+1, "chmod line to test: '%s'\n", s); if ((s[0] == 'O') && (s[1] == 'K')) { logprintf(logdepth+2, "(OK)\n"); ret = 1; break; } s++; } } else ret = 0; free(out); if (ret) { put("fstools/chmodx", command); logprintf(logdepth, "chmodx command validated: '%s'\n", command); report("OK (%s)\n", command); } unlink(tmp); return ret; } static int test_file(int logdepth, const char *node, const char *command) { char cmd[1024]; char *out; int ret = 0; char *fn, *fn_esc; logprintf(logdepth, "trying '%s'\n", command); fn = tempfile_dump("plain text file\r\n", ".txt"); fn_esc = shell_escape_dup(fn); sprintf(cmd, "%s %s", command, fn_esc); run_shell(logdepth, cmd, &out); unlink(fn); free(fn); free(fn_esc); if ((out != NULL) && (strstr(out, "text") != NULL)) { put(node, command); report("OK (%s)\n", command); ret = 1; } free(out); return ret; } int find_fstools_cp(const char *name, int logdepth, int fatal) { const char *cp; (void) fatal; /* to suppress compiler warnings about not using fatal */ report("Checking for cp... "); logprintf(logdepth, "find_fstools_cp: trying to find cp...\n"); logdepth++; cp = get("/arg/fstools/cp"); if (cp == NULL) { if (test_cp_ln(logdepth, "cp -rp", 0)) return 0; if (test_cp_ln(logdepth, "cp -r", 0)) return 0; if (test_cp_ln(logdepth, "copy /r", 0)) return 0; /* wine */ } else { report(" user provided (%s)...", cp); if (test_cp_ln(logdepth, cp, 0)) return 0; } return 1; } int find_fstools_ln(const char *name, int logdepth, int fatal) { const char *ln; (void) fatal; /* to suppress compiler warnings about not using fatal */ report("Checking for ln... "); logprintf(logdepth, "find_fstools_ln: trying to find ln...\n"); logdepth++; ln = get("/arg/fstools/ln"); if (ln == NULL) { if (test_cp_ln(logdepth, "ln -sf",1 )) return 0; if (test_cp_ln(logdepth, "ln -s",1 )) return 0; if (test_cp_ln(logdepth, "ln", 1)) return 0; /* "mklink /H" -> win32 equivalent to "ln" */ /* "cp -s" -> same as "ln -s" */ /* "cp -l" -> same as "ln" */ if (test_cp_ln(logdepth, "cp", 1)) return 0; } else { report(" user provided (%s)...", ln); if (test_cp_ln(logdepth, ln, 1)) return 0; } return 1; } int find_fstools_mv(const char *name, int logdepth, int fatal) { const char *mv; (void) fatal; /* to suppress compiler warnings about not using fatal */ report("Checking for mv... "); logprintf(logdepth, "find_fstools_mv: trying to find mv...\n"); logdepth++; mv = get("/arg/fstools/mv"); if (mv == NULL) { if (test_mv(logdepth, "mv")) return 0; if (test_mv(logdepth, "move")) return 0; /* win32 */ if (test_mv(logdepth, "cp")) return 0; } else { report(" user provided (%s)...", mv); if (test_mv(logdepth, mv)) return 0; } return 1; } int find_fstools_rm(const char *name, int logdepth, int fatal) { const char *rm; (void) fatal; /* to suppress compiler warnings about not using fatal */ report("Checking for rm... "); logprintf(logdepth, "find_fstools_rm: trying to find rm...\n"); logdepth++; rm = get("/arg/fstools/rm"); if (rm == NULL) { if (test_rm(logdepth, "rm -rf")) return 0; if (test_rm(logdepth, "rm -f")) return 0; if (test_rm(logdepth, "rm")) return 0; if (test_rm(logdepth, "del")) return 0; /* for win32 */ } else { report(" user provided (%s)...", rm); if (test_rm(logdepth, rm)) return 0; } return 1; } int find_fstools_mkdir(const char *name, int logdepth, int fatal) { const char *mkdir; (void) fatal; /* to suppress compiler warnings about not using fatal */ report("Checking for mkdir... "); logprintf(logdepth, "find_fstools_mkdir: trying to find mkdir...\n"); logdepth++; mkdir = get("/arg/fstools/mkdir"); if (mkdir == NULL) { if (test_mkdir(logdepth, "mkdir -p")) return 0; if (test_mkdir(logdepth, "md")) return 0; /* for win32 */ } else { report(" user provided (%s)...", mkdir); if (test_mkdir(logdepth, mkdir)) return 0; } return 1; } int find_fstools_ar(const char *name, int logdepth, int fatal) { const char *ar, *target; char *targetar; int len; (void) fatal; /* to suppress compiler warnings about not using fatal */ require("sys/path_sep", logdepth, fatal); report("Checking for ar... "); logprintf(logdepth, "find_fstools_ar: trying to find ar...\n"); logdepth++; ar = get("/arg/fstools/ar"); if (ar == NULL) { target = get("/arg/sys/target"); if (target != NULL) { logprintf(logdepth+1, "find_ar: crosscompiling for '%s', looking for target ar\n", target); len = strlen(target); targetar = malloc(len + 8); memcpy(targetar, target, len); strcpy(targetar + len, "-ar"); if (test_ar(logdepth, targetar)) { free(targetar); return 0; } free(targetar); } if (test_ar(logdepth, "ar")) return 0; if (test_ar(logdepth, "/usr/bin/ar")) return 0; } else { report(" user provided (%s)...", ar); if (test_ar(logdepth, ar)) return 0; } return 1; } int find_fstools_ranlib(const char *name, int logdepth, int fatal) { const char *ranlib, *target; char *targetranlib; int len; char *test_code = NL "int zero() { return 0; }" NL; char *obj = ".o"; (void) fatal; /* to suppress compiler warnings about not using fatal */ require("fstools/ar", logdepth, fatal); require("cc/cc", logdepth, fatal); report("Checking for ranlib... "); logprintf(logdepth, "find_fstools_ranlib: trying to find ranlib...\n"); logdepth++; logprintf(logdepth, "compiling test object...\n"); if (compile_code(logdepth+1, test_code, &obj, NULL, "-c", NULL) != 0) { logprintf(logdepth, "ERROR: Can't compile test object\n"); report("ERROR: Can't compile test object\n"); abort(); } ranlib = get("/arg/fstools/ranlib"); if (ranlib == NULL) { target = get("/arg/sys/target"); if (target != NULL) { logprintf(logdepth+1, "find_ranlib: crosscompiling for '%s', looking for target ranlib\n", target); len = strlen(target); targetranlib = malloc(len + 16); memcpy(targetranlib, target, len); strcpy(targetranlib + len, "-ranlib"); if (test_ranlib(logdepth, targetranlib, obj)) { free(targetranlib); return 0; } free(targetranlib); } if (test_ranlib(logdepth, "ranlib", obj)) goto found; if (test_ranlib(logdepth, "/usr/bin/ranlib", obj)) goto found; if (test_ranlib(logdepth, "ar -s", obj)) goto found; if (test_ranlib(logdepth, "/usr/bin/ar -s", obj)) goto found; /* some systems (for example IRIX) can't run s without doing something else; t is harmless */ if (test_ranlib(logdepth, "ar ts", obj)) goto found; if (test_ranlib(logdepth, "/usr/bin/ar ts", obj)) goto found; /* final fallback: some systems (for example minix3) simply do not have ranlib or ar equivalent; it's easier to detect a dummy command than to force conditions into Makefiles */ if (test_ranlib(logdepth, "true", obj)) goto found; } else { report(" user provided (%s)...", ranlib); if (test_ranlib(logdepth, ranlib, obj)) goto found; } unlink(obj); free(obj); return 1; found:; unlink(obj); free(obj); return 0; } int find_fstools_awk(const char *name, int logdepth, int fatal) { const char *awk; (void) fatal; /* to suppress compiler warnings about not using fatal */ report("Checking for awk... "); logprintf(logdepth, "find_fstools_awk: trying to find awk...\n"); logdepth++; awk = get("/arg/fstools/awk"); if (awk == NULL) { if (test_awk(logdepth, "awk")) return 0; if (test_awk(logdepth, "gawk")) return 0; if (test_awk(logdepth, "mawk")) return 0; if (test_awk(logdepth, "nawk")) return 0; } else { report(" user provided (%s)...", awk); if (test_awk(logdepth, awk)) return 0; } return 1; } int find_fstools_chmodx(const char *name, int logdepth, int fatal) { const char *chmod; (void) fatal; /* to suppress compiler warnings about not using fatal */ report("Checking for chmod to executable... "); logprintf(logdepth, "find_fstools_awk: trying to find chmod to executable...\n"); logdepth++; chmod = get("/arg/fstools/chmodx"); if (chmod == NULL) { if (test_chmodx(logdepth, "chmod +x")) return 0; if (test_chmodx(logdepth, "chmod 755")) return 0; if (test_chmodx(logdepth, "")) return 0; /* on some systems we don't need to do anything */ } else { report(" user provided (%s)...", chmod); if (test_chmodx(logdepth, chmod)) return 0; } return 1; } int find_fstools_cat(const char *name, int logdepth, int fatal) { const char *cat; (void) fatal; /* to suppress compiler warnings about not using fatal */ report("Checking for cat... "); logprintf(logdepth, "find_fstools_cat: trying to find cat...\n"); logdepth++; cat = get("/arg/fstools/cat"); if (cat == NULL) { if (test_cat(logdepth, "cat")) return 0; if (test_cat(logdepth, "type")) return 0; } else { report(" user provided (%s)...", cat); if (test_cat(logdepth, cat)) return 0; } return 1; } int find_fstools_sed(const char *name, int logdepth, int fatal) { const char *sed; (void) fatal; /* to suppress compiler warnings about not using fatal */ report("Checking for sed... "); logprintf(logdepth, "find_fstools_sed: trying to find sed...\n"); logdepth++; sed = get("/arg/fstools/sed"); if (sed == NULL) { if (test_sed(logdepth, "sed")) return 0; } else { report(" user provided (%s)...", sed); if (test_sed(logdepth, sed)) return 0; } return 1; } int find_fstools_file_l(const char *name, int logdepth, int fatal) { const char *file; (void) fatal; /* to suppress compiler warnings about not using fatal */ report("Checking for file... "); logprintf(logdepth, "find_fstools_file_l: trying to find file -L...\n"); logdepth++; file = get("/arg/fstools/file_l"); if (file == NULL) { if (test_file(logdepth, "fstools/file_l", "file -L")) return 0; if (test_file(logdepth, "fstools/file_l", "file")) return 0; } else { report(" user provided (%s)...", file); if (test_file(logdepth, "fstools/file_l", file)) return 0; } return 1; } int find_fstools_file(const char *name, int logdepth, int fatal) { const char *file; (void) fatal; /* to suppress compiler warnings about not using fatal */ report("Checking for file... "); logprintf(logdepth, "find_fstools_file: trying to find file...\n"); logdepth++; file = get("/arg/fstools/file"); if (file == NULL) { if (test_file(logdepth, "fstools/file", "file")) return 0; } else { report(" user provided (%s)...", file); if (test_file(logdepth, "fstools/file", file)) return 0; } return 1; } fungw-1.2.0/scconfig/src/default/find_thread.c0000644000175100017510000001327414003560750017470 0ustar svnsvn/* scconfig - detection of standard library features Copyright (C) 2009,2017 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" int find_lib_lpthread(const char *name, int logdepth, int fatal) { const char *lpthread; char *s; int ret = 0; char *test_c_recursive = NL "#define _GNU_SOURCE 1 /* Needed for recursive thread-locking */" NL "#include " NL "#include " NL "pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;" NL "int main() {" NL " pthread_attr_t a;" NL " if (pthread_attr_init(&a) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL ; char *test_c_simple = NL "#include " NL "#include " NL "int main() {" NL " pthread_attr_t a;" NL " if (pthread_attr_init(&a) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL ; require("cc/cc", logdepth, fatal); report("Checking for -lpthread... "); logprintf(logdepth, "find_lib_lpthread: trying to find lpthread...\n"); logdepth++; lpthread = get("/arg/libs/lpthread"); if (lpthread != NULL) { put("libs/lpthread", lpthread); report("User provided... "); s = strclone(lpthread); } else s = strclone("-lpthread"); if (try_icl(logdepth, NULL, test_c_recursive, NULL, NULL, s)) { put("libs/lpthread", s); put("libs/lpthread-recursive", strue); report("OK, recursive (%s)\n", s); } else if (try_icl(logdepth, NULL, test_c_simple, NULL, NULL, s)) { put("libs/lpthread", s); put("libs/lpthread-recursive", sfalse); report("OK, NOT RECURSIVE (%s)\n", s); } else ret = 1; free(s); return ret; } int find_thread_semget(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "int main()" NL "{" NL " int semid = semget(IPC_PRIVATE, 1, IPC_CREAT);" NL " if (semid < 0) return 0;" NL " if(semctl(semid, 0, IPC_RMID) < 0) return 0;" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; const char *node = "thread/semget"; char **inc, *incs[] = {"#include \n#include \n#include ", "#include \n#include ", "#include \n#include ", NULL}; if (require("cc/cc", logdepth, fatal)) return try_fail(logdepth, node); report("Checking for semget... "); logprintf(logdepth, "find_semget:\n"); logdepth++; for(inc = incs; *inc != NULL; inc++) if (try_icl(logdepth, node, test_c, *inc, NULL, NULL) != 0) return 0; return try_fail(logdepth, node); } int find_thread_pthread_create(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "void* test_thread(void* param)" NL "{" NL " return NULL;" NL "}" NL "int main()" NL "{" NL " pthread_t pt;" NL " if (pthread_create(&pt, NULL, test_thread, NULL) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; const char *node = "thread/pthread_create"; char **inc, *incs[] = {"#include ", "#include \n#include ", NULL}; const char *lpthread; char* s; if (require("cc/cc", logdepth, fatal)) return try_fail(logdepth, node); report("Checking for pthread_create... "); logprintf(logdepth, "find_pthread_create:\n"); logdepth++; lpthread = get("/arg/libs/lpthread"); if (lpthread != NULL) { report("User provided... "); s = strclone(lpthread); } else s = strclone("-lpthread"); for(inc = incs; *inc != NULL; inc++) if (try_icl(logdepth, node, test_c, *inc, NULL, s) != 0) { free(s); return 0; } free(s); return try_fail(logdepth, node); } int find_thread_CreateSemaphore(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "int main()" NL "{" NL " if (CreateSemaphore(NULL, 1, 1, NULL))" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; const char *node = "thread/CreateSemaphore"; if (require("cc/cc", logdepth, fatal)) return try_fail(logdepth, node); report("Checking for CreateSemaphore... "); logprintf(logdepth, "find_thread_CreateSemaphore:\n"); logdepth++; if (try_icl(logdepth, node, test_c, "#include ", NULL, NULL) != 0) return 0; return try_fail(logdepth, node); } int find_thread_CreateThread(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "DWORD WINAPI test_thread(void* param)" NL "{" NL " return 0;" NL "}" NL "int main()" NL "{" NL " if (CreateThread(NULL, 0, test_thread, NULL, 0, NULL))" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; const char *node = "thread/CreateThread"; if (require("cc/cc", logdepth, fatal)) return try_fail(logdepth, node); report("Checking for CreateThread... "); logprintf(logdepth, "find_thread_CreateThread:\n"); logdepth++; if (try_icl(logdepth, node, test_c, "#include ", NULL, NULL) != 0) return 0; return try_fail(logdepth, node); } fungw-1.2.0/scconfig/src/default/find_proc.c0000644000175100017510000002256613424303425017170 0ustar svnsvn/* scconfig - detection of standard library features (processes) Copyright (C) 2016 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" int find_proc__spawnvp(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "int main() {" NL " const char *a[3] = {\"/c\", \"echo OK\", NULL};" NL " _spawnvp(_P_WAIT, \"cmd\", a);" NL " return 0;" NL "}" NL ; require("cc/cc", logdepth, fatal); report("Checking for _spawnvp... "); logprintf(logdepth, "find_proc__spawnvp: trying to find _spawnvp...\n"); logdepth++; if (try_icl(logdepth, "libs/proc/_spawnvp", test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, "libs/proc/_spawnvp"); } int find_proc_CreateProcessA(const char *name, int logdepth, int fatal) { const char *key = "libs/proc/CreateProcessA"; char *test_c = NL "#include " NL "char buf[2000];" NL "int main() {" NL " const char *cmd;" NL " STARTUPINFOA si;" NL " PROCESS_INFORMATION pi;" NL " cmd = getenv(\"COMSPEC\");" NL " if (cmd == NULL) { /* fallback to guessing */" NL " UINT len = GetWindowsDirectoryA(buf, sizeof(buf));" NL " strcpy(buf+len, \"\\\\system32\\\\cmd.exe\");" NL " cmd = buf;" NL " }" NL " memset(&si, 0, sizeof(si)); si.cb = sizeof(si);" NL " if (!CreateProcessA(cmd, \"/c \\\"echo OK\\\"\"," NL " NULL, NULL, TRUE," NL " 0, NULL, NULL, &si, &pi))" NL " return 1;" NL " if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0)" NL " abort();" NL " return 0;" NL "}" NL ; require("cc/cc", logdepth, fatal); report("Checking for CreateProcessA... "); logprintf(logdepth, "find_proc_CreateProcessA: trying to find CreateProcessA...\n"); logdepth++; if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, key); } int find_proc_fork(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " if (fork() == 0) { return 0; }" NL " puts(\"OK\");" NL " return 0;" NL "}" NL ; /* NOTE: can't print OK from the child process because of a possible race with the parent immediately exiting without wait(). */ require("cc/cc", logdepth, fatal); report("Checking for fork... "); logprintf(logdepth, "find_proc_fork: trying to find fork...\n"); logdepth++; if (try_icl(logdepth, "libs/proc/fork", test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, "libs/proc/fork"); } int find_proc_wait(const char *name, int logdepth, int fatal) { char *inc; const char *inc1; char test_c[1024]; char *test_c_in = NL "%s\n" NL "#include " NL "#include " NL "int main() {" NL " int st = 0;" NL " if (fork() == 0) {" NL " printf(\"O\");" NL " return 42;" NL " }" NL " wait(&st);" NL " if (WIFEXITED(st) && (WEXITSTATUS(st) == 42))" NL " printf(\"K\");" NL " else" NL " printf(\"%%d\", st);" NL " printf(\"\\n\");" NL " return 0;" NL "}" NL ; require("cc/cc", logdepth, fatal); if (require("libs/proc/fork", logdepth, fatal)) return try_fail(logdepth, "libs/proc/wait"); report("Checking for wait... "); logprintf(logdepth, "find_proc_wait: trying to find wait...\n"); logdepth++; inc1 = get("libs/proc/fork/includes"); if (inc1 != NULL) { char *i, *o; inc = strclone(inc1); for(i = o = inc; *i != '\0'; i++,o++) { if ((i[0] == '\\') && (i[1] == 'n')) { *o = '\n'; i++; } else *o = *i; } *o = '\0'; sprintf(test_c, test_c_in, inc); free(inc); } else sprintf(test_c, test_c_in, ""); if (try_icl(logdepth, "libs/proc/wait", test_c, "#include \n#include ", NULL, NULL)) return 0; return try_fail(logdepth, "libs/proc/wait"); } int find_proc__getpid(const char *name, int logdepth, int fatal) { const char *key = "libs/proc/_getpid"; const char *test_c = NL "#include " NL "int main()" NL "{" NL no_implicit(int, "_getpid", "_getpid") NL " if (_getpid() != (-1))" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for _getpid()... "); logprintf(logdepth, "find_proc__getpid: trying to find _getpid()...\n"); logdepth++; if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, key); } int find_proc_getexecname(const char *name, int logdepth, int fatal) { const char *key = "libs/proc/getexecname"; const char *test_c = NL "void my_puts(const char *s);" NL "int main()" NL "{" NL no_implicit(const char*, "getexecname", "getexecname") NL " getexecname();" NL " my_puts(\"OK\");" NL " return 0;" NL "}" NL "#include " NL "void my_puts(const char *s)" NL "{" NL " puts(s);" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for getexecname()... "); logprintf(logdepth, "find_proc_getexecname: trying to find getexecname()...\n"); logdepth++; if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, key); } int find_proc_GetModuleFileNameA(const char *name, int logdepth, int fatal) { const char *key = "libs/proc/GetModuleFileNameA"; const char *test_c = NL "void my_puts(const char *s);" NL "char path_buffer[5000];" NL "int main()" NL "{" NL " HMODULE hModule = GetModuleHandleA(NULL);" NL " if (GetModuleFileNameA(hModule, path_buffer, sizeof(path_buffer)) != 0)" NL " my_puts(\"OK\");" NL " return 0;" NL "}" NL "#include " NL "void my_puts(const char *s)" NL "{" NL " puts(s);" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for GetModuleFileNameA()... "); logprintf(logdepth, "find_proc_GetModuleFileNameA: trying to find GetModuleFileNameA()...\n"); logdepth++; if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0; if (try_icl(logdepth, key, test_c, "#include ", NULL, "-lkernel32")) return 0; return try_fail(logdepth, key); } int find_proc_shmget(const char *name, int logdepth, int fatal) { const char *key = "libs/proc/shmget"; const char *test_c = NL "void my_puts(const char *s);" NL "int main()" NL "{" NL " int shmid;" NL " char *addr;" NL " shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT|IPC_EXCL|0600);" NL " if (shmid < 0)" NL " return 1;" NL " addr = (char *)shmat(shmid, (void *)0, 0);" NL " if (addr == (char *)0) {" NL " shmctl(shmid, IPC_RMID, (void *)0);" NL " return 1;" NL " }" NL " if (shmctl(shmid, IPC_RMID, (void *)0) != 0)" NL " return 1;" NL " if (addr[2] != 0)" NL " return 1;" NL " addr[0] = 'O'; addr[1] = 'K';" NL " my_puts(addr);" NL " return 0;" NL "}" NL "#include " NL "void my_puts(const char *s)" NL "{" NL " puts(s);" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for shmget()... "); logprintf(logdepth, "find_proc_shmget: trying to find shmget()...\n"); logdepth++; if (try_icl(logdepth, key, test_c, "#include \n#include ", NULL, NULL)) return 0; return try_fail(logdepth, key); } int find_proc_CreateFileMappingA(const char *name, int logdepth, int fatal) { const char *key = "libs/proc/CreateFileMappingA"; const char *test_c = NL "void my_puts(const char *s);" NL "#define MAP_BUF_SIZE 4096" NL "int main()" NL "{" NL " char *addr;" NL " HANDLE hMap = CreateFileMappingA(" NL " INVALID_HANDLE_VALUE," NL " NULL," NL " PAGE_READWRITE," NL " 0," NL " MAP_BUF_SIZE," NL " NULL);" NL " if (hMap == NULL)" NL " return 1;" NL " addr = (char *)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS," NL " 0, 0, MAP_BUF_SIZE);" NL " if (addr == NULL) {" NL " CloseHandle(hMap);" NL " return 1;" NL " }" NL " if (addr[2] != 0) {" NL " UnmapViewOfFile(addr);" NL " CloseHandle(hMap);" NL " return 1;" NL " }" NL " addr[0] = 'O'; addr[1] = 'K';" NL " my_puts(addr);" NL " UnmapViewOfFile(addr);" NL " CloseHandle(hMap);" NL " return 0;" NL "}" NL "#include " NL "void my_puts(const char *s)" NL "{" NL " puts(s);" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for CreateFileMappingA()... "); logprintf(logdepth, "find_proc_CreateFileMappingA: trying to find CreateFileMappingA()...\n"); logdepth++; if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0; if (try_icl(logdepth, key, test_c, "#include ", NULL, "-lkernel32")) return 0; return try_fail(logdepth, key); } fungw-1.2.0/scconfig/src/default/find_time.c0000644000175100017510000002042414003560750017152 0ustar svnsvn/* scconfig - detection of standard library features: time/date/sleep related calls Copyright (C) 2011..2012 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" int find_time_usleep(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " if (usleep(1) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for usleep()... "); logprintf(logdepth, "find_time_usleep: trying to find usleep...\n"); logdepth++; if (try_icl(logdepth, "libs/time/usleep", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "libs/time/usleep"); } int find_time_Sleep(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "int main() {" NL " Sleep(1);" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for Sleep()... "); logprintf(logdepth, "find_time_Sleep: trying to find Sleep...\n"); logdepth++; if (try_icl(logdepth, "libs/time/Sleep", test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, "libs/time/Sleep"); } int find_time_gettimeofday(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " struct timeval tv;" NL " if (gettimeofday(&tv, NULL) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for gettimeofday()... "); logprintf(logdepth, "find_time_gettimeofday: trying to find gettimeofday...\n"); logdepth++; if (try_icl(logdepth, "libs/time/gettimeofday", test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, "libs/time/gettimeofday"); } int find_time_ftime(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "int main() {" NL " struct timeb tb;" NL " if (ftime(&tb) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for ftime()... "); logprintf(logdepth, "find_time_ftime: trying to find ftime...\n"); logdepth++; if (try_icl(logdepth, "libs/time/ftime", test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, "libs/time/ftime"); } static const char timegm_test_c_template[] = NL "void my_puts(const char *s);" NL "int main() {" NL " struct tm tm;" NL " tm.tm_sec = 50;" NL " tm.tm_min = 30;" NL " tm.tm_hour = 6;" NL " tm.tm_mday = 1;" NL " tm.tm_mon = 11;" NL " tm.tm_year = 2018 - 1900;" NL " tm.tm_wday = 0;" NL " tm.tm_yday = 0;" NL " if (%s(&tm) != (time_t)(-1))" NL " my_puts(\"OK\");" NL " return 0;" NL "}" NL "#include " NL "void my_puts(const char *s)" NL "{" NL " puts(s);" NL "}" NL; int find_time_timegm(const char *name, int logdepth, int fatal) { char test_c[1000]; sprintf(test_c, timegm_test_c_template, "timegm"); require("cc/cc", logdepth, fatal); report("Checking for timegm()... "); logprintf(logdepth, "find_time_timegm: trying to find timegm...\n"); logdepth++; if (try_icl(logdepth, "libs/time/timegm", test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, "libs/time/timegm"); } int find_time_mkgmtime(const char *name, int logdepth, int fatal) { char test_c[1000]; const char *ldflags[] = {"","-lmsvcr120","-lmsvcr110","-lmsvcr100","-lmsvcr90","-lmsvcr80","-lmsvcr71","-lmsvcr70",NULL}; const char **ldf; sprintf(test_c, timegm_test_c_template, "_mkgmtime"); require("cc/cc", logdepth, fatal); report("Checking for _mkgmtime()... "); logprintf(logdepth, "find_time_mkgmtime: trying to find _mkgmtime...\n"); logdepth++; for (ldf = ldflags; *ldf; ++ldf) if (try_icl(logdepth, "libs/time/_mkgmtime", test_c, "#include ", NULL, *ldf)) return 0; return try_fail(logdepth, "libs/time/_mkgmtime"); } int find_time_gmtime_r(const char *name, int logdepth, int fatal) { const char test_c[] = NL "void my_puts(const char *s);" NL "int main() {" NL " time_t tim = 1543645850;" NL " struct tm tm;" NL " if (gmtime_r(&tim, &tm)" /* returns '&tm' */ NL " && 50==tm.tm_sec" NL " && 30==tm.tm_min" NL " && 6==tm.tm_hour" NL " && 1==tm.tm_mday" NL " && 11==tm.tm_mon" NL " && (2018-1900)==tm.tm_year)" NL " my_puts(\"OK\");" NL " return 0;" NL "}" NL "#include " NL "void my_puts(const char *s)" NL "{" NL " puts(s);" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for gmtime_r()... "); logprintf(logdepth, "find_time_gmtime_r: trying to find gmtime_r...\n"); logdepth++; if (try_icl(logdepth, "libs/time/gmtime_r", test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, "libs/time/gmtime_r"); } int find_time_gmtime_s(const char *name, int logdepth, int fatal) { const char test_c[] = NL "void my_puts(const char *s);" NL "int main() {" NL " time_t tim = 1543645850;" NL " struct tm tm;" NL " if (0==gmtime_s(&tm, &tim)" /* returns errno */ NL " && 50==tm.tm_sec" NL " && 30==tm.tm_min" NL " && 6==tm.tm_hour" NL " && 1==tm.tm_mday" NL " && 11==tm.tm_mon" NL " && (2018-1900)==tm.tm_year)" NL " my_puts(\"OK\");" NL " return 0;" NL "}" NL "#include " NL "void my_puts(const char *s)" NL "{" NL " puts(s);" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for gmtime_s()... "); logprintf(logdepth, "find_time_gmtime_s: trying to find gmtime_s...\n"); logdepth++; if (try_icl(logdepth, "libs/time/gmtime_s", test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, "libs/time/gmtime_s"); } int find_time_localtime_r(const char *name, int logdepth, int fatal) { const char *key = "libs/time/localtime_r"; const char test_c[] = NL "void my_puts(const char *s);" NL "int main() {" NL " time_t tim = 1543645850;" NL " struct tm tm;" NL " if (localtime_r(&tim, &tm)" /* returns '&tm' */ NL " && 0!=tm.tm_sec" /* depends on TZ: sadly we can't sure in anything */ NL " && 0!=tm.tm_min" NL " && 0!=tm.tm_hour)" NL " my_puts(\"OK\");" NL " return 0;" NL "}" NL "#include " NL "void my_puts(const char *s)" NL "{" NL " puts(s);" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for localtime_r()... "); logprintf(logdepth, "find_time_localtime_r: trying to find localtime_r...\n"); logdepth++; if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, key); } int find_time_localtime_s(const char *name, int logdepth, int fatal) { const char *key = "libs/time/localtime_s"; const char test_c[] = NL "void my_puts(const char *s);" NL "int main() {" NL " time_t tim = 1543645850;" NL " struct tm tm;" NL " if (0==localtime_s(&tm, &tim)" /* returns errno */ NL " && 0!=tm.tm_sec" /* depends on TZ: sadly we can't sure in anything */ NL " && 0!=tm.tm_min" NL " && 0!=tm.tm_hour)" NL " my_puts(\"OK\");" NL " return 0;" NL "}" NL "#include " NL "void my_puts(const char *s)" NL "{" NL " puts(s);" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for localtime_s()... "); logprintf(logdepth, "find_time_localtime_s: trying to find localtime_s...\n"); logdepth++; if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, key); } fungw-1.2.0/scconfig/src/default/lib_srctree.c0000644000175100017510000000367312673161611017524 0ustar svnsvn/* scconfig - library to explore the source tree Copyright (C) 2015 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "db.h" #include "libs.h" #include "log.h" #include "dep.h" char *svn_info(int logdepth, const char *dir, const char *key) { char *cmd, *stdo = NULL; char *res = NULL; int keylen = strlen(key); cmd = str_concat(" ", "svn info", dir, NULL); if (run_shell(logdepth, cmd, &stdo) == 0) { char *line, *nline; /* check key against each line */ for(line = stdo; line != NULL; line = nline) { /* split line */ nline = strpbrk(line, "\r\n"); if (nline != NULL) { *nline = '\0'; nline++; while((*nline == '\n') || (*nline == '\r')) nline++; } /* compare key */ if (strncmp(line, key, keylen) == 0) { char *val; /* extract value */ val = strchr(line, ':'); if (val != NULL) { val++; while((*val == ' ') || (*val == '\t')) val++; } else val = line; /* produce output */ res = strclone(val); goto found; } } } found:; if (stdo != NULL) free(stdo); free(cmd); return res; } fungw-1.2.0/scconfig/src/default/ht.h0000644000175100017510000000276111375674401015650 0ustar svnsvn#ifndef STR_HT_H #define STR_HT_H /* char * -> void * open addressing hashtable */ /* keys and values are strdupped (strcloned) */ #define ht_deleted_key ((char *)1) #define ht_isused(e) ((e)->key && (e)->key != ht_deleted_key) #define ht_isempty(e) (((e)->key == NULL) || (e)->key == ht_deleted_key) #define ht_isdeleted(e) ((e)->key == ht_deleted_key) typedef struct { unsigned int hash; char *key; void *value; } ht_entry_t; typedef struct { unsigned int mask; unsigned int fill; unsigned int used; int isstr; ht_entry_t *table; int refcount; } ht_t; ht_t *ht_alloc(int isstr); void ht_free(ht_t *ht); ht_t *ht_clear(ht_t *ht); ht_t *ht_resize(ht_t *ht, unsigned int hint); /* value of ht[key], NULL if key is empty or deleted */ void *ht_get(ht_t *ht, const char *key); /* ht[key] = value and return NULL or return ht[key] if key is already used */ void *ht_insert(ht_t *ht, const char *key, void *value); /* ht[key] = value and return a pointer to the strdupped key */ const char *ht_set(ht_t *ht, const char *key, void *value); /* delete key and return ht_deleted_key or NULL if key was not used */ const char *ht_del(ht_t *ht, const char *key); /* iteration */ #define foreach(ht, e) \ for (e = (ht)->table; e != (ht)->table + (ht)->mask + 1; e++) \ if (ht_isused(e)) /* first used (useful for iteration) NULL if empty */ ht_entry_t *ht_first(const ht_t *ht); /* next used (useful for iteration) NULL if there is no more used */ ht_entry_t *ht_next(const ht_t *ht, ht_entry_t *entry); #endif fungw-1.2.0/scconfig/src/default/arg.h0000644000175100017510000000065413470423112015772 0ustar svnsvn#ifndef SCC_ARG_H #define SCC_ARG_H #include typedef struct { char *arg; char *path; int (*callback)(const char *key, const char *value); char *help; } argtbl_t; extern argtbl_t main_argument_table[]; void process_args(int argc, char *argv[]); void help_default_args(FILE *f, const char *prefix); /* main.c: */ extern int custom_arg(const char *key, const char *value); extern int num_custom_reqs; #endif fungw-1.2.0/scconfig/src/default/regex.c0000644000175100017510000003023113325620424016324 0ustar svnsvn/* * regex - Regular expression pattern matching and replacement * * By: Ozan S. Yigit (oz) * Dept. of Computer Science * York University * * These routines are the PUBLIC DOMAIN equivalents of regex * routines as found in 4.nBSD UN*X, with minor extensions. * * These routines are derived from various implementations found * in software tools books, and Conroy's grep. They are NOT derived * from licensed/restricted software. * For more interesting/academic/complicated implementations, * see Henry Spencer's regexp routines, or GNU Emacs pattern * matching module. * * const correctness patch by Tibor 'Igor2' Palinkas in 2009..2010 * new subs code by Tibor 'Igor2' Palinkas in 2015 */ #include #include #include "regex.h" #define MAXNFA 1024 #define MAXTAG 10 #define OKP 1 #define NOP 0 #define CHR 1 #define ANY 2 #define CCL 3 #define BOL 4 #define EOL 5 #define BOT 6 #define EOT 7 #define BOW 8 #define EOW 9 #define REF 10 #define CLO 11 #define END 0 /* * The following defines are not meant to be changeable. * They are for readability only. */ #define MAXCHR 128 #define CHRBIT 8 #define BITBLK MAXCHR/CHRBIT #define BLKIND 0170 #define BITIND 07 #define ASCIIB 0177 #ifdef NO_UCHAR typedef char CHAR; #else typedef unsigned char CHAR; #endif static int tagstk[MAXTAG]; /* subpat tag stack..*/ static CHAR nfa[MAXNFA]; /* automaton.. */ static int sta = NOP; /* status of lastpat */ static CHAR bittab[BITBLK]; /* bit table for CCL */ /* pre-set bits... */ static CHAR bitarr[] = {1,2,4,8,16,32,64,128}; static void chset(CHAR c) { bittab[(CHAR) ((c) & BLKIND) >> 3] |= bitarr[(c) & BITIND]; } #define badpat(x) (*nfa = END, x) #define store(x) *mp++ = x char * re_comp(const char *pat) { register const char *p; /* pattern pointer */ register CHAR *mp=nfa; /* nfa pointer */ register CHAR *lp; /* saved pointer.. */ register CHAR *sp=nfa; /* another one.. */ register int tagi = 0; /* tag stack index */ register int tagc = 1; /* actual tag count */ register int n; register CHAR mask; /* xor mask -CCL/NCL */ int c1, c2; if (!pat || !*pat) { if (sta) return 0; else return badpat("No previous regular expression"); } sta = NOP; for (p = pat; *p; p++) { lp = mp; switch(*p) { case '.': /* match any char.. */ store(ANY); break; case '^': /* match beginning.. */ if (p == pat) store(BOL); else { store(CHR); store(*p); } break; case '$': /* match endofline.. */ if (!*(p+1)) store(EOL); else { store(CHR); store(*p); } break; case '[': /* match char class..*/ store(CCL); if (*++p == '^') { mask = 0377; p++; } else mask = 0; if (*p == '-') /* real dash */ chset(*p++); if (*p == ']') /* real brac */ chset(*p++); while (*p && *p != ']') { if (*p == '-' && *(p+1) && *(p+1) != ']') { p++; c1 = *(p-2) + 1; c2 = *p++; while (c1 <= c2) chset((CHAR)c1++); } #ifdef EXTEND else if (*p == '\\' && *(p+1)) { p++; chset(*p++); } #endif else chset(*p++); } if (!*p) return badpat("Missing ]"); for (n = 0; n < BITBLK; bittab[n++] = (char) 0) store(mask ^ bittab[n]); break; case '*': /* match 0 or more.. */ case '+': /* match 1 or more.. */ if (p == pat) return badpat("Empty closure"); lp = sp; /* previous opcode */ if (*lp == CLO) /* equivalence.. */ break; switch(*lp) { case BOL: case BOT: case EOT: case BOW: case EOW: case REF: return badpat("Illegal closure"); default: break; } if (*p == '+') for (sp = mp; lp < sp; lp++) store(*lp); store(END); store(END); sp = mp; while (--mp > lp) *mp = mp[-1]; store(CLO); mp = sp; break; case '\\': /* tags, backrefs .. */ switch(*++p) { case '(': if (tagc < MAXTAG) { tagstk[++tagi] = tagc; store(BOT); store(tagc++); } else return badpat("Too many \\(\\) pairs"); break; case ')': if (*sp == BOT) return badpat("Null pattern inside \\(\\)"); if (tagi > 0) { store(EOT); store(tagstk[tagi--]); } else return badpat("Unmatched \\)"); break; case '<': store(BOW); break; case '>': if (*sp == BOW) return badpat("Null pattern inside \\<\\>"); store(EOW); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = *p-'0'; if (tagi > 0 && tagstk[tagi] == n) return badpat("Cyclical reference"); if (tagc > n) { store(REF); store(n); } else return badpat("Undetermined reference"); break; #ifdef EXTEND case 'b': store(CHR); store('\b'); break; case 'n': store(CHR); store('\n'); break; case 'f': store(CHR); store('\f'); break; case 'r': store(CHR); store('\r'); break; case 't': store(CHR); store('\t'); break; #endif default: store(CHR); store(*p); } break; default : /* an ordinary char */ store(CHR); store(*p); break; } sp = lp; } if (tagi > 0) return badpat("Unmatched \\("); store(END); sta = OKP; return 0; } static const char *bol; const char *bopat[MAXTAG]; const char *eopat[MAXTAG]; static const char *pmatch(const char *, CHAR *, int *); /* * re_exec: * execute nfa to find a match. * * special cases: (nfa[0]) * BOL * Match only once, starting from the * beginning. * CHR * First locate the character without * calling pmatch, and if found, call * pmatch for the remaining string. * END * re_comp failed, poor luser did not * check for it. Fail fast. * * If a match is found, bopat[0] and eopat[0] are set * to the beginning and the end of the matched fragment, * respectively. * */ int re_exec(const char *lp) { register CHAR c; register const char *ep = 0; register CHAR *ap = nfa; int score = 1; bol = lp; bopat[0] = 0; bopat[1] = 0; bopat[2] = 0; bopat[3] = 0; bopat[4] = 0; bopat[5] = 0; bopat[6] = 0; bopat[7] = 0; bopat[8] = 0; bopat[9] = 0; switch(*ap) { case BOL: /* anchored: match from BOL only */ ep = pmatch(lp,ap, &score); break; case CHR: /* ordinary char: locate it fast */ c = *(ap+1); while (*lp && *lp != c) lp++; if (!*lp) /* if EOS, fail, else fall thru. */ return 0; default: /* regular matching all the way. */ #ifdef OLD while (*lp) { if ((ep = pmatch(lp,ap, &score))) break; lp++; } #else /* match null string */ do { if ((ep = pmatch(lp,ap, &score))) break; } while (*lp++); #endif break; case END: /* munged automaton. fail always */ return 0; } if (!ep) return 0; bopat[0] = lp; eopat[0] = ep; return score; } /* * pmatch: internal routine for the hard part * * This code is partly snarfed from an early grep written by * David Conroy. The backref and tag stuff, and various other * innovations are by oz. * * special case optimizations: (nfa[n], nfa[n+1]) * CLO ANY * We KNOW .* will match everything up to the * end of line. Thus, directly go to the end of * line, without recursive pmatch calls. As in * the other closure cases, the remaining pattern * must be matched by moving backwards on the * string recursively, to find a match for xy * (x is ".*" and y is the remaining pattern) * where the match satisfies the LONGEST match for * x followed by a match for y. * CLO CHR * We can again scan the string forward for the * single char and at the point of failure, we * execute the remaining nfa recursively, same as * above. * * At the end of a successful match, bopat[n] and eopat[n] * are set to the beginning and end of subpatterns matched * by tagged expressions (n = 1 to 9). * */ #ifndef re_fail extern void re_fail(char *, unsigned char); #endif /* * character classification table for word boundary operators BOW * and EOW. the reason for not using ctype macros is that we can * let the user add into our own table. see re_modw. This table * is not in the bitset form, since we may wish to extend it in the * future for other character classifications. * * TRUE for 0-9 A-Z a-z _ */ static CHAR chrtyp[MAXCHR] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; #define inascii(x) (0177&(x)) #define iswordc(x) chrtyp[inascii(x)] #define isinset(x,y) ((x)[((y)&BLKIND)>>3] & bitarr[(y)&BITIND]) /* * skip values for CLO XXX to skip past the closure */ #define ANYSKIP 2 /* [CLO] ANY END ... */ #define CHRSKIP 3 /* [CLO] CHR chr END ... */ #define CCLSKIP 18 /* [CLO] CCL 16bytes END ... */ static const char * pmatch(const char *lp, CHAR *ap, int *score) { register int op, c, n; register const char *e; /* extra pointer for CLO */ register const char *bp; /* beginning of subpat.. */ register const char *ep; /* ending of subpat.. */ const char *are; /* to save the line ptr. */ while ((op = *ap++) != END) switch(op) { case CHR: if (*lp++ != *ap++) return 0; (*score) += 100; break; case ANY: if (!*lp++) return 0; (*score)++; break; case CCL: c = *lp++; if (!isinset(ap,c)) return 0; ap += BITBLK; (*score) += 2; break; case BOL: if (lp != bol) return 0; (*score) += 10; break; case EOL: if (*lp) return 0; (*score) += 10; break; case BOT: bopat[*ap++] = lp; break; case EOT: eopat[*ap++] = lp; break; case BOW: if ((lp!=bol && iswordc(lp[-1])) || !iswordc(*lp)) return 0; (*score) += 5; break; case EOW: if (lp==bol || !iswordc(lp[-1]) || iswordc(*lp)) return 0; (*score) += 5; break; case REF: n = *ap++; bp = bopat[n]; ep = eopat[n]; while (bp < ep) { if (*bp++ != *lp++) return 0; (*score) += 2; } break; case CLO: are = lp; switch(*ap) { case ANY: while (*lp) lp++; n = ANYSKIP; (*score)++; break; case CHR: c = *(ap+1); while (*lp && c == *lp) lp++; n = CHRSKIP; (*score) += 100; break; case CCL: while ((c = *lp) && isinset(ap+1,c)) lp++; n = CCLSKIP; (*score) += 2; break; default: re_fail("closure: bad nfa.", *ap); return 0; } ap += n; while (lp >= are) { e = pmatch(lp, ap, score); if (e) return e; --lp; } return 0; default: re_fail("re_exec: bad nfa.", op); return 0; } return lp; } /* * re_modw: * add new characters into the word table to change re_exec's * understanding of what a word should look like. Note that we * only accept additions into the word definition. * * If the string parameter is 0 or null string, the table is * reset back to the default containing A-Z a-z 0-9 _. [We use * the compact bitset representation for the default table] */ static CHAR deftab[16] = { 0, 0, 0, 0, 0, 0, 0377, 003, 0376, 0377, 0377, 0207, 0376, 0377, 0377, 007 }; void re_modw(char *s) { register int i; if (!s || !*s) { for (i = 0; i < MAXCHR; i++) if (!isinset(deftab,i)) iswordc(i) = 0; } else while(*s) iswordc(*s++) = 1; } /* Substitute the matching part in the last re_exec call with sub. The result is returned in a newly allocated string. */ char *re_subs_dup(char *sub) { char *dst; const char *end; int l1, l2, l3; end = bol + strlen(bol); l1 = bopat[0] - bol; if (sub != NULL) l2 = strlen(sub); else l2 = 0; l3 = end - eopat[0]; if (l3 < 0) l3 = 0; dst = malloc(l1+l2+l3+1); memcpy(dst, bol, l1); if (l2 != 0) memcpy(dst+l1, sub, l2); memcpy(dst+l1+l2, eopat[0], l3+1); return dst; } fungw-1.2.0/scconfig/src/default/main_custom_args.c0000644000175100017510000000304012660127205020542 0ustar svnsvn/* scconfig - default way to handle custom args (save them in an array) Copyright (C) 2016 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "libs.h" #include "log.h" #include "hooks.h" #include "main_custom_args.h" char *custom_reqs[MAX_CUSTOM_REQS]; int num_custom_reqs = 0; int custom_arg(const char *key, const char *value) { if (hook_custom_arg(key, value)) return 1; if (strcmp(key, "detect") == 0) { printf("Will detect: %s\n", value); if (num_custom_reqs >= MAX_CUSTOM_REQS) { report("Too many custom reqs from the command line, exiting\n"); exit(1); } custom_reqs[num_custom_reqs] = strclone(value); num_custom_reqs++; return 1; } return 0; } fungw-1.2.0/scconfig/src/default/find_sys.c0000644000175100017510000003143613640533375017050 0ustar svnsvn/* scconfig - detect features of the system or the host/target computer Copyright (C) 2009..2012 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" int find_sys_ptrwidth(const char *name, int logdepth, int fatal) { char *end, W[32]; char *out = NULL; int w; char *test_c = NL "#include " NL "int main() {" NL " void *ptr;" NL " printf(\"%d\\n\", sizeof(ptr));" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for pointer width... "); logprintf(logdepth, "find_sys_ptrwidth: trying to find pointer width...\n"); logdepth++; if (compile_run(logdepth, test_c, NULL, NULL, NULL, &out) == 0) { w = strtol(out, &end, 10); if ((*end != '\0') && (*end != '\n') && (*end != '\r')) { report("FAILED (test code failed)\n"); logprintf(logdepth+1, "FAILED: returned '%s' which is not a valid decimal number (at '%s')\n", out, end); return 1; } sprintf(W, "%d", w * 8); report("OK (%s bits)\n", W); put("sys/ptrwidth", W); logprintf(logdepth+1, "OK (%s bits)\n", W); } return 0; } int find_sys_byte_order(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "int main() {" NL " long int i = 8;" NL " char *s = (char *)&i;" NL " printf(\"%d\\n\", s[0]);" NL " return 0;" NL "}" NL; const char* test_c_blind_template = NL "#include " NL "#include " NL "#ifndef __BYTE_ORDER" NL "#error \"ERROR 1\"" NL "void void *;" NL "#endif" NL "#ifndef __%s_ENDIAN" NL "#error \"ERROR 2\"" NL "char char *;" NL "#endif" NL "#if __BYTE_ORDER != __%s_ENDIAN" NL "#error \"ERROR 3\"" NL "int int *;" NL "#endif" NL "int main() {" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; const char *key = "sys/byte_order"; const char *endians1[] = { "LITTLE", "BIG", NULL }; const char *endians2[] = { "LSB", "MSB", NULL }; int index; char test_c_blind[1024]; char *end; const char *W; char *out = NULL; int w; require("cc/cc", logdepth, fatal); report("Checking for byte order... "); logprintf(logdepth, "find_sys_byte_order: trying to find byte order...\n"); logdepth++; if ((!isblind(db_cwd)) && compile_run(logdepth, test_c, NULL, NULL, NULL, &out) == 0) { w = strtol(out, &end, 10); if (((*end != '\0') && (*end != '\n') && (*end != '\r')) || ((w != 0) && (w != 8))) { report("FAILED (test code failed)\n"); logprintf(logdepth+1, "FAILED: returned '%s' which is not a valid decimal number (at '%s')\n", out, end); return 1; } if (w == 0) W = "MSB"; else W = "LSB"; report("OK (%s first)\n", W); put("sys/byte_order", W); logprintf(logdepth+1, "OK (%s first)\n", W); return 0; } for (index=0; endians1[index]!=NULL; ++index) { sprintf(test_c_blind, test_c_blind_template, endians1[index], endians1[index]); if (compile_run(logdepth, test_c_blind, NULL, NULL, NULL, NULL) == 0) { W = endians2[index]; report("OK (%s first)\n", W); put(key, W); return 0; } } report("FAILED (cannot determine byte order, you must supply it)\n"); return try_fail(logdepth, key); } static int test_shell_eats_backslash(int logdepth) { char *test = "echo c:\\n"; char *out; logprintf(logdepth, "testing if shell eats \\...\n"); run_shell(logdepth+1, test, &out); if (out == NULL) { logprintf(logdepth+1, "oops, couldn't run shell?! (returned NULL)\n"); report("ERROR: shell fails."); abort(); } if (out[2] == '\\') { logprintf(logdepth, "shell does NOT eat \\...\n"); put("sys/shell_eats_backslash", sfalse); free(out); return 0; } free(out); logprintf(logdepth, "shell eats \\...\n"); put("sys/shell_eats_backslash", strue); return 1; } static int try_get_cwd(int logdepth, const char *cmd) { char *cwd, *end; run_shell(logdepth+1, cmd, &cwd); if (cwd != NULL) { end = strpbrk(cwd, "\r\n"); if (end != NULL) *end = '\0'; if (*cwd != '\0') { end = cwd + strlen(cwd) - 1; while((*end == ' ') || (*end == '\t')) { *end = '\0'; end--; } put("sys/tmp", cwd); /* ugly hack for win32: paths there start as c:\ */ if ((cwd[1] == ':') && (cwd[2] == '\\')) append("sys/tmp", "\\"); else append("sys/tmp", "/"); logprintf(logdepth, "cwd is '%s'\n", get("sys/tmp")); free(cwd); return 1; } else free(cwd); } return 0; } static int try_tmp(int logdepth) { char *fn; logprintf(logdepth, "validating temp dir '%s'\n", get("sys/tmp")); fn = tempfile_new_noabort(""); if (fn != NULL) { unlink(fn); free(fn); logprintf(logdepth, "temp dir works!\n"); return 1; } logprintf(logdepth, "temp dir fails\n"); return 0; } /* test temp dir with all sort of workarounds */ static int try_tmp_all(int logdepth) { const char *tmp, *si; char c; char *t, *so, *old_tmp; int eats, n; tmp = get("sys/tmp"); /* path must end in path separator */ c = tmp[strlen(tmp)-1]; if ((c != '/') && (c != '\\')) { append("sys/tmp", "/"); tmp = get("sys/tmp"); } logprintf(logdepth, "trying detected temp dir '%s'\n", tmp); if (try_tmp(logdepth+1)) return 1; /* try msys-on-windows hack: if path starts with /d/something, try d:/ instead */ if ((tmp[0] == '/') && (isalpha(tmp[1])) && (tmp[2] == '/')) { /* for the next test we shouldn't use our half-detected tmp path but go with . */ old_tmp = strclone(tmp); put("sys/tmp", ""); eats = istrue(get("sys/shell_eats_backslash")); tmp = old_tmp; logprintf(logdepth, "tmp2='%s' eats=%d\n", tmp, eats); t = malloc(strlen(tmp) * 2); t[0] = tmp[1]; t[1] = ':'; for(si = tmp + 2, so = t + 2; *si != '\0'; si++, so++) { if (*si == '/') { *so = '\\'; if (eats) { for(n = 0; n < 3; n++) { so++; *so = '\\'; } } } else *so = *si; } *so = '\0'; free(old_tmp); logprintf(logdepth, "trying windows fix: '%s'\n", t); put("sys/tmp", t); free(t); if (try_tmp(logdepth+1)) { if (eats) put("sys/path_sep", "\\\\\\\\"); else put("sys/path_sep", "\\"); put("sys/path_sep_escaped", "\\\\"); return 1; } tmp = get("sys/tmp"); } /* fail. Set back tmp to empty so next command has a chance to run */ put("sys/tmp", ""); return 0; } int find_tmp(const char *name, int logdepth, int fatal) { const char *usertmp; if (in_cross_target) { report("Temp dir for cross compilation target is the same as for host..."); logprintf(logdepth, "Copying temp dir from host to target\n"); require("/host/sys/tmp", logdepth, fatal); usertmp = get("/host/sys/tmp"); if (usertmp == NULL) { report("Host temp dir not found.\n"); logprintf(logdepth, "Host temp dir not found.\n"); return 1; } put("sys/tmp", usertmp); return 0; } /* we need shell for later tests; do this detection in . */ put("sys/tmp", ""); require("sys/shell", logdepth, fatal); put("sys/path_sep", "/"); put("sys/path_sep_escaped", "/"); report("Detecting temp dir..."); logprintf(logdepth, "Finding temp dir (current working directory)...\n"); usertmp = get("/arg/sys/tmp"); /* . as tmp would fail for commands including a "cd" - this would cause temporary files left in the target dir. We start out with empty string (which is ., but on windows ./ would fail), and run pwd (without cd) to find out the current directory (as getcwd() is not portable). If pwd fails, we stay with ./ */ put("sys/tmp", ""); /* we need to know about shell backslash problem regardless of how we end up with tmp - currently tmp is ., where the test could run safely */ test_shell_eats_backslash(logdepth+1); /* Special case: cross-compilation with emulator; we can not assume the emulator uses the same paths as the host system, while we mix accessing files from host and emu. If we stay in ., both emulator and host system should be (more or less) happy. */ if (istarget(db_cwd) && iscross) { if (usertmp == NULL) { report("using temp dir . for cross-compilation\n"); logprintf(logdepth, "staying with . for cross-compilation\n"); } else { put("sys/tmp", usertmp); report("using user supplied temp dir '%s' for cross-compilation\n", usertmp); logprintf(logdepth, "using user supplied temp dir '%s' for cross-compilation\n", usertmp); logprintf(logdepth, "Path sep: '%s'\n", get("sys/path_sep")); } return 0; } if ((usertmp != NULL)) put("sys/tmp", usertmp); if ( ((usertmp != NULL) && (try_tmp_all(logdepth+2))) || /* try user supplied temp dir */ ((try_get_cwd(logdepth+1, "pwd")) && (try_tmp_all(logdepth+2))) || /* try pwd for finding out cwd */ ((try_get_cwd(logdepth+1, "echo %cd%") && (try_tmp_all(logdepth+2))))) { /* try windows-specific way for finding out cwd */ report(" validated %s\n", get("sys/tmp")); logprintf(logdepth, "Detected temp dir '%s'\n", get("sys/tmp")); logprintf(logdepth, "Path sep: '%s'\n", get("sys/path_sep")); return 0; } put("sys/tmp", ""); report("using temp dir fallback .\n"); logprintf(logdepth, "all temp directories failed, using . as tmp\n"); logprintf(logdepth, "Path sep: '%s'\n", get("sys/path_sep")); return 0; } int test_shell(const char *shell, int logdepth, int quote) { char *test = "echo hello"; char *cmd; char *out; char *q; if (quote) q = "\""; else q = ""; logprintf(logdepth, "testing '%s' as shell\n", shell); cmd = malloc(strlen(test) + strlen(shell) + 8); sprintf(cmd, "%s %s%s%s", shell, q, test, q); run(logdepth+1, cmd, &out); free(cmd); if ((out != NULL) && (strncmp(out, "hello", 5) == 0)) { put("sys/shell", shell); if (quote) put("sys/shell_needs_quote", strue); else put("sys/shell_needs_quote", sfalse); logprintf(logdepth, "accepted.\n"); free(out); return 1; } logprintf(logdepth, "refused.\n"); free(out); return 0; } static int find_shell_escape(const char *name, int logdepth, int fatal, const char *shell) { char cmdline[256]; char **t; char *tests[] = { "\\", "\\ {}&;|", "^", "^ &", NULL, NULL }; (void) fatal; /* not used */ (void) shell; /* not used */ report("Looking for a shell escape character... "); logprintf(logdepth, "finding shell escape character...\n"); for(t = tests; *t != NULL; t += 2) { char *s, *end, *out, *start; strcpy(cmdline, "echo "); end = cmdline+5; for(s = t[1]; *s != '\0'; s++) { *end++ = *t[0]; *end++ = *s; } *end = '\0'; run(logdepth+1, cmdline, &out); if (out != NULL) { int res; if (*out == '\"') /* wine likes to wrap the output in quotes for some reason */ start = out+1; else start = out; res = strncmp(start, t[1], strlen(t[1])); free(out); if (res == 0) { report("found: '%s'\n", t[0]); logprintf(logdepth, "found shell escape char '%s'\n", t[0]); put("sys/shell_escape_char", t[0]); return 0; } } } report("NOT FOUND\n"); logprintf(logdepth, "shell escape character not found\n"); return 1; } int find_shell(const char *name, int logdepth, int fatal) { const char *shells[] = { "/bin/sh -c", "/bin/bash -c", "bash -c", "cmd.exe /c", "sh -c", "/bin/dash -c", "dash -c", "/bin/ksh -c", "ksh -c", NULL }; const char **s; if (cross_blind) { const char *shell = get("/arg/sys/target-shell"); if (shell == NULL) { report("Need to specify sys/target-shell in blind cross compiling mode, because the shell cannot be detected (note: scconfig will not attempt to run the target shell)\n"); exit(1); } put("sys/shell", shell); report("Blind cross compiling: accepting '%s' as shell\n", shell); logprintf(logdepth, "Blind cross compiling: accepting '%s' as shell\n", shell); return 0; } report("Looking for a shell... "); logprintf(logdepth, "finding a shell\n"); for(s = shells; *s != NULL; s++) { if ((test_shell(*s, logdepth+1, 0)) || (test_shell(*s, logdepth+1, 1))) { report("%s\n", *s); logprintf(logdepth, "found a shell '%s', need quote: %s\n", *s, get("sys/shell_needs_quote")); return find_shell_escape(name, logdepth, fatal, *s); } } report("NOT FOUND\n"); logprintf(logdepth, "shell not found\n"); return 1; } fungw-1.2.0/scconfig/src/default/deps_default.c0000644000175100017510000002276214001245115017653 0ustar svnsvn/* scconfig - dependency list of default tests Copyright (C) 2009..2012 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "dep.h" #include "find.h" void deps_default_init(void) { dep_add("cc/cc", find_cc); dep_add("cc/argstd/*", find_cc_argstd); dep_add("cc/cflags", find_cc); dep_add("cc/ldflags", find_cc); dep_add("cc/inline", find_inline); dep_add("cc/varargmacro", find_varargmacro); dep_add("cc/funcmacro", find_funcmacro); dep_add("cc/constructor", find_constructor); dep_add("cc/destructor", find_destructor); dep_add("cc/rdynamic", find_rdynamic); dep_add("cc/soname", find_soname); dep_add("cc/so_undefined", find_so_undefined); dep_add("cc/wlrpath", find_wlrpath); dep_add("cc/wloutimplib", find_cc_wloutimplib); dep_add("cc/wloutputdef", find_cc_wloutputdef); dep_add("cc/fpic", find_fpic); dep_add("cc/fpie/*", find_cc_fpie); dep_add("cc/fnopie/*", find_cc_fnopie); dep_add("cc/fnopic/*", find_cc_fnopic); dep_add("cc/alloca/*", find_alloca); dep_add("cc/_exit/*", find__exit); dep_add("cc/ldflags_dynlib", find_ldflags_dynlib); dep_add("cc/ldflags_dll", find_ldflags_dll); dep_add("cc/ldflags_so", find_ldflags_so); dep_add("cc/func_attr/unused/*", find_fattr_unused); dep_add("cc/declspec/dllimport/*", find_declspec_dllimport); dep_add("cc/declspec/dllexport/*", find_declspec_dllexport); dep_add("cc/argmachine/*", find_cc_argmachine); dep_add("cc/pragma_message/*", find_cc_pragma_message); dep_add("cc/static_libgcc/*", find_cc_static_libgcc); dep_add("libs/ldl", find_lib_ldl); dep_add("libs/LoadLibrary/*", find_lib_LoadLibrary); dep_add("libs/lpthread", find_lib_lpthread); dep_add("libs/lpthread-recursive", find_lib_lpthread); dep_add("thread/semget/*", find_thread_semget); dep_add("thread/pthread_create/*", find_thread_pthread_create); dep_add("thread/CreateSemaphore/*", find_thread_CreateSemaphore); dep_add("thread/CreateThread/*", find_thread_CreateThread); dep_add("libs/errno/*", find_lib_errno); dep_add("libs/printf_x", find_printf_x); dep_add("libs/printf_ptrcast", find_printf_ptrcast); dep_add("libs/snprintf", find_snprintf); dep_add("libs/snprintf_safe", find_snprintf); dep_add("libs/dprintf", find_dprintf); dep_add("libs/vdprintf", find_vdprintf); dep_add("libs/vsnprintf", find_vsnprintf); dep_add("libs/proc/_spawnvp/*", find_proc__spawnvp); dep_add("libs/proc/fork/*", find_proc_fork); dep_add("libs/proc/wait/*", find_proc_wait); dep_add("libs/proc/_getpid/*", find_proc__getpid); dep_add("libs/proc/CreateProcessA/*",find_proc_CreateProcessA); dep_add("libs/proc/getexecname/*", find_proc_getexecname); dep_add("libs/proc/GetModuleFileNameA/*",find_proc_GetModuleFileNameA); dep_add("libs/proc/shmget/*", find_proc_shmget); dep_add("libs/proc/CreateFileMappingA/*",find_proc_CreateFileMappingA); dep_add("libs/fs/realpath/*", find_fs_realpath); dep_add("libs/fs/_fullpath/*", find_fs__fullpath); dep_add("libs/fs/readdir/*", find_fs_readdir); dep_add("libs/fs/findnextfile/*", find_fs_findnextfile); dep_add("libs/fs/stat/macros/*", find_fs_stat_macros); dep_add("libs/fs/stat/fields/*", find_fs_stat_fields); dep_add("libs/fs/access/*", find_fs_access); dep_add("libs/fs/access/macros/*", find_fs_access_macros); dep_add("libs/fs/lstat/*", find_fs_lstat); dep_add("libs/fs/statlstat/*", find_fs_statlstat); dep_add("libs/fs/getcwd/*", find_fs_getcwd); dep_add("libs/fs/_getcwd/*", find_fs__getcwd); dep_add("libs/fs/getwd/*", find_fs_getwd); dep_add("libs/fs/mkdir/*", find_fs_mkdir); dep_add("libs/fs/_mkdir/*", find_fs__mkdir); dep_add("libs/fs/utime/*", find_fs_utime); dep_add("libs/fs/_utime/*", find_fs__utime); dep_add("libs/fs/_utime64/*", find_fs__utime64); dep_add("libs/fs/mkdtemp/*", find_fs_mkdtemp); dep_add("libs/fs/mmap/*", find_fs_mmap); dep_add("libs/fsmount/next_dev/*", find_fsmount_next_dev); dep_add("libs/fsmount/struct_fsstat/*",find_fsmount_fsstat_fields); dep_add("libs/fsmount/struct_statfs/*",find_fsmount_statfs_fields); dep_add("libs/fsmount/struct_statvfs/*",find_fsmount_statvfs_fields); dep_add("libs/fs/ustat/*", find_fs_ustat); dep_add("libs/fs/statfs/*", find_fs_statfs); dep_add("libs/fs/statvfs/*", find_fs_statvfs); dep_add("libs/fs/flock/*", find_fs_flock); dep_add("libs/fs/makedev/*", find_fs_makedev); dep_add("libs/io/pipe/*", find_io_pipe); dep_add("libs/io/pipe2/*", find_io_pipe2); dep_add("libs/io/_pipe/*", find_io__pipe); dep_add("libs/io/dup2/*", find_io_dup2); dep_add("libs/io/fileno/*", find_io_fileno); dep_add("libs/io/lseek/*", find_io_lseek); dep_add("libs/io/popen/*", find_io_popen); dep_add("libs/time/usleep/*", find_time_usleep); dep_add("libs/types/stdint/*", find_types_stdint); dep_add("sys/types/size/*", find_types_sizes); dep_add("libs/time/Sleep/*", find_time_Sleep); dep_add("libs/time/gettimeofday/*", find_time_gettimeofday); dep_add("libs/time/ftime/*", find_time_ftime); dep_add("libs/time/timegm/*", find_time_timegm); dep_add("libs/time/_mkgmtime/*", find_time_mkgmtime); dep_add("libs/time/gmtime_r/*", find_time_gmtime_r); dep_add("libs/time/gmtime_s/*", find_time_gmtime_s); dep_add("libs/time/localtime_r/*", find_time_localtime_r); dep_add("libs/time/localtime_s/*", find_time_localtime_s); dep_add("libs/env/main_3arg/*", find_main_arg3); dep_add("libs/env/putenv/*", find_putenv); dep_add("libs/env/setenv/*", find_setenv); dep_add("libs/env/environ/*", find_environ); dep_add("signal/raise/*", find_signal_raise); dep_add("signal/names/*", find_signal_names); dep_add("fstools/cp", find_fstools_cp); dep_add("fstools/ln", find_fstools_ln); dep_add("fstools/mv", find_fstools_mv); dep_add("fstools/rm", find_fstools_rm); dep_add("fstools/mkdir", find_fstools_mkdir); dep_add("fstools/ar", find_fstools_ar); dep_add("fstools/ranlib", find_fstools_ranlib); dep_add("fstools/awk", find_fstools_awk); dep_add("fstools/cat", find_fstools_cat); dep_add("fstools/sed", find_fstools_sed); dep_add("fstools/file_l", find_fstools_file_l); dep_add("fstools/file", find_fstools_file); dep_add("fstools/chmodx", find_fstools_chmodx); dep_add("sys/name", find_uname); dep_add("sys/uname", find_uname); dep_add("sys/triplet", find_triplet); dep_add("sys/sysid", find_sysid); dep_add("sys/shell", find_shell); dep_add("sys/shell_needs_quote", find_shell); dep_add("sys/tmp", find_tmp); dep_add("sys/shell_eats_backslash", find_tmp); dep_add("sys/ext_exe", find_uname); dep_add("sys/ext_dynlib", find_uname); dep_add("sys/ext_dynlib_native", find_uname); dep_add("sys/ext_stalib", find_uname); dep_add("sys/class", find_uname); dep_add("sys/path_sep", find_uname); dep_add("sys/ptrwidth", find_sys_ptrwidth); dep_add("sys/byte_order", find_sys_byte_order); dep_add("sys/types/size_t/*", find_types_size_t); dep_add("sys/types/off_t/*", find_types_off_t); dep_add("sys/types/off64_t/*", find_types_off64_t); dep_add("sys/types/gid_t/*", find_types_gid_t); dep_add("sys/types/uid_t/*", find_types_uid_t); dep_add("sys/types/pid_t/*", find_types_pid_t); dep_add("sys/types/mode_t/*", find_types_mode_t); dep_add("sys/types/nlink_t/*", find_types_nlink_t); dep_add("sys/types/ptrdiff_t/*", find_types_ptrdiff_t); dep_add("sys/types/dev_t/*", find_types_dev_t); dep_add("sys/types/ino_t/*", find_types_ino_t); dep_add("sys/types/void_ptr/*", find_types_void_ptr); dep_add("str/strcasecmp/*", find_strcasecmp); dep_add("str/strncasecmp/*", find_strncasecmp); dep_add("str/stricmp/*", find_stricmp); dep_add("str/strnicmp/*", find_strnicmp); dep_add("/internal/filelist/cmd", find_filelist); dep_add("/internal/filelist/method", find_filelist); } fungw-1.2.0/scconfig/src/default/find_uname.c0000644000175100017510000002362113764335422017334 0ustar svnsvn/* scconfig - evaluate uname and classify the system Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "regex.h" #include "log.h" #include "db.h" #include "libs.h" #include "dep.h" static void sys_unix(void) { put("sys/ext_exe", ""); put("sys/ext_dynlib", ".so"); put("sys/ext_stalib", ".a"); put("sys/ext_dynlib_native", ".so"); } static void sys_netbsd(void) { sys_unix(); put("cc/ldflags", "-Wl,-R/usr/pkg/lib -L/usr/pkg/lib"); /* TODO: is this the best way? */ } static void sys_win32dlc(void) { put("sys/ext_exe", ".exe"); put("sys/ext_dynlib", ".dlc"); put("sys/ext_stalib", ".a"); put("sys/ext_dynlib_native", ".dll"); } typedef void (*callback_t)(void); typedef struct { char *uname_regex; char *name; char *class; callback_t callback; } uname_t; typedef struct { char *file_name; char *name; char *class; callback_t callback; } magic_file_t; /* Guess system class by uname; class is informative, nothing important should depend on it. Order *does* matter */ uname_t unames[] = { {"[Nn]et[Bb][Ss][Dd]", "NetBSD", "UNIX", sys_netbsd}, {"[Ll]inux", "Linux", "UNIX", sys_unix}, {"[Bb][Ss][Dd]", "BSD", "UNIX", sys_unix}, {"SunOS", "SunOS", "UNIX", sys_unix}, {"OSF1", "OSF", "UNIX", sys_unix}, /* TODO: note the difference in cflags for debugging ("-ms -g") */ {"AIX", "AIX", "UNIX", sys_unix}, {"IRIX", "IRIX", "UNIX", sys_unix}, {"SunOS", "SunOS", "UNIX", sys_unix}, {"[Mm]inix", "Minix", "UNIX", sys_unix}, {"[Aa][Rr][Oo][Ss]", "Aros", "UNIX", sys_unix}, {"^Darwin", "MacOSX", "UNIX", sys_unix}, {"[Th]hreos", "Threos", "UNIX", sys_unix}, {"[Cc]ygwin", "cygwin", "WIN32", sys_win32dlc}, {"[Mm][Ii][Nn][Gg][Ww]", "mingw", "WIN32", sys_win32dlc}, {"win32", "win32", "WIN32", sys_win32dlc}, /* vanilla windows */ {NULL, NULL, NULL, NULL} }; /* Fallback: extract machine name from uname -a if uname -m fails */ static const char *machine_names[] = { "i[0-9]86[^ ]*", "x86_[^ ]*", "amd[0-9]*", "armv[0-9][^ ]*", "ppc[0-9]+", "sparc[0-9]*", "BePC", "ia64", "x86", "IP[0-9]*", "k1om", "sun4u", "RM600", "R4000", "alpha", NULL }; /* Fallback: extract system name from uname -a if uname -s fails */ static const char *system_names[] = { "[Ll]inux", "sn5[0-9]*", "CYGWIN_NT[^ ]*", "GNU[^ ]*", "DragonFly", "[^ ]*BSD[^ ]*", "Haiku", "HP-UX", "AIX", "OS4000", "Interix", "IRIX[0-9]*", "Darwin", "Minix", "MINGW[^ ]*", "ReliantUNIX[^ ]*", "SunOS", "OSF1", "ULTRIX", "UWIN-W7", "IS/WB", "OS/390", "SCO[^ ]*", "QNX", NULL }; /* Fallback: if uname -a fails, guess system by looking at "magic file names" */ magic_file_t magic_files[] = { {"/dev/null", "UNIX", "UNIX", sys_unix}, {"c:\\config.sys", "win32", "WIN32", sys_win32dlc}, {"c:\\windows\\system.ini", "win32", "WIN32", sys_win32dlc}, {"c:\\windows\\win.ini", "win32", "WIN32", sys_win32dlc}, {"c:\\windows\\notepad.exe", "win32", "WIN32", sys_win32dlc}, {NULL, NULL, NULL, NULL} } ; static int match(const char *regex, const char *str) { re_comp(regex); return re_exec(str); } /* match uname against each pattern on the list; on a match, put() the portion of the string matched in node and return 1 */ int uname_guess(const char *node, const char *uname, const char *list[]) { const char **l; if (uname == NULL) return 0; for(l = list; *l != NULL; l++) { if (match(*l, uname)) { char *s; int len = eopat[0] - bopat[0]; s = malloc(len+1); memcpy(s, bopat[0], len); s[len] = '\0'; put(node, s); return 1; } } return 0; } /* Don't worry about linear search or matching regexes all the time - this function will be run at most two times */ static callback_t lookup_uname(char **uname, const char **name, const char **class) { uname_t *u; for(u = unames; u->uname_regex != NULL; u++) { if ( ((*uname != NULL) && (match(u->uname_regex, *uname))) /* uname match */ || ((*name != NULL) && ((strcmp(u->name, *name) == 0))) /* name match */ || ((*class != NULL) && ((strcmp(u->class, *class) == 0))) /* class match */ ) { if (*name == NULL) *name = u->name; if (*class == NULL) *class = u->class; return u->callback; } } return NULL; } static callback_t lookup_magic_file(int logdepth, const char **name, const char **class) { magic_file_t *u; for(u = magic_files; u->file_name != NULL; u++) { if (is_file(u->file_name)) { logprintf(logdepth, "%s -> %s\n", u->file_name, u->class); if (*name == NULL) *name = u->name; if (*class == NULL) *class = u->class; return u->callback; } } return NULL; } int find_uname(const char *rname, int logdepth, int fatal) { const char *name, *class, *tname, *uname_orig; char *s, *uname, *mname, *sname; void (*callback)(void); require("sys/tmp", logdepth, fatal); if (istarget(db_cwd)) require("/target/sys/target", logdepth, fatal); report("Checking for system type... "); logprintf(logdepth, "[find_uname] checking for sys/name\n"); logdepth++; tname = get("/arg/sys/target-name"); if (istarget(db_cwd) && (tname != NULL)) put("sys/name", tname); tname = get("/arg/sys/target-uname"); if (istarget(db_cwd) && (tname != NULL)) put("sys/uname", tname); name = get("sys/name"); uname_orig = get("sys/uname"); if (name == NULL) { if (uname_orig == NULL) { logprintf(logdepth, "not set, running\n"); run_shell(logdepth, "uname -a", (char **)&uname); if (uname != NULL) { for(s = uname; *s != '\0'; s++) if ((*s == '\n') || (*s == '\r')) *s = ' '; put("sys/uname", uname); } else put("sys/uname", ""); if (run_shell(logdepth, "uname -m", (char **)&mname) == 0) put("sys/machine_name", strip(mname)); else put("sys/machine_name", NULL); if (mname != NULL) free(mname); if (run_shell(logdepth, "uname -o", (char **)&sname) == 0) put("sys/system_name", strip(sname)); else if (run_shell(logdepth, "uname -s", (char **)&sname) == 0) put("sys/system_name", strip(sname)); else put("sys/system_name", NULL); if (sname != NULL) free(sname); } /* we have uname by now, set sys/name */ name = NULL; class = NULL; callback = lookup_uname(&uname, &name, &class); if (name == NULL) { /* no uname or unknown system by uname - fallback: check for cross target */ const char *target = get("/arg/sys/target"); if ((target != NULL) && (strstr(target, "mingw") != NULL)) { name = "WIN32"; report("(detected mingw cross compilation to WIN32)\n"); } else { report("Warning: unknown system\n"); name = "unknown"; } } put("sys/name", name); } else { /* we had sys/name, that should be enough */ uname = NULL; class = name; callback = lookup_uname(&uname, &name, &class); } /* predefined and/or detected uname failed, try magic file method */ if (callback == NULL) { logprintf(logdepth, "System class is unknown by uname, running heuristics...\n"); report("System class is unknown by uname, running heuristics... "); callback = lookup_magic_file(logdepth + 1, &name, &class); } if (callback == NULL) { /* System unknown. */ error("Unknown system '%s'\n", get("sys/uname")); abort(); } callback(); report("OK (name: %s; class: %s)\n", name, class); put("sys/class", class); /* fallbacks */ if (get("sys/machine_name") == NULL) uname_guess("sys/machine_name", uname, machine_names); if (get("sys/system_name") == NULL) uname_guess("sys/system_name", uname, system_names); /* on windows, overwrite the path sep with the right amount of \ (the tmp finder may have left / in it) */ if ((strcmp(class, "WIN32") == 0) || (strcmp(class, "win32") == 0)) { int eats = istrue(get("sys/shell_eats_backslash")); if (eats) put("sys/path_sep", "\\\\\\\\"); else put("sys/path_sep", "\\"); put("sys/path_sep_escaped", "\\\\"); } return 0; } static int find_triplet_(const char *name, int logdepth, int fatal, const char *nodename, int include_vendor, char *sep, char *esc) { const char *machine, *vendor, *os; char *triplet, *s; char fake_sep[2]; fake_sep[0] = 1; fake_sep[1] = 0; require("sys/uname", logdepth, fatal); machine = get("sys/machine_name"); if (machine == NULL) machine = "unknown"; vendor = "unknown"; os = get("sys/system_name"); if (os == NULL) os = "unknown"; if (include_vendor) triplet = str_concat(fake_sep, machine, vendor, os, NULL); else triplet = str_concat(fake_sep, machine, os, NULL); for(s = triplet; *s != '\0'; s++) { if ((esc != NULL) && (*s == *sep)) *s = *esc; if (isalnum(*s)) *s = tolower(*s); else { if (*s == *fake_sep) *s = *sep; else if (esc != NULL) *s = *esc; else *s = '-'; } } put(nodename, triplet); free(triplet); return 0; } int find_triplet(const char *name, int logdepth, int fatal) { return find_triplet_(name, logdepth, fatal, "sys/triplet", 1, "-", NULL); } int find_sysid(const char *name, int logdepth, int fatal) { return find_triplet_(name, logdepth, fatal, "sys/sysid", 0, "-", "_"); } fungw-1.2.0/scconfig/src/default/find_cc.c0000644000175100017510000007375413770611750016626 0ustar svnsvn/* scconfig - detection of cc and compiler features Copyright (C) 2009..2012 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" static int try_flags(int logdepth, const char *cc, const char *test_c, const char *cflags, const char *ldflags, const char *expected) { char *out; logprintf(logdepth, "trying cc:try_flags with cc='%s' cflags='%s' ldflags='%s'\n", (cc == NULL ? get("cc/cc") : cc), cflags == NULL ? "" : cflags, ldflags == NULL ? "" : ldflags); if (compile_run(logdepth+1, test_c, cc, cflags, ldflags, &out) == 0) { if (((out == NULL) && (iscross)) || (strncmp(out, expected, strlen(expected)) == 0)) { free(out); return 1; } free(out); } return 0; } static int try_flags_inv(int logdepth, const char *cc, const char *test_c, const char *cflags, const char *ldflags, const char *expected_bad) { char *out; logprintf(logdepth, "trying cc:try_flags with cc='%s' cflags='%s' ldflags='%s'\n", (cc == NULL ? get("cc/cc") : cc), cflags == NULL ? "" : cflags, ldflags == NULL ? "" : ldflags); if (compile_run(logdepth+1, test_c, cc, cflags, ldflags, &out) == 0) { if (((out == NULL) && (iscross)) || (strncmp(out, expected_bad, strlen(expected_bad)) != 0)) { free(out); return 1; } free(out); } return 0; } static int try(int logdepth, const char *cc, const char *test_c, const char *expected) { return try_flags(logdepth, cc, test_c, NULL, NULL, expected); } static int trycc(int logdepth, const char *cc, const char *test_c) { int ret; if (cc == NULL) return 0; ret = try(logdepth, cc, test_c, "OK"); if (ret) put("cc/cc", cc); return ret; } int find_cc(const char *name, int logdepth, int fatal) { char *test_c = "#include \nint main() { printf(\"OK\\n\");\nreturn 0;}\n"; char *out = NULL, *targetcc; const char *cc, *cflags, *ldflags, *target, *sys; int len; require("sys/name", logdepth, fatal); sys = istarget(db_cwd) ? "target" : "host"; report("Checking for cc (%s)... ", sys); logprintf(logdepth, "find_cc: trying to find cc (%s)...\n", sys); logdepth++; /* cflags */ cflags = get("/arg/cc/cflags"); if (cflags != NULL) { logprintf(logdepth+1, "using user supplied cflags '%s'\n", cflags); put("cc/cflags", cflags); } /* ldflags */ ldflags = get("/arg/cc/ldflags"); if (ldflags != NULL) { logprintf(logdepth+1, "using user supplied ldflags '%s'\n", ldflags); put("cc/ldflags", ldflags); } cc = get("/arg/cc/cc"); if (cc == NULL) { target = get("sys/target"); if (target != NULL) { logprintf(logdepth+1, "find_cc: crosscompiling for '%s', looking for target cc\n", target); len = strlen(target); targetcc = malloc(len + 8); memcpy(targetcc, target, len); strcpy(targetcc + len, "-gcc"); if (!trycc(logdepth+1, targetcc, test_c)) { strcpy(targetcc + len, "-cc"); if (!trycc(logdepth+1, targetcc, test_c)) { report("FAILED: failed to find crosscompiler for target '%s'\n", target); logprintf(logdepth, "find_cc: FAILED to find a crosscompiler for target '%s'\n", target); return 1; } } put("cc/cc", targetcc); } else { cc = getenv("CC"); logprintf(logdepth, "find_cc: Detecting cc (host)\n"); /* Find a working cc (no arguments) */ if (!(((cc != NULL) && (trycc(logdepth+1, cc, test_c))) || trycc(logdepth+1, "gcc", test_c) || trycc(logdepth+1, "cc", test_c))) { report("FAILED to find a compiler\n"); logprintf(logdepth, "find_cc: FAILED to find a compiler\n"); return 1; } } } else { put("cc/cc", cc); logprintf(logdepth+1, "using user supplied '%s' (will test later)\n", cc); } /* cflags (again) */ if (cflags == NULL) { logprintf(logdepth, "find_cc: Detecting -pipe\n"); if (compile_run(logdepth+1, test_c, NULL, "-pipe", "", &out) == 0) { if (target_emu_fail(out) || (strncmp(out, "OK", 2) == 0)) { append("cc/cflags", " -pipe"); } free(out); } } if (get("cc/cflags") == NULL) put("cc/cflags", ""); /* ldflags (again) */ if (get("cc/ldflags") == NULL) put("cc/ldflags", ""); /* Final test of all arguments together */ logprintf(logdepth, "find_cc: final test on cc and all flags \n"); if (compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) != 0) { report("FAILED to get the compiler and all flags to work together\n"); logprintf(logdepth, "find_cc: the compiler and all the flags don't work well together, aborting\n"); if (out != NULL) free(out); return 1; } report("OK ('%s', '%s', '%s')\n", get("cc/cc"), get("cc/cflags"), get("cc/ldflags")); logprintf(logdepth, "find_cc: conclusion: cc='%s' cflags='%s' ldflags='%s'\n", get("cc/cc"), get("cc/cflags"), get("cc/ldflags")); if (out != NULL) free(out); return 0; } int find_cc_argstd(const char *det_name, int logdepth, int fatal) { char *test_c = "#include \nint main() { printf(\"OK\\n\");\nreturn 0;}\n"; char *out = NULL; char **flg, *flags[] = {"-ansi", "-pedantic", "-Wall", "-std=c89", "-std=c99", "-Werror", "-Wextra", "-W", "-pg", "-no-pie", "-static-pie", NULL}; const char *det_target = det_list_target(det_name); require("cc/cc", logdepth, fatal); logprintf(logdepth, "find_cc: Detecting CC args %s\n", det_target); report("Checking for cc args for std %s... ", det_target); for(flg = flags; *flg != NULL; flg++) { char name[128], *end; const char *found = ""; sprintf(name, "cc/argstd/%s", (*flg)+1); end = strchr(name, '='); if (end != NULL) *end = '_'; if (!asked_for(name, det_name)) continue; if (compile_run(logdepth+1, test_c, NULL, *flg, "", &out) == 0) { if (target_emu_fail(out) || (strncmp(out, "OK", 2) == 0)) { found = *flg; report(" "); report(found); } free(out); } put(name, found); } if (is_dep_wild(det_name)) put("cc/argstd/presents", strue); /* to avoid re-detection*/ report("\n"); return 0; } int find_cc_argmachine(const char *name, int logdepth, int fatal) { #define ARGM(flag) "-m" #flag , "-mno-" #flag const char *test_c = "#include \nint main() { printf(\"OK\\n\");\nreturn 0;}\n"; char *out = NULL; const char **flg, *flags[] = { ARGM(mmx), ARGM(sse), ARGM(sse2), ARGM(sse3), ARGM(ssse3), ARGM(sse4), ARGM(sse4.1), ARGM(sse4.2), ARGM(avx), ARGM(avx2), NULL}; require("cc/cc", logdepth, fatal); logprintf(logdepth, "find_cc: Detecting CC machine args\n"); report("Checking for cc args for machine... "); for(flg = flags; *flg != NULL; flg++) { char name[128], *end; const char *found = ""; { const char* ptr = (*flg) + 1; strcpy(name, "cc/argmachine/"); end = name + strlen(name); while(*ptr) { if('.'!=*ptr && '-'!=*ptr) *end++ = *ptr; ++ptr; } *end = '\0'; } end = strchr(name, '='); if (end != NULL) *end = '_'; if (compile_run(logdepth+1, test_c, NULL, *flg, "", &out) == 0) { if (target_emu_fail(out) || (strncmp(out, "OK", 2) == 0)) { found = *flg; report(" "); report(found); } free(out); } put(name, found); } report("\n"); return 0; #undef ARGM } int find_inline(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "static inline void test_inl()" NL "{" NL " puts(\"OK\");" NL "}" NL "int main() {" NL " test_inl();" NL " return 0;" NL "}" NL ; require("cc/cc", logdepth, fatal); report("Checking for inline... "); logprintf(logdepth, "find_inline: trying to find inline...\n"); logdepth++; if (try(logdepth, NULL, test_c, "OK")) { put("cc/inline", strue); report("Found.\n"); return 0; } put("cc/inline", sfalse); report("Not found.\n"); return 1; } int find_varargmacro(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "#define pr(fmt, x...) {printf(\"PR \"); printf(fmt, x); }" NL "int main() {" NL " pr(\"%d %d %s\", 42, 8192, \"test\");" NL " puts(\"\");" NL " return 0;" NL "}" NL ; require("cc/cc", logdepth, fatal); report("Checking for vararg macro... "); logprintf(logdepth, "find_varargmacro: trying to find vararg macro...\n"); logdepth++; if (try(logdepth, NULL, test_c, "PR 42 8192 test")) { put("cc/varargmacro", strue); report("Found.\n"); return 0; } put("cc/varargmacro", sfalse); report("Not found.\n"); return 1; } int find_funcmacro(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "int main() {" NL " printf(\"%s\\n\", __func__);" NL " return 0;" NL "}" NL ; require("cc/cc", logdepth, fatal); report("Checking for __func__ macro... "); logprintf(logdepth, "find_funcmacro: trying to find __func__ macro...\n"); logdepth++; if (try(logdepth, NULL, test_c, "main")) { put("cc/funcmacro", strue); report("Found.\n"); return 0; } put("cc/funcmacro", sfalse); report("Not found.\n"); return 1; } int find_constructor(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "void startup() __attribute__ ((constructor));" NL "void startup()" NL "{" NL " puts(\"OK\");" NL "}" NL "int main() {" NL " return 0;" NL "}" NL ; require("cc/cc", logdepth, fatal); report("Checking for constructor... "); logprintf(logdepth, "find_constructor: trying to find constructor...\n"); logdepth++; if (try(logdepth, NULL, test_c, "OK")) { put("cc/constructor", strue); report("Found.\n"); return 0; } put("cc/constructor", sfalse); report("Not found.\n"); return 1; } int find_destructor(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "void startup() __attribute__ ((destructor));" NL "void startup()" NL "{" NL " puts(\"OK\");" NL "}" NL "int main() {" NL " return 0;" NL "}" NL ; require("cc/cc", logdepth, fatal); report("Checking for destructor... "); logprintf(logdepth, "find_destructor: trying to find destructor...\n"); logdepth++; if (try(logdepth, NULL, test_c, "OK")) { put("cc/destructor", strue); report("Found.\n"); return 0; } put("cc/destructor", sfalse); report("Not found.\n"); return 1; } static int test_fattr(const char *name, int logdepth, int fatal, const char *fattr) { char path[64]; char test_c[256]; const char *test_c_tmp = NL "#include " NL "static void test1() __attribute__ ((%s));" NL "static void test1()" NL "{" NL " puts(\"OK\");" NL "}" NL "int main() {" NL " puts(\"OK\");" NL " return 0;" NL "}" NL ; require("cc/cc", logdepth, fatal); sprintf(test_c, test_c_tmp, fattr); sprintf(path, "cc/func_attr/%s/presents", fattr); report("Checking for function attribute %s... ", fattr); logprintf(logdepth, "test_fattr: trying to find %s...\n", fattr); logdepth++; if (try(logdepth, NULL, test_c, "OK")) { put(path, strue); report("Found.\n"); return 0; } put(path, sfalse); report("Not found.\n"); return 1; } int find_fattr_unused(const char *name, int logdepth, int fatal) { return test_fattr(name, logdepth, fatal, "unused"); } static int test_declspec(const char *name, int logdepth, int fatal, const char *dspec) { char path[64]; char test_c[256]; const char *test_c_tmp = NL "#include " NL "void __declspec (%s) test1();" NL "void test1()" NL "{" NL " puts(\"OK\");" NL "}" NL "int main() {" NL " test1();" NL " return 0;" NL "}" NL ; require("cc/cc", logdepth, fatal); sprintf(test_c, test_c_tmp, dspec); sprintf(path, "cc/declspec/%s/presents", dspec); report("Checking for declspec %s... ", dspec); logprintf(logdepth, "test_declspec: trying to find %s...\n", dspec); logdepth++; if (try(logdepth, NULL, test_c, "OK")) { put(path, strue); report("Found.\n"); return 0; } put(path, sfalse); report("Not found.\n"); return 1; } int find_declspec_dllimport(const char *name, int logdepth, int fatal) { return test_declspec(name, logdepth, fatal, "dllimport"); } int find_declspec_dllexport(const char *name, int logdepth, int fatal) { return test_declspec(name, logdepth, fatal, "dllexport"); } static int test_dll_auxfile(const char *name, int logdepth, int fatal, const char *path, const char *ldflag, const char *filename) { char *ldflags; char test_c[256]; const char *test_c_template = NL "#include " NL "void %s test1();" NL "void test1()" NL "{" NL " puts(\"OK\");" NL "}" NL "int main() {" NL " test1();" NL " return 0;" NL "}" NL ; const char *dspec; require("cc/cc", logdepth, fatal); require("cc/declspec/dllexport/*", logdepth, 0); if (istrue("cc/declspec/dllexport/presents")) dspec = " __declspec(dllexport) "; else dspec = ""; sprintf(test_c, test_c_template, dspec); report("Checking for DLL flag %s... ", ldflag); logprintf(logdepth, "test_dll_auxfile: trying to find %s...\n", ldflag); logdepth++; ldflags = str_concat("", ldflag, ",", filename, " ", get("cc/ldflags"), NULL); if (try_flags(logdepth, NULL, test_c, NULL, ldflags, "OK")) { unlink(filename); put(path, ldflag); free(ldflags); report("Found.\n"); return 0; } unlink(filename); free(ldflags); report("Not found.\n"); return 1; } int find_cc_wloutimplib(const char *name, int logdepth, int fatal) { return test_dll_auxfile(name, logdepth, fatal, "cc/wloutimplib", "-Wl,--out-implib", "libscconfig_0.a"); } int find_cc_wloutputdef(const char *name, int logdepth, int fatal) { return test_dll_auxfile(name, logdepth, fatal, "cc/wloutputdef", "-Wl,--output-def", "libscconfig_0.def"); } /* Hello world program to test compiler flags */ static const char *test_hello_world = NL "#include " NL "int main() {" NL " puts(\"OK\");" NL " return 0;" NL "}" NL ; static int try_hello(int logdepth, const char *cflags, const char *ldflags, const char *name, const char *value) { if (try_flags(logdepth, NULL, test_hello_world, cflags, ldflags, "OK")) { put(name, value); report("OK (%s)\n", value); return 1; } return 0; } int find_rdynamic(const char *name, int logdepth, int fatal) { const char *node = "cc/rdynamic"; require("cc/cc", logdepth, fatal); report("Checking for rdynamic... "); logprintf(logdepth, "find_rdynamic: trying to find rdynamic...\n"); logdepth++; if (try_hello(logdepth, NULL, "-rdynamic", node, "-rdynamic")) return 0; if (try_hello(logdepth, NULL, "-Wl,-export-dynamic", node, "-Wl,-export-dynamic")) return 0; if (try_hello(logdepth, NULL, NULL, node, "")) return 0; report("Not found.\n"); return 1; } int find_cc_fpie(const char *name, int logdepth, int fatal) { const char *test_c = test_hello_world; require("cc/cc", logdepth, fatal); /* TODO: what about -fpic? */ report("Checking for -fpie... "); logprintf(logdepth, "find_cc_fpie: trying to find -fpie...\n"); logdepth++; /* NOTE: some gcc configuration might not pass the -pie flag to the linker, so */ /* try to detect whether we can force it to the linker */ if (try_icl(logdepth, "cc/fpie", test_c, NULL, "-fpie", "-pie -Wl,-pie")) return 0; if (try_icl(logdepth, "cc/fpie", test_c, NULL, "-fPIE", "-pie -Wl,-pie")) return 0; if (try_icl(logdepth, "cc/fpie", test_c, NULL, "-fpie", "-pie")) return 0; if (try_icl(logdepth, "cc/fpie", test_c, NULL, "-fPIE", "-pie")) return 0; if (try_icl(logdepth, "cc/fpie", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "cc/fpie"); } int find_cc_fnopie(const char *name, int logdepth, int fatal) { const char *test_c = test_hello_world; require("cc/cc", logdepth, fatal); report("Checking for -fno-pie... "); logprintf(logdepth, "find_cc_fnopie: trying to find -fno-pie...\n"); logdepth++; if (try_icl(logdepth, "cc/fnopie", test_c, NULL, "-fno-pie", NULL)) return 0; if (try_icl(logdepth, "cc/fnopie", test_c, NULL, "-fno-pie", "-static")) return 0; if (try_icl(logdepth, "cc/fnopie", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "cc/fnopie"); } int find_cc_fnopic(const char *name, int logdepth, int fatal) { const char *test_c = test_hello_world; require("cc/cc", logdepth, fatal); report("Checking for -fno-pic... "); logprintf(logdepth, "find_cc_fnopic: trying to find -fno-pic...\n"); logdepth++; if (try_icl(logdepth, "cc/fnopic", test_c, NULL, "-fno-pic", NULL)) return 0; if (try_icl(logdepth, "cc/fnopic", test_c, NULL, "-fno-pic", "-static")) return 0; if (try_icl(logdepth, "cc/fnopic", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "cc/fnopic"); } int find_soname(const char *name, int logdepth, int fatal) { require("cc/cc", logdepth, fatal); report("Checking for soname... "); logprintf(logdepth, "find_soname: trying to find soname...\n"); logdepth++; if (try_hello(logdepth, NULL, "-Wl,-soname,libscconfig.0", "cc/soname", "-Wl,-soname,")) return 0; if (try_hello(logdepth, NULL, NULL, "cc/soname", "")) return 0; report("Not found.\n"); return 1; } int find_so_undefined(const char *name, int logdepth, int fatal) { static const char *test_c = NL "#include " NL "void intentionally_undefined_symbol(void);" NL "int main() {" NL " intentionally_undefined_symbol();" NL " puts(\"OK\");" NL " return 0;" NL "}" NL ; const char **t, *try_ldflags[] = { "", "-undefined dynamic_lookup", /* OSX + clang */ NULL }; require("cc/cc", logdepth, fatal); require("cc/ldflags_dynlib", logdepth, fatal); report("Checking for so_undefined... "); logprintf(logdepth, "find_so_undefined: trying to find so_undefined...\n"); logdepth++; for(t = try_ldflags; *t != NULL; t++) { const char *fpic; char *ldf, *oname = ".o", *libname_dyn, *cflags_c; int res1, res2; fpic = get("cc/fpic"); if (fpic == NULL) fpic = ""; cflags_c = str_concat(" ", "-c", fpic, NULL); libname_dyn = (char *)get("sys/ext_dynlib"); ldf = str_concat(" ", get("cc/ldflags_dynlib"), *t, NULL); res1 = compile_code(logdepth, test_c, &oname, NULL, cflags_c, NULL); res2 = compile_file(logdepth, oname, &libname_dyn, NULL, NULL, ldf); unlink(libname_dyn); unlink(oname); free(libname_dyn); free(oname); free(cflags_c); if ((res1 == 0) && (res2 == 0)) { put(name, *t); report("OK (%s)\n", *t); return 0; } } report("Not found.\n"); return 1; } int find_wlrpath(const char *name, int logdepth, int fatal) { require("cc/cc", logdepth, fatal); report("Checking for rpath... "); logprintf(logdepth, "find_wlrpath: trying to find rpath...\n"); logdepth++; if (try_hello(logdepth, NULL, "-Wl,-rpath=.", "cc/wlrpath", "-Wl,-rpath=")) return 0; report("Not found.\n"); return 1; } int find_fpic(const char *name, int logdepth, int fatal) { require("cc/cc", logdepth, fatal); report("Checking for -fpic... "); logprintf(logdepth, "find_fpic: trying to find -fpic...\n"); logdepth++; if (try_hello(logdepth, NULL, "-fPIC", "cc/fpic", "-fPIC")) return 0; if (try_hello(logdepth, NULL, "-fpic", "cc/fpic", "-fpic")) return 0; if (try_hello(logdepth, NULL, NULL, "cc/fpic", "")) return 0; report("Not found.\n"); return 1; } /* Hello world lib... */ static const char *test_lib = NL "#include " NL "int hello() {" NL " puts(\"OK\");" NL " return 0;" NL "}" NL ; /* ...and the corresponding host application */ static const char *test_host = NL "#include " NL "#include " NL "#include %s" NL "#ifndef RTLD_NOW" NL "#define RTLD_NOW RTLD_LAZY" /* on old BSD and probably on SunOS */ NL "#endif" NL "int main() {" NL " void *handle = NULL;" NL " void (*func)() = NULL;" NL " char *error;" NL NL " handle = dlopen(\"%s\", RTLD_NOW);" NL " if (handle == NULL) {" NL " printf(\"dlopen fails: %%s\", dlerror());" NL " return 1;" NL " }" NL " func = dlsym(handle, \"hello\");" NL " if (func == NULL) {" NL " printf(\"dlsym fails: %%s\", dlerror());" NL " return 1;" NL " }" NL " func();" NL " return 0;" NL "}" NL ; static int try_dynlib(int logdepth, const char *cflags, char *concated_ldflags, const char *name, const char *value, const char *host_app_cflags, const char *host_app_ldflags) { char test_host_app[1024]; const char *fpic; const char *ld_include; const char *dlc; char *libname, *libname_dyn; char *cflags_c; char *oname = ".o"; int ret = 0; dlc = get("libs/dl-compat"); if ((dlc != NULL) && (strcmp(dlc, strue) == 0)) ld_include = ""; else ld_include = ""; fpic = get("cc/fpic"); if (fpic == NULL) fpic = ""; if (cflags == NULL) cflags=""; cflags_c = malloc(strlen(cflags) + 8 + strlen(fpic)); sprintf(cflags_c, "%s -c %s", cflags, fpic); libname_dyn = libname = (char *)get("sys/ext_dynlib"); if ((compile_code(logdepth, test_lib, &oname, NULL, cflags_c, NULL) != 0) || (compile_file(logdepth, oname, &libname_dyn, NULL, NULL, concated_ldflags) != 0)) { report("('%s': nope) ", concated_ldflags); } else { sprintf(test_host_app, test_host, ld_include, libname_dyn); if (try_flags(logdepth, NULL, test_host_app, host_app_cflags, host_app_ldflags, "OK")) { put(name, value); report("OK (%s)\n", value); ret = 1; } } unlink(libname_dyn); unlink(oname); if (libname != libname_dyn) free(libname_dyn); free(oname); free(concated_ldflags); free(cflags_c); return ret; } int find_ldflags_dynlib(const char *name, int logdepth, int fatal) { require("cc/cc", logdepth, fatal); require("cc/rdynamic", logdepth, fatal); require("cc/fpic", logdepth, fatal); require("libs/ldl", logdepth, fatal); report("Checking for dynamic library ldflags... "); logprintf(logdepth, "find_ldflags_dynlib: trying to find dynamic library ldflags...\n"); logdepth++; if (try_dynlib(logdepth, NULL, concat_nodes("-dynamic -shared", "cc/rdynamic", "libs/ldl", NULL), "cc/ldflags_dynlib", "-dynamic -shared", NULL, get("libs/ldl"))) return 0; if (try_dynlib(logdepth, NULL, concat_nodes("-shared", "cc/rdynamic", "libs/ldl", NULL), "cc/ldflags_dynlib", "-shared", NULL, get("libs/ldl"))) return 0; if (try_dynlib(logdepth, NULL, concat_nodes("-G", "libs/ldl", NULL), "cc/ldflags_dynlib", "-G", NULL, get("libs/ldl"))) return 0; /* xlc (on AIX) */ report("Not found.\n"); return 1; } static int try_dll_or_so(int logdepth, int is_dll, const char *lib_ldflags, const char *name, const char *value, const char *dspec_dllexport, const char *dspec_dllimport, const char *app_cflags, const char *app_ldflags) { static const char *test_lib_template = NL "#include " NL "%s void hello();" NL "void hello() {" NL " puts(\"OK\");" NL "}" NL ; static const char *test_app_template = NL "%s void hello();" NL "int main() {" NL " hello();" NL " return 0;" NL "}" NL ; char test_lib[1024]; char test_app[1024]; const char *fpic; char *cflags_c; char *oname, *oname_ext; char *libname, *libname_ext; char *appname = NULL, *appname_ext = NULL; char *lib_filename = NULL, *lib_dirname = NULL; char *lib_ldflags_new = NULL; char *app_ldflags_new = NULL; size_t len, ii; int ret = 0; ++logdepth; require("cc/cc", logdepth, 0); require("cc/cflags", logdepth, 0); require("cc/ldflags", logdepth, 0); require("cc/fpic", logdepth, 0); require("sys/ext_exe", logdepth, 0); require("sys/ext_dynlib_native", logdepth, 0); fpic = get("cc/fpic"); if (fpic == NULL) fpic = ""; if (app_cflags == NULL) app_cflags = ""; if (app_ldflags == NULL) app_ldflags = ""; cflags_c = str_concat(" ", get("cc/cflags"), "-c", fpic, NULL); oname = oname_ext = ".o"; libname = libname_ext = (char *)get("sys/ext_dynlib_native"); sprintf(test_lib, test_lib_template, dspec_dllexport); lib_ldflags_new = str_concat(" ", get("cc/ldflags"), lib_ldflags, NULL); if ((compile_code(logdepth, test_lib, &oname, NULL, cflags_c, NULL) != 0) || (compile_file(logdepth, oname, &libname, NULL, NULL, lib_ldflags_new) != 0)) { report("FAILED (compiling %s)\n", (is_dll?"DLL":"SO")); } else { lib_filename = file_name(libname); lib_dirname = dir_name(libname); if (!is_dll) { len = strlen(lib_filename) - strlen(libname_ext); for (ii=3; ii" NL "int main() {" NL " char *s;" NL " s = alloca(128);" NL " if (s != NULL)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL ; static int try_alloca(int logdepth, const char *cflags, const char *ldflags, const char *name, const char *value) { if (try_flags(logdepth, NULL, test_alloca, cflags, ldflags, "OK")) { put(name, value); report("OK (%s)\n", value); return 1; } return 0; } int find_alloca(const char *name, int logdepth, int fatal) { require("cc/cc", logdepth, fatal); report("Checking for alloca()... "); logprintf(logdepth, "find_alloca: trying to find alloca()...\n"); logdepth++; if (try_alloca(logdepth, NULL, NULL, "cc/alloca/presents", "true")) return 0; put("cc/alloca/presents", "false"); report("Not found.\n"); return 1; } int find__exit(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "int main() {" NL " _exit(0);" NL " puts(\"BAD\");" NL " return 0;" NL "}" NL ; require("cc/cc", logdepth, fatal); report("Checking for _exit()... "); logprintf(logdepth, "find__exit: trying to find _exit()...\n"); logdepth++; if (try_flags_inv(logdepth, NULL, test_c, NULL, NULL, "BAD")) { put("cc/_exit/presents", strue); report("found\n"); return 0; } put("cc/_exit/presents", sfalse); report("Not found.\n"); return 1; } int find_cc_pragma_message(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "#define DO_PRAGMA(arg) _Pragma(#arg)" NL "#define TODO(x) DO_PRAGMA(message(\"TODO: \" #x))" NL "TODO(test)" NL "int main()" NL "{" NL " puts(\"OK\");" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for _Pragma(message)... "); logprintf(logdepth, "find_cc_pragma_message: trying to find pragma_message...\n"); logdepth++; if (try(logdepth, NULL, test_c, "OK")) { put("cc/pragma_message", strue); report("Found.\n"); return 0; } put("cc/pragma_message", sfalse); report("Not found.\n"); return 1; } int find_cc_static_libgcc(const char *name, int logdepth, int fatal) { const char *test_c = test_hello_world; const char *key = "cc/static_libgcc"; require("cc/cc", logdepth, fatal); report("Checking for -static-libgcc... "); logprintf(logdepth, "find_cc_static_libgcc: trying to find -static-libgcc...\n"); logdepth++; if (try_icl(logdepth, key, test_c, NULL, NULL, "-static-libgcc")) return 0; return try_fail(logdepth, key); } fungw-1.2.0/scconfig/src/default/find_types.c0000644000175100017510000003142014003560750017356 0ustar svnsvn/* scconfig - detection of types and type sizes Copyright (C) 2012 Tibor Palinkas Copyright (C) 2017-2018 Aron Barath This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" /* assume there is no integer that is at least this wide, in bytes */ #define MAX_INT_WIDTH 9 /* ('long double' can be 16 bytes; see https://en.wikipedia.org/wiki/Long_double) */ #define MAX_FLT_WIDTH 17 static int try_size(int logdepth, char *cflags, char *ldflags, const char *type, int use_stdint, const char *path, const char **sizearr, unsigned char *inc_stdint, const int max_width) { char *out = NULL; const char *test_c_template = NL "#include " NL "int main() {" NL " printf(\"OK %%d\\n\", sizeof(%s));" NL " return 0;" NL "}" NL; char test_c[512], *start; const char *inc = "#include \n"; int size; if (use_stdint) { strcpy(test_c, inc); start = test_c + strlen(inc); } else start = test_c; sprintf(start, test_c_template, type); report("Testing size of type %25s... ", type); logprintf(logdepth, "trying size with ldflags '%s'\n", ldflags == NULL ? get("cc/ldflags") : ldflags); if (compile_run(logdepth+1, test_c, NULL, cflags, ldflags, &out) == 0) { if (target_emu_fail(out)) { report(" FAIL (emulator)\n"); free(out); return -1; } if (strncmp(out, "OK", 2) == 0) { size = atoi(out+3); if ((size > 0) && (size < max_width)) { sprintf(test_c, "%d", size); put(path, test_c); sizearr[size] = type; if (inc_stdint != NULL) inc_stdint[size] = use_stdint; report(" OK, size %d byte%s\n", size, (size > 1) ? "s" : ""); } else { report(" FAIL, size %d bytes\n", size); size = -1; } free(out); return size; } free(out); } report(" FAIL (compile)\n"); return -1; } int find_types_stdint(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " if (sizeof(uint8_t) == 1)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for stdint.h... "); logprintf(logdepth, "find_types_stdint: trying to find stdint.h...\n"); logdepth++; if (try_icl(logdepth, "libs/types/stdint", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "libs/types/stdint"); } int find_types_sizes(const char *name, int logdepth, int fatal) { const char *stdint; const char *sizearr_u[MAX_INT_WIDTH]; const char *sizearr_s[MAX_INT_WIDTH]; const char *sizearr_f[MAX_FLT_WIDTH]; unsigned char inc_stdint_u[MAX_INT_WIDTH]; unsigned char inc_stdint_s[MAX_INT_WIDTH]; int n; const char *includes = ""; const char *path_template = "sys/types/size/%d_%c_int"; const char *path_template_f = "sys/types/size/%d_float"; char path[64]; require("cc/cc", logdepth, fatal); require("libs/types/stdint/presents", logdepth, 0); stdint = get("libs/types/stdint/presents"); for(n = 0; n < MAX_INT_WIDTH; n++) { sizearr_u[n] = NULL; sizearr_s[n] = NULL; inc_stdint_u[n] = 0; inc_stdint_s[n] = 0; } for(n = 0; n < MAX_FLT_WIDTH; n++) sizearr_f[n] = NULL; try_size(logdepth+1, NULL, NULL, "unsigned long long int", 0, "sys/types/size/unsigned_long_long_int", sizearr_u, NULL, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "unsigned char", 0, "sys/types/size/unsigned_char", sizearr_u, NULL, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "unsigned short int", 0, "sys/types/size/unsigned_short_int", sizearr_u, NULL, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "unsigned int", 0, "sys/types/size/unsigned_int", sizearr_u, NULL, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "unsigned long int", 0, "sys/types/size/unsigned_long_int", sizearr_u, NULL, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "signed long long int", 0, "sys/types/size/signed_long_long_int", sizearr_s, NULL, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "signed char", 0, "sys/types/size/signed_char", sizearr_s, NULL, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "signed short int", 0, "sys/types/size/signed_short_int", sizearr_s, NULL, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "signed int", 0, "sys/types/size/signed_int", sizearr_s, NULL, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "signed long int", 0, "sys/types/size/signed_long_int", sizearr_s, NULL, MAX_INT_WIDTH); if ((stdint != NULL) && (istrue(stdint))) { try_size(logdepth+1, NULL, NULL, "uint8_t", 1, "sys/types/size/uint8_t", sizearr_u, inc_stdint_u, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "uint16_t", 1, "sys/types/size/uint16_t", sizearr_u, inc_stdint_u, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "uint32_t", 1, "sys/types/size/uint32_t", sizearr_u, inc_stdint_u, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "uint64_t", 1, "sys/types/size/uint64_t", sizearr_u, inc_stdint_u, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "int8_t", 1, "sys/types/size/int8_t", sizearr_s, inc_stdint_s, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "int16_t", 1, "sys/types/size/int16_t", sizearr_s, inc_stdint_s, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "int32_t", 1, "sys/types/size/int32_t", sizearr_s, inc_stdint_s, MAX_INT_WIDTH); try_size(logdepth+1, NULL, NULL, "int64_t", 1, "sys/types/size/int64_t", sizearr_s, inc_stdint_s, MAX_INT_WIDTH); } try_size(logdepth+1, NULL, NULL, "float", 0, "sys/types/size/float", sizearr_f, NULL, MAX_FLT_WIDTH); try_size(logdepth+1, NULL, NULL, "double", 0, "sys/types/size/double", sizearr_f, NULL, MAX_FLT_WIDTH); try_size(logdepth+1, NULL, NULL, "long double", 0, "sys/types/size/long_double", sizearr_f, NULL, MAX_FLT_WIDTH); for(n = 0; n < MAX_INT_WIDTH; n++) { if (sizearr_u[n] != NULL) { report("Found best fit %d bytes wide uint: %s\n", n, sizearr_u[n]); sprintf(path, path_template, n, 'u'); put(path, sizearr_u[n]); if (inc_stdint_u[n]) includes = "#include "; } if (sizearr_s[n] != NULL) { report("Found best fit %d bytes wide sint: %s\n", n, sizearr_s[n]); sprintf(path, path_template, n, 's'); put(path, sizearr_s[n]); if (inc_stdint_s[n]) includes = "#include "; } } for(n = 0; n < MAX_FLT_WIDTH; n++) { if (sizearr_f[n] != NULL) { report("Found best fit %d bytes wide float: %s\n", n, sizearr_f[n]); sprintf(path, path_template_f, n); put(path, sizearr_f[n]); } } put("sys/types/size/presents", strue); /* to avoid redetection */ put("sys/types/size/includes", includes); return 0; } int find_types_something_t(const char *name, int logdepth, int fatal, const char *prefix, const char *typ, const char *define, const char *try_include) { char *out = NULL; int res; char test_c[512]; char node[256], *nodeend; const char **include, *includes[] = {"", "#include ", "#include ", "#include ", "#include ", "#include ", "#include ", NULL}; const char ** const first_include = (try_include && *try_include) ? includes : (includes+1); char *test_c_include = NL "%s" NL "int my_puts(const char *s);" NL "%s" NL "int main() {" NL " %s s;" NL " my_puts(\"OK\");" NL " return 0;" NL "}" NL "#include " NL "int my_puts(const char *s)" NL "{" NL " return puts(s);" NL "}" NL; char *test_c_size = NL "%s" NL "#include " NL "%s" NL "int main() {" NL " printf(\"%%d\", sizeof(%s));" NL " return 0;" NL "}" NL; char *test_c_broken = NL "%s" NL "int my_puts(const char *s);" NL "%s" NL "int main() {" NL " %s s;" NL " void *v;" NL " if (sizeof(v) != sizeof(s)) my_puts(\"yes\");" NL " else my_puts(\"no\");" NL " return 0;" NL "}" NL "#include " NL "int my_puts(const char *s)" NL "{" NL " return puts(s);" NL "}" NL; includes[0] = try_include; require("cc/cc", logdepth, fatal); report("Checking for type %s... ", typ); logprintf(logdepth, "find_types_something_t: Checking for %s...\n", typ); logdepth++; sprintf(node, "%s/%s", prefix, typ); nodeend = node + strlen(node); /* replace '*' at the end of the node path with _ptr so it can be saved in the tree */ if (nodeend[-1] == '*') { nodeend--; while((nodeend > node) && (*nodeend == ' ')) nodeend--; strcpy(nodeend-1, "_ptr"); nodeend+=4; } nodeend[0] = '/'; nodeend[1] = '\0'; nodeend++; if (define == NULL) define = ""; for(include = first_include; *include != NULL; include++) { sprintf(test_c, test_c_include, define, *include, typ); if ((compile_run(logdepth, test_c, NULL, NULL, NULL, &out) == 0) && out != NULL && (strncmp(out, "OK", 2) == 0)) { report("Found; "); logprintf(logdepth+1, "include %s works\n", *include); sprintf(nodeend, "includes"); if (define) { put(node, define); append(node, "\\n"); append(node, *include); } else put(node, *include); break; } logprintf(logdepth+1, "include %s fails\n", *include); if (out != NULL) free(out); } if (*include == NULL) { report("Not found\n"); return 1; } sprintf(nodeend, "presents"); put(node, strue); /* check if typ is broken (smaller than void *) */ sprintf(test_c, test_c_broken, define, *include, typ); if (compile_run(logdepth, test_c, NULL, NULL, NULL, &out) == 0) { if ((out != NULL) && (strncmp(out, "yes", 3) == 0)) { report("(%s is narrower than void *)\n", typ); sprintf(nodeend, "broken"); put(node, strue); res = 0; } else if ((out != NULL) && (strncmp(out, "no", 2) == 0)) { report("(%s is not narrower than void *)\n", typ); sprintf(nodeend, "broken"); put(node, sfalse); res = 0; } else { report("ERROR: test failed (%s)\n", out); res = 1; } } if (out != NULL) free(out); if (res == 0) { report("Checking for size of %s... ", typ); sprintf(test_c, test_c_size, define, *include, typ); if (compile_run(logdepth, test_c, NULL, NULL, NULL, &out) == 0) { if (out != NULL) { report("(sizeof %s is %s)\n", typ, out); sprintf(nodeend, "size"); put(node, out); } } if (out != NULL) free(out); } return res; } int find_types_size_t(const char *name, int logdepth, int fatal) { return find_types_something_t(name, logdepth, fatal, "sys/types", "size_t", NULL, NULL); } int find_types_off_t(const char *name, int logdepth, int fatal) { return find_types_something_t(name, logdepth, fatal, "sys/types", "off_t", NULL, NULL); } int find_types_off64_t(const char *name, int logdepth, int fatal) { return find_types_something_t(name, logdepth, fatal, "sys/types", "off64_t", NULL, NULL) && find_types_something_t(name, logdepth, fatal, "sys/types", "off64_t", "#define _LARGEFILE64_SOURCE", NULL); } int find_types_gid_t(const char *name, int logdepth, int fatal) { return find_types_something_t(name, logdepth, fatal, "sys/types", "gid_t", NULL, NULL); } int find_types_uid_t(const char *name, int logdepth, int fatal) { return find_types_something_t(name, logdepth, fatal, "sys/types", "uid_t", NULL, NULL); } int find_types_pid_t(const char *name, int logdepth, int fatal) { return find_types_something_t(name, logdepth, fatal, "sys/types", "pid_t", NULL, NULL); } int find_types_mode_t(const char *name, int logdepth, int fatal) { return find_types_something_t(name, logdepth, fatal, "sys/types", "mode_t", NULL, NULL); } int find_types_nlink_t(const char *name, int logdepth, int fatal) { return find_types_something_t(name, logdepth, fatal, "sys/types", "nlink_t", NULL, NULL); } int find_types_ptrdiff_t(const char *name, int logdepth, int fatal) { return find_types_something_t(name, logdepth, fatal, "sys/types", "ptrdiff_t", NULL, NULL); } int find_types_dev_t(const char *name, int logdepth, int fatal) { return find_types_something_t(name, logdepth, fatal, "sys/types", "dev_t", NULL, NULL); } int find_types_ino_t(const char *name, int logdepth, int fatal) { return find_types_something_t(name, logdepth, fatal, "sys/types", "ino_t", NULL, NULL); } int find_types_void_ptr(const char *name, int logdepth, int fatal) { return find_types_something_t(name, logdepth, fatal, "sys/types", "void *", NULL, NULL); } fungw-1.2.0/scconfig/src/default/main.c0000644000175100017510000000506513334271760016152 0ustar svnsvn/* scconfig - test code for default and scripts Copyright (C) 2009..2016 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "db.h" #include "find.h" #include "log.h" #include "arg.h" #include "dep.h" #include "deps_default.h" #include "libs.h" #include "hooks.h" #include "regex.h" #include "main_custom_args.h" #include "main_lib.h" void re_fail(char *s, char c) { fprintf(stderr, "Regex error: %s [opcode %o]\n", s, c); abort(); } int no_autodetect_sys = 0; int no_save_cache = 0; int main(int argc, char *argv[]) { int blind_save; if (main_init() != 0) return 1; if (main_process_args(argc, argv) != 0) return 1; if (!no_autodetect_sys) { find_target("", 0, 1); blind_save = cross_blind; cross_blind = 0; printf("--- Detecting host\n"); require("sys/name", 0, 1); } if (hook_detect_host()) { fprintf(stderr, "hook_detect_host failed, exiting\n"); return 1; } cross_blind = blind_save; if (!no_autodetect_sys) { if (!iscross) printf("--- Detecting target (same as host)\n"); else printf("--- Detecting target (differs from host)\n"); } db_cd("/target"); run_custom_reqs(); if (hook_detect_target()) { fprintf(stderr, "hook_detect_target failed, exiting\n"); return 1; } #ifdef RUNTIME if (!no_autodetect_sys) { if (!iscross) printf("--- Detecting runtime (same as host)\n"); else printf("--- Detecting runtime (differs from host)\n"); } db_cd("/runtime"); if (hook_detect_runtime()) { fprintf(stderr, "hook_detect_runtime failed, exiting\n"); return 1; } #endif if (hook_generate()) { fprintf(stderr, "hook_generate failed, exiting\n"); return 1; } if (!no_save_cache) export("config.cache", 1, "/"); main_uninit(); return 0; } fungw-1.2.0/scconfig/src/default/lib_filelist.c0000644000175100017510000001776413240634424017674 0ustar svnsvn/* scconfig - library for listing files in a directory Copyright (C) 2009..2012 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "db.h" #include "libs.h" #include "log.h" #include "dep.h" static void destroy_testdir(int logdepth, char *dir) { const char *rm; char *cmd, *dir_esc; rm = get("fstools/rm"); if (rm == NULL) { logprintf(logdepth, "CAN NOT delete test directory '%s': no rm available\n", dir); return; } if (dir == NULL) return; logprintf(logdepth, "deleting test directory '%s'\n", dir); cmd = malloc(strlen(dir) + strlen(rm) + 4); dir_esc = shell_escape_dup(dir); sprintf(cmd, "%s %s", rm, dir_esc); run_shell(0, cmd, NULL); free(cmd); free(dir); free(dir_esc); } static char *create_testdir(int logdepth) { char *dir, *fn, *cmd; const char *mkdir; int n; logprintf(logdepth, "creating test directory\n"); dir = tempdir_new(logdepth+1, ""); logprintf(logdepth, "sandbox is: '%s'\n", dir); fn = malloc(strlen(dir) + 32); for(n = 0; n < 2; n++) { FILE *f; sprintf(fn, "%s%sfile%d", dir, get("sys/path_sep"), n+1); f = fopen(fn, "w"); if (f != NULL) { fclose(f); if (!is_file(fn)) { logprintf(logdepth, "Can not create file %s\n", fn); free(fn); destroy_testdir(logdepth, dir); return NULL; } } } mkdir = get("fstools/mkdir"); cmd = malloc(strlen(dir) + 64); for(n = 0; n < 2; n++) { char *fn_esc; sprintf(fn, "%s%sdir%d", dir, get("sys/path_sep"), n+1); fn_esc = shell_escape_dup(fn); sprintf(cmd, "%s %s", mkdir, fn_esc); free(fn_esc); if (run_shell(logdepth+1, cmd, NULL) || (!is_dir(fn))) { logprintf(logdepth, "Can not create directory %s\n", fn); free(fn); free(cmd); destroy_testdir(logdepth, dir); return NULL; } } free(cmd); free(fn); return dir; } static int test(int logdepth, int argc, char *argv[]) { int dir[2], file[2], n; int *arr, idx; for(n = 0; n < 2; n++) { dir[n] = 0; file[n] = 0; } /* count the list of files, increase arrays by hit */ for(n = 0; n < argc; n++) { arr = NULL; if (strncmp(argv[n], "dir", 3) == 0) { arr = dir; idx = atoi(argv[n]+3); } if (strncmp(argv[n], "file", 4) == 0) { arr = file; idx = atoi(argv[n]+4); } if (arr == NULL) { logprintf(logdepth, "test fails: unknown existing file on the list: '%s'\n", argv[n]); return 0; } idx--; if ((idx < 0) || (idx > 1)) { logprintf(logdepth, "test fails: file name changed: '%s'\n", argv[n]); return 0; } arr[idx]++; } /* check if every item was found exactly once */ for(n = 0; n < 2; n++) { if ((dir[n] != 1) || (file[n] != 1)) { logprintf(logdepth, "test fails: %s%d not found \n", dir[n] ? "file" : "dir", n); return 0; } } return 1; } static void filelist_extract(char *out, const char *dir, const char *method, int *argc, char ***argv) { char *s, sep, *start, *end; int len, allocated = 0, count = 0; char **arr = NULL; const char *psep; psep = get("sys/path_sep"); len = strlen(dir); /* uniform separator */ if (*method == 'w') { /* if word splitting then convert newlines to spaces and convert tabs to spaces */ for(s = out; *s != '\0'; s++) { if ((*s == '\n') || (*s == '\r') || (*s == '\t')) *s = ' '; } sep = ' '; } else { for(s = out; *s != '\0'; s++) { if (*s == '\r') *s = '\n'; } sep = '\n'; } start = out; while((s = str_chr(start, sep)) != NULL) { *s = '\0'; if (strncmp(dir, start, len) == 0) start += len; while(*start == *psep) start++; if (*start != '\0') { end = str_chr(start, *psep); if (end != NULL) *end = '\0'; /* add only if not the same as previous and exists */ if ((!((count > 0) && (strcmp(arr[count - 1], start) == 0))) && (exists_in(dir, start))) { if (count >= allocated) { allocated = count + 32; arr = realloc(arr, sizeof(char *) * allocated); } arr[count] = strclone(start); count++; } } start = s+1; while(*start == sep) start++; } *argc = count; *argv = arr; } void filelist_free(int *argc, char ***argv) { int n; if (*argv == NULL) return; for(n = 0; n < *argc; n++) free((*argv)[n]); free(*argv); *argc = 0; } static char *filelist_asmcmd(const char *dir, const char *list_cmd) { char *cmd; cmd = malloc(strlen(dir) + strlen(list_cmd) + 32); sprintf(cmd, list_cmd, dir); return cmd; } static int try(int logdepth, const char *dir, const char *list_cmd, const char *method) { char *cmd, *out, *dir_esc; int argc, res; char **argv; dir_esc = shell_escape_dup(dir); cmd = filelist_asmcmd(dir_esc, list_cmd); free(dir_esc); logprintf(logdepth, "trying '%s'...\n", cmd); run_shell(logdepth+1, cmd, &out); if (out != NULL) { filelist_extract(out, dir, method, &argc, &argv); res = test(logdepth+1, argc, argv); filelist_free(&argc, &argv); free(out); } if (res) { logprintf(logdepth+1, "Works.", cmd); put("/internal/filelist/cmd", list_cmd); put("/internal/filelist/method", method); report("OK ('%s' with %s split)\n", list_cmd, method); } free(cmd); return res; } int find_filelist(const char *name, int logdepth, int fatal) { char *dir; char *old_cwd; int ret; old_cwd = strclone(db_cwd); db_cd("/host"); require("fstools/mkdir", logdepth, fatal); require("fstools/rm", logdepth, fatal); report("Checking for filelist... "); logprintf(logdepth, "find_filelist: trying to find file listing...\n"); logdepth++; dir = create_testdir(logdepth); if (dir == NULL) { report("Failed to creat sandbox\n"); ret = 1; goto end; } if ( try(logdepth, dir, "ls %s", "line") || /* should return one file name per line since the output is redirected */ try(logdepth, dir, "ls -1 %s", "line") || /* try to force one file name per line */ try(logdepth, dir, "ls --format=single-column %s", "line") || /* for gnu ls */ try(logdepth, dir, "find %s", "line") || /* if ls fails, we try find */ try(logdepth, dir, "ls %s", "word") || /* if that fails too, ls may still have a list in multiple columns */ try(logdepth, dir, "dir %s", "word") || /* or we are on windows where we need to use dir maybe */ try(logdepth, dir, "echo %s/*", "word")) { /* or on a system without ls, dir or anything alike, but shell globbing may still work */ destroy_testdir(logdepth, dir); ret = 0; goto end; } destroy_testdir(logdepth, dir); ret = 1; end:; db_cd(old_cwd); free(old_cwd); return ret; } void filelist(int logdepth, const char *dir, int *argc, char ***argv) { const char *list_cmd, *method; char *cmd, *out, *dir_esc; char *old_cwd; old_cwd = strclone(db_cwd); db_cd("/host"); /* make sure these are set to invalid for easier return in case we fail anywhere later */ *argc = -1; *argv = NULL; if (!is_dir(dir)) goto end; require("/internal/filelist/cmd", logdepth, 1); require("/internal/filelist/method", logdepth, 1); list_cmd = get("/internal/filelist/cmd"); method = get("/internal/filelist/method"); dir_esc = shell_escape_dup(dir); cmd = filelist_asmcmd(dir_esc, list_cmd); free(dir_esc); run_shell(logdepth+1, cmd, &out); if (out != NULL) { filelist_extract(out, dir, method, argc, argv); logprintf(logdepth, "filelist: Getting list of files in %s\n", dir); free(out); } free(cmd); end:; db_cd(old_cwd); free(old_cwd); } fungw-1.2.0/scconfig/src/default/str.c0000644000175100017510000000723113424222362016025 0ustar svnsvn/* scconfig - non-standard string manipulation routines Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include char *strclone(const char *str) { int l; char *ret; if (str == NULL) return NULL; l = strlen(str)+1; ret = malloc(l); memcpy(ret, str, l); return ret; } #define SPACE(c) (((c) == '\r') || ((c) == '\n') || ((c) == '\t') || ((c) == ' ')) char *trim_left(char *str) { while(SPACE(*str)) str++; return str; } char *trim_right(char *str) { char *end; end = str + strlen(str) - 1; while((end >= str) && SPACE(*end)) { *end = '\0'; end--; } return str; } char *strip(char *str) { return trim_left(trim_right(str)); } char *str_chr(char *str, char c) { char *s; for(s = str; *s != '\0'; s++) if (*s == c) return s; return NULL; } char *str_rchr(char *str, char c) { char *s, *last; last = NULL; for(s = str; *s != '\0'; s++) if (*s == c) last = s; return last; } char *str_subsn(const char *str) { char *out, *o; const char *i; if (str == NULL) return strclone(""); o = out = malloc(strlen(str)+1); for(i = str; *i != '\0'; i++, o++) { if ((i[0] == '\\') && (i[1] == 'n')) { i++; *o = '\n'; } else *o = *i; } *o = '\0'; return out; } char *str_concat(const char *sep, ...) { # define CONCAT_MAX 64 int len[CONCAT_MAX]; const char *str[CONCAT_MAX]; int n, v, sum, sl; char *out, *o; va_list ap; va_start(ap, sep); if (sep == NULL) sep = ""; /* load all strings into an array, measure their lengths */ sum = 0; for(v = 0; ;v++) { if (v >= CONCAT_MAX) { fprintf(stderr, "Internal error: str_concat got more strings than CONCAT_MAX\n"); abort(); } str[v] = va_arg(ap, const char *); if (str[v] == NULL) { len[v] = 0; break; } len[v] = strlen(str[v]); sum += len[v]; } /* first string is NULL; return a new allocation that is a simple \0, empty string to avoid a nasty corner case */ if (sum == 0) return calloc(1, 1); sl = strlen(sep); sum += (v-1) * sl + 1; /* + a sep between each two strings and a terminator at the end */ o = out = malloc(sum); for(n = 0; n < v; n++) { if ((n > 0) && (sl > 0)) { memcpy(o, sep, sl); o += sl; } if (len[n] > 0) { memcpy(o, str[n], len[n]); o += len[n]; } } *o = '\0'; va_end(ap); return out; } char *esc_interpret(const char *str) { char *out, *si, *so; out = strclone(str); /* replace (interpret) \ sequences in seq */ for(si = so = out; *si != '\0'; si++,so++) { if (si[0] == '\\') { switch(si[1]) { case 'n': *so = '\n'; break; case 't': *so = '\t'; break; case 's': *so = ' '; break; case '\\': *so = '\\'; break; } si++; } else *so = *si; } *so = '\0'; return out; } int chr_inset(char c, const char *set) { while (*set != '\0') { if (c == *set) return 1; set++; } return 0; } fungw-1.2.0/scconfig/src/default/find_libs.c0000644000175100017510000001340314003560750017144 0ustar svnsvn/* scconfig - detection of standard library features Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" static int trydlc(int logdepth, const char *test_c_dlc, const char *cflagsf, const char *ldflagsf, const char *dlc) { char *cflags, *ldflags; cflags = malloc(strlen(dlc) + 64); ldflags = malloc(strlen(dlc)*2 + 256); sprintf(cflags, cflagsf, dlc); sprintf(ldflags, ldflagsf, dlc, dlc); if (try_icl(logdepth, NULL, test_c_dlc, NULL, cflags, ldflags)) { *cflags = ' '; append("cc/cflags", cflags); put("libs/ldl", ldflags); put("libs/dl-compat", strue); report("OK (%s and %s)\n", cflags, ldflags); free(cflags); free(ldflags); return 1; } free(cflags); free(ldflags); return 0; } int find_lib_ldl(const char *name, int logdepth, int fatal) { const char *ldl, *dlc; char *s; char *test_c = NL "#include " NL "#include " NL "#ifndef RTLD_NOW" NL "#define RTLD_NOW RTLD_LAZY" /* on old BSD and probably on SunOS */ NL "#endif" NL "int main() {" NL " void *handle;" NL " handle = dlopen(\"/this file does not exist.\", RTLD_NOW);" NL " if (handle == NULL) printf(\"OK\\n\");" NL " return 0;" NL "}" NL; char *test_c_dlc = NL "#include " NL "#include " NL "int main() {" NL " void *handle;" NL " handle = dlopen(\"/this file does not exist.\", RTLD_NOW);" NL " if (handle == NULL) printf(\"OK\\n\");" NL " return 0;" NL "}" NL; s = (char *)get("libs/ldl/presents"); if (s != NULL) return !istrue(s); require("cc/cc", logdepth, fatal); report("Checking for -ldl... "); logprintf(logdepth, "find_lib_ldl: trying to find ldl...\n"); logdepth++; ldl = get("/arg/libs/ldl"); if (ldl == NULL) { dlc = get("/arg/libs/dl-compat"); if (dlc == NULL) { /* If dlc is not explicitly requested by the user, try standard dl (see whether we need -ldl for dlopen()) */ if (try_icl(logdepth, NULL, test_c, NULL, NULL, NULL)) { put("libs/ldl", ""); put("libs/ldl/includes", "#include \\n"); put("libs/ldl/presents", strue); report("OK ()\n"); return 0; } if (try_icl(logdepth, NULL, test_c, NULL, NULL, "-ldl")) { put("libs/ldl", "-ldl"); put("libs/ldl/includes", "#include \\n"); put("libs/ldl/presents", strue); report("OK (-ldl)\n"); return 0; } } /* try dl-compat (dl compatibility lib) */ if (dlc != NULL) { /* test at user supplied dlc prefix: - first assume the linker will find it - next assume gcc and pass rpath to the linker - finally try static linking */ if (trydlc(logdepth, test_c_dlc, "-I%s/include", "-L%s/lib -ldl-compat\000%s", dlc)) { put("libs/ldl/includes", "#include \\n"); return 0; } if (trydlc(logdepth, test_c_dlc, "-I%s/include", "-L%s/lib -Wl,-rpath=%s/lib -ldl-compat", dlc)) { put("libs/ldl/includes", "#include \\n"); return 0; } if (trydlc(logdepth, test_c_dlc, "-I%s/include", "%s/lib/libdl-compat.a\000%s", dlc)) { put("libs/ldl/includes", "#include \\n"); return 0; } } else if (try_icl(logdepth, NULL, test_c_dlc, NULL, NULL, "-ldl-compat")) { /* check at normal system installation */ put("libs/ldl", "-ldl-compat"); put("libs/ldl/includes", "#include \\n"); put("libs/ldl/presents", strue); report("OK (-ldl-compat)\n"); return 0; } } else { report("User provided... "); s = strclone(ldl); if (try_icl(logdepth, NULL, test_c, NULL, NULL, s)) { put("libs/ldl", ldl); put("libs/ldl/includes", "#include \\n"); put("libs/ldl/presents", strue); report("OK (%s)\n", ldl); free(s); return 0; } free(s); } put("libs/ldl/presents", sfalse); report("Not found\n"); return 1; } int find_lib_LoadLibrary(const char *name, int logdepth, int fatal) { /*char *s;*/ char *test_c = NL "#include " NL "int main() {" NL " void *handle;" NL " handle = LoadLibrary(\"/this file does not exist.\");" NL " if (handle == NULL) printf(\"OK\\n\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for LoadLibrary... "); logprintf(logdepth, "find_lib_LoadLibrary: trying to find LoadLibrary...\n"); logdepth++; if (try_icl(logdepth, "libs/LoadLibrary", test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, "libs/LoadLibrary"); } int find_lib_errno(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " errno = 0;" NL " puts(\"OK\");" NL " return 0;" NL "}" NL ; require("cc/cc", logdepth, fatal); report("Checking for errno.h... "); logprintf(logdepth, "find_lib_errno: trying to find errno...\n"); logdepth++; if (try_icl(logdepth, "libs/errno", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "libs/errno"); } fungw-1.2.0/scconfig/src/default/find.h0000644000175100017510000002275314001245115016141 0ustar svnsvn/* cc */ int find_cc(const char *name, int logdepth, int fatal); int find_cc_argstd(const char *name, int logdepth, int fatal); int find_cc_argmachine(const char *name, int logdepth, int fatal); int find_cc_fpie(const char *name, int logdepth, int fatal); int find_cc_fnopie(const char *name, int logdepth, int fatal); int find_cc_fnopic(const char *name, int logdepth, int fatal); int find_inline(const char *name, int logdepth, int fatal); int find_varargmacro(const char *name, int logdepth, int fatal); int find_funcmacro(const char *name, int logdepth, int fatal); int find_constructor(const char *name, int logdepth, int fatal); int find_destructor(const char *name, int logdepth, int fatal); int find_fattr_unused(const char *name, int logdepth, int fatal); int find_declspec_dllimport(const char *name, int logdepth, int fatal); int find_declspec_dllexport(const char *name, int logdepth, int fatal); int find_rdynamic(const char *name, int logdepth, int fatal); int find_soname(const char *name, int logdepth, int fatal); int find_so_undefined(const char *name, int logdepth, int fatal); int find_wlrpath(const char *name, int logdepth, int fatal); int find_cc_wloutimplib(const char *name, int logdepth, int fatal); int find_cc_wloutputdef(const char *name, int logdepth, int fatal); int find_fpic(const char *name, int logdepth, int fatal); int find_ldflags_dynlib(const char *name, int logdepth, int fatal); int find_ldflags_dll(const char *name, int logdepth, int fatal); int find_ldflags_so(const char *name, int logdepth, int fatal); int find_alloca(const char *name, int logdepth, int fatal); int find__exit(const char *name, int logdepth, int fatal); int find_cc_pragma_message(const char *name, int logdepth, int fatal); int find_cc_static_libgcc(const char *name, int logdepth, int fatal); /* libs */ int find_lib_ldl(const char *name, int logdepth, int fatal); int find_lib_LoadLibrary(const char *name, int logdepth, int fatal); int find_lib_errno(const char *name, int logdepth, int fatal); /* thread */ int find_lib_lpthread(const char *name, int logdepth, int fatal); int find_thread_semget(const char *name, int logdepth, int fatal); int find_thread_pthread_create(const char *name, int logdepth, int fatal); int find_thread_CreateSemaphore(const char *name, int logdepth, int fatal); int find_thread_CreateThread(const char *name, int logdepth, int fatal); /* fscalls */ int find_fs_realpath(const char *name, int logdepth, int fatal); int find_fs__fullpath(const char *name, int logdepth, int fatal); int find_fs_readdir(const char *name, int logdepth, int fatal); int find_fs_findnextfile(const char *name, int logdepth, int fatal); int find_fs_access(const char *name, int logdepth, int fatal); int find_fs_access_macros(const char *name, int logdepth, int fatal); int find_fs_stat_macros(const char *name, int logdepth, int fatal); int find_fs_stat_fields(const char *name, int logdepth, int fatal); int find_fs_lstat(const char *name, int logdepth, int fatal); int find_fs_statlstat(const char *name, int logdepth, int fatal); int find_fs_getcwd(const char *name, int logdepth, int fatal); int find_fs__getcwd(const char *name, int logdepth, int fatal); int find_fs_getwd(const char *name, int logdepth, int fatal); int find_fs_mkdir(const char *name, int logdepth, int fatal); int find_fs__mkdir(const char *name, int logdepth, int fatal); int find_fs_utime(const char *name, int logdepth, int fatal); int find_fs__utime(const char *name, int logdepth, int fatal); int find_fs__utime64(const char *name, int logdepth, int fatal); int find_fs_mkdtemp(const char *name, int logdepth, int fatal); int find_fs_mmap(const char *name, int logdepth, int fatal); int find_fsmount_next_dev(const char *name, int logdepth, int fatal); int find_fsmount_fsstat_fields(const char *name, int logdepth, int fatal); int find_fsmount_statfs_fields(const char *name, int logdepth, int fatal); int find_fsmount_statvfs_fields(const char *name, int logdepth, int fatal); int find_fs_ustat(const char *name, int logdepth, int fatal); int find_fs_statfs(const char *name, int logdepth, int fatal); int find_fs_statvfs(const char *name, int logdepth, int fatal); int find_fs_flock(const char *name, int logdepth, int fatal); int find_fs_makedev(const char *name, int logdepth, int fatal); /* printf */ int find_printf_x(const char *name, int logdepth, int fatal); int find_printf_ptrcast(const char *name, int logdepth, int fatal); int find_snprintf(const char *name, int logdepth, int fatal); int find_dprintf(const char *name, int logdepth, int fatal); int find_vdprintf(const char *name, int logdepth, int fatal); int find_vsnprintf(const char *name, int logdepth, int fatal); /* proc */ int find_proc__spawnvp(const char *name, int logdepth, int fatal); int find_proc_fork(const char *name, int logdepth, int fatal); int find_proc_wait(const char *name, int logdepth, int fatal); int find_proc__getpid(const char *name, int logdepth, int fatal); int find_proc_CreateProcessA(const char *name, int logdepth, int fatal); int find_proc_getexecname(const char *name, int logdepth, int fatal); int find_proc_GetModuleFileNameA(const char *name, int logdepth, int fatal); int find_proc_shmget(const char *name, int logdepth, int fatal); int find_proc_CreateFileMappingA(const char *name, int logdepth, int fatal); /* fstools */ int find_fstools_cp(const char *name, int logdepth, int fatal); int find_fstools_ln(const char *name, int logdepth, int fatal); int find_fstools_mv(const char *name, int logdepth, int fatal); int find_fstools_rm(const char *name, int logdepth, int fatal); int find_fstools_mkdir(const char *name, int logdepth, int fatal); int find_fstools_ar(const char *name, int logdepth, int fatal); int find_fstools_ranlib(const char *name, int logdepth, int fatal); int find_fstools_awk(const char *name, int logdepth, int fatal); int find_fstools_cat(const char *name, int logdepth, int fatal); int find_fstools_sed(const char *name, int logdepth, int fatal); int find_fstools_file(const char *name, int logdepth, int fatal); int find_fstools_file_l(const char *name, int logdepth, int fatal); int find_fstools_chmodx(const char *name, int logdepth, int fatal); /* uname */ int find_uname(const char *name, int logdepth, int fatal); int find_triplet(const char *name, int logdepth, int fatal); int find_sysid(const char *name, int logdepth, int fatal); /* find_target */ int find_target(const char *name, int logdepth, int fatal); /* filelist */ int find_filelist(const char *name, int logdepth, int fatal); /* find_str.c */ int find_strcasecmp(const char *name, int logdepth, int fatal); int find_strncasecmp(const char *name, int logdepth, int fatal); int find_stricmp(const char *name, int logdepth, int fatal); int find_strnicmp(const char *name, int logdepth, int fatal); /* find_sys.c */ int find_sys_ptrwidth(const char *name, int logdepth, int fatal); int find_sys_byte_order(const char *name, int logdepth, int fatal); int find_tmp(const char *name, int logdepth, int fatal); int find_shell(const char *name, int logdepth, int fatal); /* find_io.c */ int find_io_pipe(const char *name, int logdepth, int fatal); int find_io_pipe2(const char *name, int logdepth, int fatal); int find_io__pipe(const char *name, int logdepth, int fatal); int find_io_dup2(const char *name, int logdepth, int fatal); int find_io_fileno(const char *name, int logdepth, int fatal); int find_io_lseek(const char *name, int logdepth, int fatal); int find_io_popen(const char *name, int logdepth, int fatal); /* find_time.c */ int find_time_usleep(const char *name, int logdepth, int fatal); int find_time_Sleep(const char *name, int logdepth, int fatal); int find_time_gettimeofday(const char *name, int logdepth, int fatal); int find_time_ftime(const char *name, int logdepth, int fatal); int find_time_timegm(const char *name, int logdepth, int fatal); int find_time_mkgmtime(const char *name, int logdepth, int fatal); int find_time_gmtime_s(const char *name, int logdepth, int fatal); int find_time_gmtime_r(const char *name, int logdepth, int fatal); int find_time_localtime_s(const char *name, int logdepth, int fatal); int find_time_localtime_r(const char *name, int logdepth, int fatal); /* find_types.c */ int find_types_stdint(const char *name, int logdepth, int fatal); int find_types_sizes(const char *name, int logdepth, int fatal); int find_types_size_t(const char *name, int logdepth, int fatal); int find_types_off_t(const char *name, int logdepth, int fatal); int find_types_off64_t(const char *name, int logdepth, int fatal); int find_types_gid_t(const char *name, int logdepth, int fatal); int find_types_uid_t(const char *name, int logdepth, int fatal); int find_types_pid_t(const char *name, int logdepth, int fatal); int find_types_dev_t(const char *name, int logdepth, int fatal); int find_types_dev_t(const char *name, int logdepth, int fatal); int find_types_mode_t(const char *name, int logdepth, int fatal); int find_types_nlink_t(const char *name, int logdepth, int fatal); int find_types_ptrdiff_t(const char *name, int logdepth, int fatal); int find_types_dev_t(const char *name, int logdepth, int fatal); int find_types_ino_t(const char *name, int logdepth, int fatal); int find_types_void_ptr(const char *name, int logdepth, int fatal); /* find_signal.c */ int find_signal_names(const char *name, int logdepth, int fatal); int find_signal_raise(const char *name, int logdepth, int fatal); /* environ.c */ int find_main_arg3(const char *name, int logdepth, int fatal); int find_putenv(const char *name, int logdepth, int fatal); int find_setenv(const char *name, int logdepth, int fatal); int find_environ(const char *name, int logdepth, int fatal); fungw-1.2.0/scconfig/src/default/main_custom_args.h0000644000175100017510000000023012660127205020545 0ustar svnsvn#define MAX_CUSTOM_REQS 32 extern char *custom_reqs[MAX_CUSTOM_REQS]; extern int num_custom_reqs; int custom_arg(const char *key, const char *value); fungw-1.2.0/scconfig/src/default/db.h0000644000175100017510000000206513216413253015607 0ustar svnsvn#include "ht.h" #define strue "true" #define sfalse "false" #define istrue(s) ((s != NULL) && (*s == 't')) #define isfalse(s) ((s != NULL) && (*s == 'f')) /* the 3rd option is "unknown" */ /* accessors */ const char *get(const char *key); const char *put(const char *key, const char *data); void append(const char *key, const char *value); char *concat_nodes(const char *prefix, ...); int node_istrue(const char *key); /* init/uninit */ void db_init(void); void db_uninit(void); /* export/import */ int export(const char *fn, int export_empty, const char *root); int import(const char *fn); int import_args(const char *key, const char *fn); /* file system features */ extern char *db_cwd; void db_cd(const char *path); void db_mkdir(const char *path); void db_link(const char *existing, const char *new); void db_rmdir(const char *path); extern ht_t *DBs; #define iscross (ht_get(DBs, "target") != ht_get(DBs, "host")) #define in_cross_target (iscross && (strcmp(db_cwd, "/target") == 0)) #define in_cross_host (iscross && (strcmp(db_cwd, "/host") == 0)) fungw-1.2.0/scconfig/src/default/find_io.c0000644000175100017510000001464614003560750016634 0ustar svnsvn/* scconfig - detect I/O features of the system Copyright (C) 2010 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" int find_io_pipe(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " int fd[2];" NL " if (pipe(fd) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for pipe(2)... "); logprintf(logdepth, "find_io_pipe: trying to find pipe(2)...\n"); logdepth++; if (try_icl(logdepth, "libs/io/pipe", test_c, "#include \n", NULL, NULL)) return 0; return try_fail(logdepth, "libs/io/pipe"); } int find_io_pipe2(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "#include " NL "int main() {" NL " int fd[2];" NL " if (pipe2(fd, 0) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for pipe2(2)... "); logprintf(logdepth, "find_io_pipe2: trying to find pipe2(2)...\n"); logdepth++; if (try_icl(logdepth, "libs/io/pipe2", test_c, "#include \n", NULL, NULL)) return 0; return try_fail(logdepth, "libs/io/pipe2"); } int find_io__pipe(const char *name, int logdepth, int fatal) { const char *test_c = NL "#include " NL "int main() {" NL " int fd[2];" NL " if (_pipe(fd, 1024, _O_BINARY) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for _pipe()... "); logprintf(logdepth, "find_io__pipe: trying to find _pipe()...\n"); logdepth++; if (try_icl(logdepth, "libs/io/_pipe", test_c, "#include \n#include \n", NULL, NULL)) return 0; return try_fail(logdepth, "libs/io/_pipe"); } int find_io_dup2(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "int main() {" NL " int fd;" NL " if (dup2(1, 4) == 4)" NL " write(4, \"OK\\n\", 3); " NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for dup2(2)... "); logprintf(logdepth, "find_io_dup2: trying to find dup2(2)...\n"); logdepth++; if (try_icl(logdepth, "libs/io/dup2", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "libs/io/dup2"); } int find_io_fileno(const char *name, int logdepth, int fatal) { char test_c[256]; char *test_c_ = NL "#include " NL "int main() {" /* NOTE: can not check for implicit declaration as fileno() may be a macro (e.g. on MINIX3) */ NL " if (%s(stdout) >= 0)" NL " puts(\"OK\"); " NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for fileno(3)... "); logprintf(logdepth, "find_io_fileno: trying to find fileno(3)...\n"); logdepth++; /* UNIX */ sprintf(test_c, test_c_, "fileno"); if (try_icl(logdepth, "libs/io/fileno", test_c, NULL, NULL, NULL)) { put("libs/io/fileno/call", "fileno"); return 0; } sprintf(test_c, test_c_, "fileno"); if (try_icl(logdepth, "libs/io/fileno", test_c, "#include \n", NULL, NULL)) { put("libs/io/fileno/call", "fileno"); return 0; } sprintf(test_c, test_c_, "fileno"); if (try_icl(logdepth, "libs/io/fileno", test_c, "#define _XOPEN_SOURCE\n#include \n", NULL, NULL)) { put("libs/io/fileno/call", "fileno"); return 0; } /* windows */ sprintf(test_c, test_c_, "_fileno"); if (try_icl(logdepth, "libs/io/fileno", test_c, "#include \n", NULL, NULL)) { put("libs/io/fileno/call", "_fileno"); return 0; } return try_fail(logdepth, "libs/io/fileno"); } int find_io_lseek(const char *name, int logdepth, int fatal) { #define NODE "libs/io/lseek" char test_c[3256]; const char *test_c_template = NL "#include " NL "#include " NL "int main() {" NL " const char *filename = \"%s\";" NL no_implicit(int, "%s", "%s") NL " int fd = open(filename, O_WRONLY);" NL " if (write(fd, \"abc\", 3) == 3 && %s(fd, 1, SEEK_SET) == 1)" NL " puts(\"OK\"); " NL " return 0;" NL "}" NL; char *tmpf; const char *incs[] = {"#include ","#include ",NULL}; const char *fns[] = {"lseek", "_lseek", NULL}; const char **inc; const char **fn; require("cc/cc", logdepth, fatal); report("Checking for lseek(2)... "); logprintf(logdepth, "find_io_lseek: trying to find lseek(2)...\n"); logdepth++; tmpf = tempfile_new(".psx"); for (inc = incs, fn = fns; *fn; ++inc, ++fn) { sprintf(test_c, test_c_template, tmpf, *fn, *fn, *fn); if (try_icl(logdepth, NODE, test_c, *inc, NULL, NULL)) { unlink(tmpf); free(tmpf); put(NODE "/call", *fn); return 0; } } unlink(tmpf); free(tmpf); return try_fail(logdepth, NODE); #undef NODE } int find_io_popen(const char *name, int logdepth, int fatal) { const char **i, *incs[] = {"#define _XOPEN_SOURCE", "#define _BSD_SOURCE", "#define _POSIX_C_SOURCE 2", NULL}; char *test_c = NL "#include " NL "#include " NL "int main() {" NL " FILE *f = popen(\"echo OK\", \"r\");" NL " char line[16];" NL " if (f == NULL) return 0;" NL " if (fgets(line, sizeof(line)-1, f) == NULL) return 0;" NL " puts(line);" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for popen(3)... "); logprintf(logdepth, "find_io_popen: trying to find popen(3)...\n"); logdepth++; for(i = incs; *i != NULL; i++) if (try_icl(logdepth, "libs/io/popen", test_c, *i, NULL, NULL)) return 0; return try_fail(logdepth, "libs/io/popen"); } fungw-1.2.0/scconfig/src/default/ht.c0000644000175100017510000001335012750535606015640 0ustar svnsvn/* scconfig - hash tables Copyright (C) 2007, 2008, 2009 by Szabolcs Nagy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include #include #include "libs.h" #include "ht.h" #define HT_MINSIZE 8 #define HT_MAXSIZE ((UINT_MAX >> 1U) + 1U) #define JUMP(i, j) i += j++ #define JUMP_FIRST(i, j) j = 1, i += j++ /* portable str hash */ #define HASH_INIT 0xbabeface static unsigned int keyhash(const char *key) { unsigned int a = HASH_INIT; while (*key) a += (a << 5) + *key++; return a; } /* fill threshold = 3/4 */ #define HT_LOG_THRES 2 static void checkfill(ht_t *ht) { if (ht->fill > ht->mask - (ht->mask >> HT_LOG_THRES) || ht->fill > ht->used << 2) ht_resize(ht, ht->used << (ht->used > 1U << 15 ? 1 : 2)); } static ht_t *ht_init(ht_t *ht, int isstr) { ht->mask = HT_MINSIZE - 1; ht->fill = 0; ht->used = 0; ht->isstr = isstr; ht->table = calloc(ht->mask + 1, sizeof(ht_entry_t)); ht->refcount = 1; return ht; } static ht_t *ht_uninit(ht_t *ht) { ht_entry_t *entry; for (entry = ht->table; entry != ht->table + ht->mask + 1; entry++) if (ht_isused(entry)) { if (ht->isstr) free(entry->value); free(entry->key); } free(ht->table); return ht; } ht_t *ht_alloc(int isstr) { ht_t *ht; ht = malloc(sizeof(ht_t)); return ht_init(ht, isstr); } void ht_free(ht_t *ht) { ht_uninit(ht); free(ht); } ht_t *ht_clear(ht_t *ht) { ht_uninit(ht); return ht_init(ht, ht->isstr); } /* one lookup function to rule them all */ static ht_entry_t *ht_lookup(ht_t *ht, const char *key, unsigned int hash) { unsigned int mask; unsigned int i; unsigned int j; ht_entry_t *table; ht_entry_t *entry; ht_entry_t *free_entry; mask = ht->mask; i = hash; table = ht->table; assert(ht->table); entry = table + (i & mask); if (ht_isempty(entry) || !strcmp(entry->key, key)) return entry; /* free_entry: first deleted entry for insert */ free_entry = ht_isdeleted(entry) ? entry : NULL; assert(ht->fill <= ht->mask); for (JUMP_FIRST(i, j); ; JUMP(i, j)) { entry = table + (i & mask); if (ht_isempty(entry)) return (free_entry == NULL) ? entry : free_entry; if (entry->hash == hash && !strcmp(entry->key, key)) return entry; if (ht_isdeleted(entry) && free_entry == NULL) free_entry = entry; } } /* for resize: no deleted entries in ht, entry->key is not in ht, no strdup */ static void cleaninsert(ht_t *ht, const ht_entry_t *entry) { unsigned int i; unsigned int j; ht_entry_t *newentry; i = entry->hash; newentry = ht->table + (i & ht->mask); if (!ht_isempty(newentry)) for (JUMP_FIRST(i, j); !ht_isempty(newentry); JUMP(i, j)) newentry = ht->table + (i & ht->mask); ++ht->fill; ++ht->used; memcpy(newentry, entry, sizeof(ht_entry_t)); } ht_t *ht_resize(ht_t *ht, unsigned int hint) { unsigned int newsize; unsigned int oldused; ht_entry_t *oldtable, *newtable, *entry; oldused = ht->used; if (hint < oldused << 1) hint = oldused << 1; assert(hint <= HT_MAXSIZE && hint > oldused); for (newsize = HT_MINSIZE; newsize < hint; newsize <<= 1); newtable = calloc(newsize, sizeof(ht_entry_t)); oldtable = ht->table; ht->mask = newsize - 1; ht->fill = 0; ht->used = 0; ht->table = newtable; for (entry = oldtable; oldused > 0; ++entry) if (ht_isused(entry)) { --oldused; cleaninsert(ht, entry); } free(oldtable); return ht; } void *ht_get(ht_t *ht, const char *key) { ht_entry_t *entry; entry = ht_lookup(ht, key, keyhash(key)); return ht_isused(entry) ? entry->value : NULL; } void *ht_insert(ht_t *ht, const char *key, void *value) { unsigned int hash; ht_entry_t *entry; hash = keyhash(key); entry = ht_lookup(ht, key, hash); if (ht_isused(entry)) return entry->value; if (ht_isempty(entry)) ++ht->fill; ++ht->used; entry->hash = hash; entry->key = strclone(key); entry->value = ht->isstr ? strclone(value) : value; checkfill(ht); return NULL; } const char *ht_set(ht_t *ht, const char *key, void *value) { unsigned int hash; ht_entry_t *entry; char *k; hash = keyhash(key); entry = ht_lookup(ht, key, hash); if (ht_isused(entry)) { if (ht->isstr) { free(entry->value); entry->value = strclone(value); } else entry->value = value; return entry->key; } if (ht_isempty(entry)) ++ht->fill; ++ht->used; entry->hash = hash; entry->key = k = strclone(key); entry->value = ht->isstr ? strclone(value) : value; checkfill(ht); return k; } const char *ht_del(ht_t *ht, const char *key) { ht_entry_t *entry; entry = ht_lookup(ht, key, keyhash(key)); if (!ht_isused(entry)) return NULL; --ht->used; free(entry->key); if (ht->isstr) free(entry->value); entry->key = ht_deleted_key; return ht_deleted_key; } ht_entry_t *ht_first(const ht_t *ht) { ht_entry_t *entry = 0; if (ht->used) for (entry = ht->table; !ht_isused(entry); ++entry); return entry; } ht_entry_t *ht_next(const ht_t *ht, ht_entry_t *entry) { while (++entry != ht->table + ht->mask + 1) if (ht_isused(entry)) return entry; return 0; } fungw-1.2.0/scconfig/src/default/lib_try.c0000644000175100017510000003007514045260141016660 0ustar svnsvn#include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" /* Returns true if the first 2 characters of the output is OK */ static int try_icl_accept_ok(char *stdout_str) { return (strncmp(stdout_str, "OK", 2) == 0); } #define is_ctrl_prefix(ch) (((ch) == '!') || ((ch) == '^')) static int try_icl__(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags, const char *db_includes, const char *db_cflags, const char *db_ldflags, int run, int (*accept_res)(char *stdout_str)) { char *out = NULL; char *tmp, *inc; const char *test_c; char c[1024]; int l, compres; if (includes != NULL) { l = strlen(includes); memcpy(c, includes, l); c[l] = '\n'; l++; strcpy(c+l, test_c_in); test_c = c; } else test_c = test_c_in; logprintf(logdepth, "trying '%s' and '%s' and '%s', %s\n", str_null(db_includes), str_null(db_cflags), str_null(db_ldflags), run ? "with a run" : "with no run"); if (run) compres = compile_run(logdepth+1, test_c, NULL, cflags, ldflags, &out); else { char *fn_output = NULL; compres = compile_code(logdepth+1, test_c, &fn_output, NULL, cflags, ldflags); if (fn_output != NULL) { unlink(fn_output); free(fn_output); } } if (compres == 0) { if (!run || target_emu_fail(out) || accept_res(out)) { free(out); /* no prefix: don't modify the database, the caller will do that */ if (prefix == NULL) return 1; tmp = malloc(strlen(prefix) + 32); if ((db_includes == NULL) || (*db_includes == '\0')) inc = strclone(""); else inc = uniq_inc_str(db_includes, NULL, "\\n", 0, 0, NULL); sprintf(tmp, "%s/includes", prefix); put(tmp, inc); if (db_cflags == NULL) db_cflags = ""; sprintf(tmp, "%s/cflags", prefix); put(tmp, db_cflags); if (db_ldflags == NULL) db_ldflags = ""; sprintf(tmp, "%s/ldflags", prefix); put(tmp, db_ldflags); if (inc != NULL) { report("OK ('%s', '%s' and '%s')\n", str_null(inc), str_null(db_cflags), str_null(db_ldflags)); free(inc); } else report("OK ('%s' and '%s')\n", str_null(db_cflags), str_null(db_ldflags)); sprintf(tmp, "%s/presents", prefix); put(tmp, strue); free(tmp); return 1; } free(out); } return 0; } #define LOAD(node) \ do { \ if (u ## node != NULL) break; \ strcpy(apath_end, #node); \ u ## node = get(apath); \ } while(0) #define SET(dst, src, is_flag) \ do { \ char *__sep__ = is_flag ? " " : "\n"; \ const char *__dst__ = dst == NULL ? "" : dst; \ char *__out__; \ if (is_ctrl_prefix(*__dst__)) __dst__++; \ if (*src == '!') \ __out__ = strclone(src+1); \ else if (*src == '^') {\ if (src[1] != '\0') \ __out__ = str_concat("", src+1, __sep__, __dst__, NULL); \ else \ __out__ = strclone(__dst__); \ } \ else { \ if (*__dst__ != '\0') \ __out__ = str_concat("", __dst__, __sep__, src, NULL); \ else \ __out__ = strclone(src); \ } \ free(dst); \ dst = __out__; \ if (is_flag) { \ char *__s__; \ for(__s__ = dst; *__s__ != '\0'; __s__++) \ if ((*__s__ == '\n') || (*__s__ == '\r')) \ *__s__ = ' '; \ } \ } while(0) /* Figure user overrides and call try_icl__() accordingly */ int try_icl_(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags, int run, int (*accept_res)(char *stdout_str)) { char apath[1024], *apath_end; int l, res; const char *uincludes = NULL, *ucflags = NULL, *uldflags = NULL, *uprefix = NULL; /* user specified */ char *rincludes, *rcflags, *rldflags; /* real */ char *dbincludes = NULL, *dbcflags = NULL, *dbldflags = NULL; /* what to add in the db at the end */ if ((prefix == NULL) ? 0 : strlen(prefix) + strlen(db_cwd) > sizeof(apath)-32) { report("ERROR: no room for try_icl_() - prefix is probably too long.\n"); return -1; } /* load uincludes, uclfags, uldflags and uprefix - LOAD() inserts the u */ if (prefix != NULL) { /* prefix == NULL means non-standard icl; caller sets non-standard nodes, the user can't affect that */ l = sprintf(apath, "/arg/icl/%s/", prefix); apath_end = apath+l; LOAD(includes); LOAD(cflags); LOAD(ldflags); LOAD(prefix); l = sprintf(apath, "/arg/icl/%s/%s/", db_cwd, prefix); apath_end = apath+l; LOAD(includes); LOAD(cflags); LOAD(ldflags); LOAD(prefix); } /* special case: all three specified by the user - ignore what the detector wanted, but run only once per node prefix */ if ((uincludes != NULL) && (ucflags != NULL) && (uldflags != NULL)) { const char *am; sprintf(apath, "%s/icl/all_manual_result", prefix); am = get(apath); if (am != NULL) return istrue(am); /* return cached result if available */ res = try_icl__(logdepth, prefix, test_c_in, uincludes, ucflags, uldflags, uincludes, ucflags, uldflags, run, accept_res); put(apath, res ? strue : sfalse); return res; } /* TODO: get default cflags here */ rincludes = NULL; rcflags = strclone(get("cc/cflags")); rldflags = strclone(get("cc/ldflags")); /* override base/default values with detection requested ones */ if (includes != NULL) SET(rincludes, includes, 0); if (cflags != NULL) SET(rcflags, cflags, 1); if (ldflags != NULL) SET(rldflags, ldflags, 1); if (includes != NULL) SET(dbincludes, includes, 0); if (cflags != NULL) SET(dbcflags, cflags, 1); if (ldflags != NULL) SET(dbldflags, ldflags, 1); /* override detection with user specified ICL values */ if (uincludes != NULL) SET(rincludes, uincludes, 0); if (ucflags != NULL) SET(rcflags, ucflags, 1); if (uldflags != NULL) SET(rldflags, uldflags, 1); if (uincludes != NULL) SET(dbincludes, uincludes, 0); if (ucflags != NULL) SET(dbcflags, ucflags, 1); if (uldflags != NULL) SET(dbldflags, uldflags, 1); /* insert prefix as needed */ if (uprefix != NULL) { char *old, *prfx; old = rcflags; if ((rcflags != NULL) && (*rcflags == '^')) { rcflags++; prfx = "^"; } else prfx = ""; rcflags = str_concat("", prfx, "-I", uprefix, "/include ", rcflags, NULL); if (old != cflags) free(old); /* add -I to the db too */ old = dbcflags; dbcflags = str_concat("", "-I", uprefix, "/include ", dbcflags, NULL); free(old); old = rldflags; if ((rldflags != NULL) && (*rldflags == '^')) { rldflags++; prfx = "^"; } else prfx = ""; rldflags = str_concat("", prfx, "-L", uprefix, "/lib ", rldflags, NULL); if (old != ldflags) free(old); /* add -L to the db too */ old = dbldflags; dbldflags = str_concat("", "-L", uprefix, "/lib ", dbldflags, NULL); free(old); } res = try_icl__(logdepth, prefix, test_c_in, rincludes, rcflags, rldflags, dbincludes, dbcflags, dbldflags, run, accept_res); /* if we had to alloc, free here */ free(rincludes); free(rcflags); free(rldflags); free(dbincludes); free(dbcflags); free(dbldflags); return res; } #undef LOAD #undef SET int try_icl(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags) { return try_icl_(logdepth, prefix, test_c_in, includes, cflags, ldflags, 1, try_icl_accept_ok); } int try_icl_with_deps(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags, const char *dep_includes, const char *dep_cflags, const char *dep_ldflags, int run) { int res; if ((dep_includes != NULL) && (*dep_includes == '\0')) dep_includes = NULL; if ((dep_cflags != NULL) && (*dep_cflags == '\0')) dep_cflags = NULL; if ((dep_ldflags != NULL) && (*dep_ldflags == '\0')) dep_ldflags = NULL; if (dep_includes != NULL) includes = str_concat(" ", dep_includes, includes, NULL); if (dep_cflags != NULL) cflags = str_concat(" ", dep_cflags, cflags, NULL); if (dep_ldflags != NULL) ldflags = str_concat(" ", dep_ldflags, ldflags, NULL); res = try_icl_(logdepth, prefix, test_c_in, includes, cflags, ldflags, run, try_icl_accept_ok); if (dep_includes != NULL) free((char *)includes); if (dep_cflags != NULL) free((char *)cflags); if (dep_ldflags != NULL) free((char *)ldflags); return res; } int try_icl_norun(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags) { return try_icl_(logdepth, prefix, test_c_in, includes, cflags, ldflags, 0, try_icl_accept_ok); } int try_fail(int logdepth, const char *prefix) { char *tmp; tmp = malloc(strlen(prefix) + 32); sprintf(tmp, "%s/presents", prefix); put(tmp, sfalse); free(tmp); report("not found\n"); logprintf(logdepth, "NOT FOUND."); return 1; } static int try_pkg_config_(int logdepth, char *pkgname, const char *prefix, const char *test_c) { char *cflags, *ldflags; int res; logprintf(logdepth, "Trying pkg-config %s\n", pkgname); if (run_pkg_config(logdepth+1, pkgname, &cflags, &ldflags) == 0) res = try_icl(logdepth+1, prefix, test_c, NULL, cflags, ldflags); else res = 0; free(cflags); free(ldflags); return res; } int try_icl_pkg_config(int logdepth, const char *prefix, const char *test_c, char *includes, const char *pkgpat, const char *reqver) { char **pkg_ver, **s; int num_pkg_ver; int res = 0; (void) includes; /* not used */ run_pkg_config_lst(logdepth, pkgpat, &num_pkg_ver, &pkg_ver); if (pkg_ver == NULL) return 0; if (reqver != NULL) { /* search the list for the preferred version */ for(s = pkg_ver; *s != NULL; s+=2) { if (strcmp(s[1], reqver) == 0) { if (try_pkg_config_(logdepth, s[0], prefix, test_c)) { res = 1; report("Found version required (%s) using pkg_config.\n", reqver); goto out; } else { report("The version required (%s) is found (via pkg_config) but does not work\n", reqver); goto out; } } } goto out; } for(s = pkg_ver; *s != NULL; s+=2) { if (try_pkg_config_(logdepth, s[0], prefix, test_c)) { res = 1; goto out; } } out:; filelist_free(&num_pkg_ver, &pkg_ver); return res; } int import_icl(const char *key, const char *fn) { char path[1024]; if (strlen(key) > sizeof(path)-32) { report("ERROR: no room for import_icl() - key is probably too long.\n"); return -1; } switch(*key) { case 'l': sprintf(path, "/arg/icl/%s/ldflags", key+8); break; case 'c': sprintf(path, "/arg/icl/%s/cflags", key+7); break; case 'i': sprintf(path, "/arg/icl/%s/includes", key+9); break; case 'p': sprintf(path, "/arg/icl/%s/prefix", key+7); break; default: return 1; } return put(path, fn) == NULL; } static long field_accept_len; static int field_accept_res(char *stdout_str) { char *end; field_accept_len = strtol(stdout_str, &end, 10); if (((*end == '\0') || (*end == '\r') || (*end == '\n')) && (field_accept_len > 0)) return 1; return 0; } int try_icl_sfield(int logdepth, const char *prefix, const char *structn, const char *fieldn, const char *includes, const char *cflags, const char *ldflags) { int res; char test_c[512]; char ls[16]; const char *test_c_in = NL "#include " NL "int main()" NL "{" NL " %s s;" NL " printf(\"%%ld\\n\", (long)sizeof(s.%s));" NL "}" NL; if (strlen(fieldn) + strlen(structn) + strlen(test_c_in) + 32 >= sizeof(test_c)) { report("ERROR: no room for try_icl_sfield() - struct or field name is probably too long.\n"); return -1; } sprintf(test_c, test_c_in, structn, fieldn); res = try_icl_(logdepth, prefix, test_c, includes, cflags, ldflags, 1, field_accept_res); if (res) { sprintf(test_c, "%s/sizeof", prefix); sprintf(ls, "%ld", field_accept_len); put(test_c, ls); } return res; } int try_icl_sfields(int logdepth, const char *prefix, const char *structn, const char **fields, const char *includes, const char *cflags, const char *ldflags, int silent_exit_first_fail) { int succ = 0, first = 1; require("cc/cc", logdepth, 1); for(; *fields != NULL; fields++) { report("Checking for %s.%s... ", structn, *fields); logprintf(logdepth, "%s: checking for field %s...\n", structn, *fields); logdepth++; if (try_icl_sfield(logdepth, prefix, structn, *fields, includes, cflags, ldflags)) { succ = 1; } else if ((silent_exit_first_fail) && (first)) { return 1; } logdepth--; first = 0; } if (!succ) try_fail(logdepth, "libs/fsmount/next_dev"); return 0; } fungw-1.2.0/scconfig/src/default/main_lib.c0000644000175100017510000000730113174300143016761 0ustar svnsvn/* scconfig - helpers for a main() Copyright (C) 2009..2016 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "db.h" #include "find.h" #include "log.h" #include "arg.h" #include "dep.h" #include "deps_default.h" #include "libs.h" #include "hooks.h" #include "regex.h" #include "main_custom_args.h" #ifdef PLUGIN_SCRIPTS #include "../scripts/INIT.h" #endif #ifdef PLUGIN_PARSER #include "../parser/INIT.h" #endif #ifdef PLUGIN_C99 #include "../c99/INIT.h" #endif #ifdef PLUGIN_PARSGEN #include "../parsgen/INIT.h" #endif #ifdef PLUGIN_MATH #include "../math/INIT.h" #endif #ifdef PLUGIN_SOCKET #include "../socket/INIT.h" #endif #ifdef PLUGIN_USERPASS #include "../userpass/INIT.h" #endif #ifdef PLUGIN_GUI #include "../gui/INIT.h" #endif #ifdef PLUGIN_TTY #include "../tty/INIT.h" #endif #ifdef PLUGIN_SUL #include "../sul/INIT.h" #endif #ifdef PLUGIN_POSIX #include "../posix/INIT.h" #endif #ifdef PLUGIN_GENERATOR #include "generator.h" #endif void init(void) { db_init(); log_init(); dep_init(); deps_default_init(); /* common internal directory */ db_mkdir("/internal"); /* We have a host system for sure - also make it the default */ db_mkdir("/host"); db_cd("/host"); /* emulator for the host system is empty string */ put("sys/emu", ""); #ifdef PLUGIN_SCRIPTS #include "../scripts/INIT.c" #endif #ifdef PLUGIN_PARSER #include "../parser/INIT.c" #endif #ifdef PLUGIN_C99 #include "../c99/INIT.c" #endif #ifdef PLUGIN_PARSGEN #include "../parsgen/INIT.c" #endif #ifdef PLUGIN_MATH #include "../math/INIT.c" #endif #ifdef PLUGIN_SOCKET #include "../socket/INIT.c" #endif #ifdef PLUGIN_USERPASS #include "../userpass/INIT.c" #endif #ifdef PLUGIN_GUI #include "../gui/INIT.c" #endif #ifdef PLUGIN_TTY #include "../tty/INIT.c" #endif #ifdef PLUGIN_SUL #include "../sul/INIT.c" #endif #ifdef PLUGIN_POSIX #include "../posix/INIT.c" #endif #ifdef PLUGIN_GENERATOR #include "../generator/INIT.c" #endif } void uninit(void) { log_uninit(); dep_uninit(); db_uninit(); } void run_custom_reqs(void) { int n; if (num_custom_reqs > 0) { printf("Running custom requirements\n"); for(n = 0; n < num_custom_reqs; n++) { if (custom_reqs[n] == NULL) { fprintf(stderr, "Error: requested detection of empty string - please check your command line, syntax is --detect=node\n"); exit(1); } require(custom_reqs[n], 1, 1); } } } int main_init(void) { re_modw("./\\"); if (hook_preinit()) { fprintf(stderr, "hook_preinit failed, exiting\n"); return 1; } init(); if (hook_postinit()) { fprintf(stderr, "hook_postinit failed, exiting\n"); return 1; } return 0; } int main_process_args(int argc, char *argv[]) { process_args(argc, argv); if (hook_postarg()) { fprintf(stderr, "hook_postarg failed, exiting\n"); return 1; } return 0; } void main_uninit(void) { hook_preuninit(); uninit(); hook_postuninit(); } fungw-1.2.0/scconfig/src/default/libs.h0000644000175100017510000002074313370510646016163 0ustar svnsvn#define NL "\n" /* main.c */ extern int no_autodetect_sys; /* set this to 1 to suppress system and cross detection */ extern int no_save_cache; /* set this to 1 to avoid saving config.cache */ /* lib_try.c: try to compile and run a test code; save results under prefix, if worked */ /* include, compile-flags, link-flags; NULL includes, cflags, *ldflags means don't put anything in the db; cflags and ldflags may be prefixed with "+" to include standard flags; the test code has to print "OK" if it worked. If prefix is NULL, do not modify the db or announce the output, silently return 0 or 1. Returns 1 if worked, 0 if not */ int try_icl(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags); /* same as try_icl(), but does not execute the code, only compiles. Useful for test programs with undesirable side effects (e.g. gtk: would open a window) */ int try_icl_norun(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags); /* same as try_icl, but also insert flags picked up from deps (if not NULL); useful for detecting features that depend on other detected features. If run is 0, do not run the test program, compile only */ int try_icl_with_deps(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags, const char *dep_includes, const char *dep_cflags, const char *dep_ldflags, int run); /* Low level function for the same, giving more control to the caller */ int try_icl_(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags, int run, int (*accept_res)(char *stdout_str)); /* use try_icl() on a list of packages found by pkg-config. Stick to the version required if reqver is non-NULL, else try them in the order pkg-config returned them. */ int try_icl_pkg_config(int logdepth, const char *prefix, const char *test_c, char *includes, const char *pkgpat, const char *reqver); /* call this when failed to find the feature (after multiple try_*() calls); always returns 1 (so that return try_fail() does the Right Thing) */ int try_fail(int logdepth, const char *prefix); /* Import an argument for controlling try_icl() */ int import_icl(const char *key, const char *fn); /* Determine the sizeof() of a struct field; works the same way as icl() but also sets prefix/sizeof */ int try_icl_sfield(int logdepth, const char *prefix, const char *structn, const char *fieldn, const char *includes, const char *cflags, const char *ldflags); int try_icl_sfields(int logdepth, const char *prefix, const char *structn, const char **fields, const char *includes, const char *cflags, const char *ldflags, int silent_exit_first_fail); /* lib_compile.c */ extern int cross_blind; /* 1 if crosscompiling is blind (no emulator to test with) */ char *shell_escape_dup(const char *in); /* strdup in and escape any special char for the shell */ int compile_file(int logdepth, const char *fn_input, char **fn_output, const char *cc, const char *cflags, const char *ldflags); int compile_code(int logdepth, const char *testcode, char **fn_output, const char *cc, const char *cflags, const char *ldflags); /* same as above, but do not add cc/cflags and cc/ldfags */ int compile_file_raw(int logdepth, const char *fn_input, char **fn_output, const char *cc, const char *cflags, const char *ldflags); int compile_code_raw(int logdepth, const char *testcode, char **fn_output, const char *cc, const char *cflags, const char *ldflags); int run(int logdepth, const char *cmd_, char **stdout_saved); int run_shell(int logdepth, const char *cmd_, char **stdout_saved); int compile_run(int logdepth, const char *testcode, const char *cc, const char *cflags, const char *ldflags, char **stdout_saved); int run_script(int logdepth, const char *interpreter, const char *script, const char *suffix, char **out); /* lib_file.c */ int file_size(const char *name); char *tempdir_new(int logdepth, const char *suffix); char *tempfile_new(const char *suffix); char *tempfile_dump(const char *testcode, const char *suffix); char *load_file(const char *name); int is_dir(const char *path); int is_file(const char *path); int exists(const char *path); int exists_in(const char *dir, const char *file); char *file_name(const char *path); /* returns malloc'd buffer */ char *dir_name(const char *path); /* returns malloc'd buffer */ char *tempfile_new_noabort(const char *suffix); /* for internal use - returns NULL instead of aborting when temp file can not be created */ int touch_file(const char *path); /* lib_filelist.c */ void filelist(int logdepth, const char *dir, int *argc, char ***argv); void filelist_free(int *argc, char ***argv); /* lib_pkg_config.c */ /** run pkg config on @pkgname: - with `--cflags` if cflags is not NULL, storing the result in cflags (malloc()'d) - with `--libs` if ldflags is not NULL, storing the result in ldflags (malloc()'d) Returns 0 on success. */ int run_pkg_config(int logdepth, const char *pkgname, char **cflags, char **ldflags); /** same as run_pkg_config(), but runs a generic config tool (e.g. gdconfig) passed in confname */ int run_gen_config(int logdepth, const char *confname, const char *pkgname, char **cflags, char **ldflags); int run_pkg_config_modversion(int logdepth, const char *pkgname, char **modversion); int run_pkg_config_modversion_db(int logdepth, const char *node, const char *pkgname); /** run pkg-config --list-all and keep lines matching regex pkgpat. argc/argv is a filelist output, each item pair is package name returned by pkg_config (odd items are full package names, even items are suffixes: pkgpath match removed) */ void run_pkg_config_lst(int logdepth, const char *pkgpat, int *argc, char ***argv); /* lib_uniqinc.c */ char **uniq_inc_arr(const char *includes, int indirect, const char *sep, int *numlines); /* split includes by sep; includes is a list of nodes to get() if indirect is non-zero; return a NULL-terminated array of unique include strings and set *numlines if numlines is not NULL */ void uniq_inc_free(char **arr); /* free an array returned by uniq_inc_arr() */ char *uniq_inc_str(const char *includes, const char *isep, const char *osep, int sort, int eren, char **eres); /* take a long list of includes separated by isep and emit an uniq list separated by osep */ char *order_inc_str(const char *includes, const char *isep, const char *word1, int dir, const char *word2); /* take a long list of includes separated by isep and emit a new list where word1 is moved before/after of word2 if dir < 0 or dir > 0 */ /* find_types.c */ int find_types_something_t(const char *name, int logdepth, int fatal, const char* prefix, const char *typ, const char* define, const char *try_include); /* str.c */ char *strclone(const char *str); char *trim_left(char *str); char *trim_right(char *str); char *strip(char *str); char *str_chr(char *str, char c); char *str_rchr(char *str, char c); char *str_subsn(const char *str); /* advanced strdup that also interprets \n */ char *str_concat(const char *sep, ...); /* concat a list of strings into a newly allocated buffer, putting sep between them */ char *esc_interpret(const char *str); int chr_inset(char c, const char *set); /* returns whether c is in set */ /* srctree.c */ /* Run svn info on dir and extract the value for key; key is case sensitive. The first match is returned or NULL if not found or on error. */ char *svn_info(int logdepth, const char *dir, const char *key); #define isblind(root) ((strncmp((root), "/target", 7) == 0) && cross_blind) #define istarget(root) (strncmp((root), "/target", 7) == 0) #define target_emu_fail(out) ((isblind(db_cwd)) && (out == NULL)) #define safeNULL(s) ((s) == NULL ? "(NULL)" : (s)) #define str_null(s) ((s) == NULL ? "" : (s)) /* Test program helper: generate code that ensures a given FUNCT exists and is a function; can be turned off by defining SCCONFIG_ACCEPT_IMPLICIT on scconfig compilation time */ /* Both FUNCT1 and FUNCT2 argument *must* be used exactly once! In some cases FUNCT1 and FUNCT2 is a format string parameter. We expect, however, both arguments will substituted to the same value. */ #ifdef SCCONFIG_ACCEPT_IMPLICIT # define no_implicit(RET_TYPE, FUNCT1, FUNCT2) \ "/* accept implicit (" FUNCT1 ", " FUNCT2 ") */\n" #else # define no_implicit(RET_TYPE, FUNCT1, FUNCT2) \ "#ifndef " FUNCT1 "\n" \ "{ " #RET_TYPE " (*tmp)() = " FUNCT2 "; if (tmp) {}}\n" \ "#endif\n" #endif fungw-1.2.0/scconfig/src/default/find_signal.c0000644000175100017510000000736113371256503017503 0ustar svnsvn/* scconfig - detection of standard library features Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" static int try_bad(int logdepth, const char *test_c, char *cflags, char *ldflags) { char *out = NULL; logprintf(logdepth, "trying signal (neg) with ldflags '%s'\n", ldflags == NULL ? get("cc/ldflags") : ldflags); if (compile_run(logdepth+1, test_c, NULL, cflags, ldflags, &out) == 0) { if (target_emu_fail(out) || (strncmp(out, "BAD", 3) == 0)) { free(out); return 0; } free(out); } return 1; } int find_signal_raise(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main(int argc, char *argv[]) {" NL " printf(\"OK\\n\");" NL " if (argc == 16)" NL " raise(1);" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for raise()... "); logprintf(logdepth, "find_signal_raise: trying to find raise()...\n"); logdepth++; if (try_icl(logdepth, "signal/raise", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "signal/raise"); } int find_signal_names(const char *rname, int logdepth, int fatal) { char *test_c_exists = NL "#include " NL "#include " NL "int main(int argc, char *argv[]) {" NL " printf(\"OK\\n\");" NL " if (argc == 16)" NL " raise(%s);" NL " return 0;" NL "}" NL; char *test_c_terms = NL "#include " NL "#include " NL "int main() {" NL " raise(%s);" NL " printf(\"BAD\\n\");" NL " return 0;" NL "}" NL; char test_c[256]; const char *names[] = {"SIGINT", "SIGABRT", "SIGKILL", "SIGTERM", "SIGQUIT", "SIGHUP", "SIGFPE", "SIGSEGV", "SIGPIPE", NULL}; const char **name; char path[256], *pathend; const char *prefix = "signal/names/"; require("cc/cc", logdepth, fatal); require("signal/raise/*", logdepth, fatal); strcpy(path, prefix); pathend = path + strlen(prefix); for(name = names; *name != NULL; name++) { /* check whether it exists */ report("Checking whether %s exists... ", *name); logprintf(logdepth, "find_signal_names: checking whether %s exists\n", *name); logdepth++; sprintf(test_c, test_c_exists, *name); strcpy(pathend, *name); if (!try_icl(logdepth, path, test_c, NULL, NULL, NULL)) { logdepth--; continue; } /* check whether it exists */ logdepth--; report("Checking whether %s terminates... ", *name); logprintf(logdepth, "find_signal_names: checking whether %s terminates\n", *name); logdepth++; sprintf(test_c, test_c_terms, *name); sprintf(pathend, "%s/terminates", *name); if (try_bad(logdepth, test_c, NULL, "")) { put(path, strue); report("terminates\n"); } else { report("does not terminate\n"); put(path, sfalse); } logdepth--; } /* to avoid redetection */ put("signal/names/presents", strue); return 0; } fungw-1.2.0/scconfig/src/default/dep.h0000644000175100017510000000154613272247000015772 0ustar svnsvn#include "ht.h" int is_dep_known(const char *name); int require(const char *name, int logdepth, int fatal); const char *dep_add(const char *name, int (*finder)(const char *name, int logdepth, int fatal)); void require_all(int fatal); /* Returns if dependency is a wildcard one (ending in / *) */ int is_dep_wild(const char *path); /* Almost 'basename': returns the last portion of the path, which may be '*'. Returns "" on error. */ const char *det_list_target(const char *path); /* Returns 1 if the user asked for detecting a feature; needtodo is the first argument passed to the detection function (the target the caller wants to get detected), cando is the output path of the test that the detector could do next. */ int asked_for(const char *cando, const char *needtodo); /* for internal use */ void dep_init(void); void dep_uninit(void); fungw-1.2.0/scconfig/src/default/lib_uniqinc.c0000644000175100017510000001670013216026724017515 0ustar svnsvn/* scconfig - library for making includes on a list unique Copyright (C) 2012, 2017 Tibor Palinkas Copyright (C) 2017 Aron Barath This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include "libs.h" #include "db.h" #include "regex.h" #define grow \ if (used >= alloced) { \ alloced += 16; \ list = realloc(list, alloced * sizeof(char *)); \ } char **uniq_inc_arr(const char *includes, int indirect, const char *sep_, int *numlines) { char *node, *next, *cw, *nw, *snode, *orig_node; char *sep; char **list; int alloced, used, n; orig_node = strclone(includes); node = orig_node; if (sep_ == NULL) sep = strclone("\r\n"); else sep = strclone(sep_); /* reset list */ alloced = used = 0; list = NULL; /* take arguments one by one */ while(node != NULL) { if (indirect) { while((*node == ' ') || (*node == '\t')) node++; next = strpbrk(node, " \t"); } else { for(;;) { next = strpbrk(node, sep); if ((next > node) || (next == NULL)) break; node = next+1; } } if (next != NULL) { *next = '\0'; next++; } if (indirect) snode = str_subsn(get(node)); else snode = node; cw = snode; /* split node value (s) by sep */ /* fprintf(stderr, "nodename=%s snode=%s next=%s\n", node, snode, next);*/ while(cw != NULL) { nw = strpbrk(cw, sep); if (nw != NULL) { *nw = '\0'; nw++; } if (*cw != '\0') { /* try to find cw in the existing list - this is a slow linear search for now */ for(n = 0; n < used; n++) { if (strcmp(list[n], cw) == 0) goto already_on_list; } /* not found, append */ grow; list[used] = strclone(cw); used++; } already_on_list:; cw = nw; } if (indirect) free(snode); node = next; } grow; list[used] = NULL; if (numlines != NULL) *numlines = used; free(orig_node); free(sep); return list; } void uniq_inc_free(char **arr) { char **s; for(s = arr; *s != NULL; s++) free(*s); free(arr); } static int uniq_inc_str_cmp(const void *a_, const void *b_) { char **a = (char **)a_, **b = (char **)b_; return strcmp(*a, *b); } static void uniq_inc_assemble_normal(char* const ret, int numelem, char **arr, const char *osep, const int oseplen) { char *end; int len; for(end = ret; 0 < numelem; ++arr, --numelem) { if (!*arr) continue; len = strlen(*arr); memcpy(end, *arr, len); end += len; memcpy(end, osep, oseplen); end += oseplen; free(*arr); } *end = '\0'; } static void uniq_inc_assemble_groups(char* const ret, int numelem, char **arr, const char *osep, const int oseplen, int eren, char **eres) { char *end = ret; int erei, ndx, len; /* re_comp() uses a global variable to store the compiler regex! */ for (erei = 0; erei < eren; ++erei) { if (re_comp(eres[erei])) abort(); for (ndx = 0; ndx < numelem; ++ndx) { if (!arr[ndx]) continue; if (re_exec(arr[ndx])) { len = strlen(arr[ndx]); memcpy(end, arr[ndx], len); end += len; memcpy(end, osep, oseplen); end += oseplen; free(arr[ndx]); arr[ndx] = NULL; } } } /* collect remaining elements */ uniq_inc_assemble_normal(end, numelem, arr, osep, oseplen); } char *uniq_inc_str(const char *includes, const char *isep, const char *osep, int sort, int eren, char **eres) { char **arr, **s, *ret; int len, numelem, oseplen; /* split and uniq */ oseplen = strlen(osep); arr = uniq_inc_arr(includes, 0, isep, NULL); /* calculate the required amount of memory */ len = 4; /* safety margin, for terminator \0, etc. */ numelem = 0; for(s = arr; *s != NULL; s++) { len += strlen(*s) + oseplen + 1; numelem++; } /* sort if needed */ if (sort) qsort(arr, numelem, sizeof(char *), uniq_inc_str_cmp); /* allocate memory to assemble into */ ret = malloc(len); /* assemble the output */ if (0>=eren) uniq_inc_assemble_normal(ret, numelem, arr, osep, oseplen); else uniq_inc_assemble_groups(ret, numelem, arr, osep, oseplen, eren, eres); /* done */ free(arr); return ret; } char *order_inc_str(const char *includes, const char *isep, const char *word1, int dir, const char *word2) { const char *s, *next, *pre, *mid, *post; char *out, *end; long w1o = -1, w2o = -1; long w1len = strlen(word1), w2len = strlen(word2), tlen; long pre_len, mid_len, post_len; if (dir == 0) return NULL; if ((w1len == 0) || (w2len == 0)) return strclone(includes); if ((w1len == w2len) && (strcmp(word1, word2) == 0)) return strclone(includes); /* search the starting offset of the first occurence of word1 and word2 */ for(s = includes; (s != NULL) && ((w1o < 0) || (w2o < 0)); s = next) { next = strpbrk(s, isep); if (next == NULL) tlen = strlen(s); else tlen = next-s; if ((w1o < 0) && (w1len == tlen) && (memcmp(s, word1, tlen) == 0)) w1o = s - includes; if ((w2o < 0) && (w2len == tlen) && (memcmp(s, word2, tlen) == 0)) w2o = s - includes; if (next != NULL) next += strspn(next, isep); } /* one of the words is not on the list, the list is ordered */ if ((w1o < 0) || (w2o < 0)) return strclone(includes); /* both words are not on the list, but the list is ordered */ if (((dir < 0) && (w1o < w2o)) || ((dir > 0) && (w1o > w2o))) return strclone(includes); /* split up the input at word1 and word2 */ tlen = strlen(includes); if (dir < 0) { /* input is: 'pre w2 mid w1 post', goal is mowing w1 before w2 */ pre = includes; pre_len = w2o; mid = includes + w2o + w2len + 1; mid_len = (includes + w1o) - mid; post = includes + w1o + w1len + 1; post_len = (includes + tlen) - post + 1; } else { /* input is: 'pre w1 mid w2 post' goal is moving w1 after w2*/ pre = includes; pre_len = w1o; mid = includes + w1o + w1len + 1; mid_len = (includes + w2o) - mid; post = includes + w2o + w2len + 1; post_len = (includes + tlen) - post + 1; } /* truncate trailing separator, if present */ if ((pre_len > 0) && (strchr(isep, pre[pre_len-1]))) pre_len--; if ((mid_len > 0) && (strchr(isep, mid[mid_len-1]))) mid_len--; if ((post_len > 0) && (strchr(isep, post[mid_len-1]))) post_len--; /* allocate extra space for a trailing separator and/or \0 */ end = out = malloc(tlen+2); /* build the string by appending the parts */ #define append(str, len) \ if (len > 0) { \ memcpy(end, str, len); \ end += len; \ *end = *isep; \ end++; \ } append(pre, pre_len); if (dir < 0) { append(word1, w1len); append(word2, w2len); } append(mid, mid_len); if (dir > 0) { append(word2, w2len); append(word1, w1len); } append(post, post_len); #undef append /* replace the last separator with \0 or just add a \0 at the end */ if ((end > out) && (strchr(isep, end[-1]))) end[-1] = '\0'; else end[0] = '\0'; return out; } fungw-1.2.0/scconfig/src/default/lib_pkg_config.c0000644000175100017510000001057213520506561020156 0ustar svnsvn#include #include #include #include #include #include #include "log.h" #include "libs.h" #include "db.h" #include "dep.h" #include "regex.h" static void zap(char **str) { const char *pat = get("/arg/sys/pkg-config-zap"); char *n; if (pat == NULL) return; if (re_comp(pat) != NULL) return; while (re_exec(*str)) { n = re_subs_dup(""); free(*str); *str = n; } } int run_gen_config(int logdepth, const char *confname, const char *pkgname, char **cflags, char **ldflags) { char cmd[256]; if (strlen(confname) + strlen(pkgname) > sizeof(cmd) - 16) { logprintf(logdepth, "run_gen_config(): confname and/or pkgname too long\n"); return -1; } if (cflags != NULL) { sprintf(cmd, "%s --cflags %s", confname, pkgname); if (run(logdepth, cmd, cflags) != 0) { report("not found: %s --cflags failed.", confname); logprintf(logdepth, "not found: %s --cflags failed.\n", confname); return -1; } if (*cflags != NULL) { zap(cflags); strip(*cflags); } } if (ldflags != NULL) { sprintf(cmd, "%s --libs %s", confname, pkgname); if (run(logdepth, cmd, ldflags) != 0) { report("not found: %s --libs failed.", confname); logprintf(logdepth, "not found: %s --libs failed.\n", confname); if (cflags != NULL) free(*cflags); return -1; } if (*ldflags != NULL) { zap(ldflags); strip(*ldflags); } } return 0; } const char *pkg_config_name() { const char *name; name = get("/arg/sys/pkg-config"); if (name != NULL) return name; return "pkg-config"; /* fallback */ } /** run_pkg_config_modversion: run `pkg-config` on @pkgname: - with `--modversion` if @modversion is not NULL, storing the result in @modversion (malloc()'d) Returns 0 on success. */ int run_pkg_config_modversion(int logdepth, const char *pkgname, char **modversion) { char cmd[256]; const char *confname = pkg_config_name(); if (strlen(confname) + strlen(pkgname) > sizeof(cmd) - 16) { logprintf(logdepth, "run_pkg_config_modversion(): confname and/or pkgname too long\n"); return -1; } if (modversion != NULL) { sprintf(cmd, "%s --modversion %s", confname, pkgname); if (run(logdepth, cmd, modversion) != 0) { /*report("Module version not found: %s --modversion %s failed.", confname, pkgname); logprintf(logdepth, "Module version not found: %s --modversion %s failed.\n", confname, pkgname); */ return -1; } zap(modversion); strip(*modversion); } return 0; } /** run_pkg_config_modversion_db: run `pkg-config --modversion` on @pkgname to find module (or package) version and store the result in @node/modversion Returns 0 on success. */ int run_pkg_config_modversion_db(int logdepth, const char *node, const char *pkgname /*, char **modversion */ ) { char *modversion; char *tmp; if (run_pkg_config_modversion(logdepth, pkgname, &modversion) != 0) { return -1; } /* Store the module version in node */ tmp = str_concat("/", node, "modversion", NULL); put(tmp, modversion); free(tmp); free(modversion); return 0; } int run_pkg_config(int logdepth, const char *pkgname, char **cflags, char **ldflags) { return run_gen_config(logdepth, pkg_config_name(), pkgname, cflags, ldflags); } void run_pkg_config_lst(int logdepth, const char *pkgpat, int *argc, char ***argv) { char *end, *s, *next; int n = 0, a = 0; char **sf = NULL; static const char *pkg_cfg_cache = NULL; static char no_pkg_cfg; char *list; if (pkg_cfg_cache == &no_pkg_cfg) goto error; if (pkg_cfg_cache == NULL) { char *cmd = str_concat(" ", pkg_config_name(), "--list-all", NULL); run(logdepth, cmd, (char **) &pkg_cfg_cache); free(cmd); if (pkg_cfg_cache == NULL) { pkg_cfg_cache = &no_pkg_cfg; goto error; } } if (re_comp(pkgpat) != NULL) goto error; s = list = strclone(pkg_cfg_cache); for (;;) { while (isspace(*s)) s++; if (*s == '\0') break; next = strpbrk(s, "\r\n"); if (next != NULL) *next = '\0'; if (re_exec(s)) { if ((n + 2) >= a) { /* n+2: make sure there's always room for the NULL at the end */ a += 16; sf = realloc(sf, sizeof(char *) * a); } end = strpbrk(s, " \t"); if (end != NULL) *end = '\0'; sf[n] = strclone(s); sf[n + 1] = re_subs_dup(""); /* report("\ns='%s' sf='%s'\n", s, sf[n]);*/ n += 2; } s = next + 1; } if (sf != NULL) sf[n] = NULL; free(list); error:; *argc = n; *argv = sf; return; } fungw-1.2.0/scconfig/src/default/lib_file.c0000644000175100017510000001133213234567512016767 0ustar svnsvn/* scconfig - library to query files and directories Copyright (C) 2009..2012 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include #include #include "db.h" #include "libs.h" #include "log.h" #include "dep.h" int file_size(const char *name) { struct stat buf; if (stat(name, &buf) != 0) return -1; return buf.st_size; } char *tempdir_new(int logdepth, const char *suffix) { char s[1024]; char *cmd; const char *tmp; const char *mkdir, *emu; unsigned int rn, n; require("sys/tmp", logdepth+1, 1); tmp = get("sys/tmp"); if (strlen(suffix) > sizeof(s) - strlen(tmp) - 32) { fprintf(stderr, "Not enough room for creating temporary file name\n"); abort(); } require("fstools/mkdir", logdepth+1, 1); mkdir = get("fstools/mkdir"); emu = get("sys/emu"); if (emu == NULL) emu = ""; for(n = 0; n < 128; n++) { rn = rand() % 100000; sprintf(s, "%sscc_%u%s", tmp, rn, suffix); if (!exists(s)) { char *s_esc = shell_escape_dup(s); cmd = malloc(strlen(s_esc) + strlen(mkdir) + 16); sprintf(cmd, "%s %s", mkdir, s_esc); run_shell(logdepth+1, cmd, NULL); free(s_esc); free(cmd); if (is_dir(s)) return strclone(s); } } error("Couldn't find a suitable temp dir name\n"); abort(); } char *tempfile_new_noabort(const char *suffix) { char s[1024]; const char *tmp; unsigned int rn, n; FILE *f; require("/host/sys/tmp", 0, 1); tmp = get("/host/sys/tmp"); if (strlen(suffix) > sizeof(s) - strlen(tmp) - 32) { fprintf(stderr, "tempfile_new_noabort(): not enough room for creating temporary file name\n"); abort(); } for(n = 0; n < 128; n++) { rn = rand() % 100000; sprintf(s, "%sscc_%u%s", tmp, rn, suffix); if (!is_file(s)) { /* can not test for exists() because that would recurse is_dir */ f = fopen(s, "w"); if (f != NULL) { fclose(f); return strclone(s); } } } return NULL; } char *tempfile_new(const char *suffix) { char *tmp; tmp = tempfile_new_noabort(suffix); if (tmp == NULL) { error("Couldn't find a suitable temp file name\n"); abort(); } return tmp; } char *tempfile_dump(const char *testcode, const char *suffix) { char *fn; FILE *f; fn = tempfile_new(suffix); f = fopen(fn, "w"); fprintf(f, "%s", testcode); fclose(f); return fn; } char *load_file(const char *name) { int size; char *content; FILE *f; size = file_size(name); if (size > 0) { content = malloc(size+1); *content = '\0'; f = fopen(name, "r"); if (f != NULL) { int len = fread(content, 1, size, f); if (len < 0) len = 0; content[len] = '\0'; fclose(f); } } else { content = malloc(1); *content = '\0'; } return content; } int is_dir(const char *path) { char *tmp, *path_esc; int ret; require("sys/shell", 0, 1); path_esc = shell_escape_dup(path); tmp = malloc(strlen(path_esc) + 16); sprintf(tmp, "cd %s", path_esc); ret = run_shell(0, tmp, NULL); free(tmp); free(path_esc); return !ret; } int is_file(const char *path) { return file_size(path) >= 0; } int exists(const char *path) { return is_file(path) || is_dir(path); } int exists_in(const char *dir, const char *file) { char *path; int ret; path = malloc(strlen(dir) + strlen(file) + 5); sprintf(path, "%s/%s", dir, file); ret = is_file(path) || is_dir(path); free(path); return ret; } const char *file_name_ptr(const char *path) { const char *s; s = str_rchr((char *)path, '/'); if (s == NULL) s = str_rchr((char *)path, '\\'); return s; } char *file_name(const char *path) { const char *s; s = file_name_ptr(path); if (s == NULL) return strclone(path); s++; return strclone(s); } char *dir_name(const char *path) { char *s, *r; s = strclone(path); r = (char *)file_name_ptr(s); if (r == NULL) { free(s); return strclone(""); } *r = '\0'; return s; } int touch_file(const char *path) { FILE *f; f = fopen(path, "a"); if (f == NULL) return -1; fclose(f); return 0; } fungw-1.2.0/scconfig/src/default/find_fscalls.c0000644000175100017510000005576314003563437017666 0ustar svnsvn/* scconfig - detection of standard library features: file system specific calls Copyright (C) 2010 Tibor Palinkas Copyright (C) 2018 Aron Barath This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" int find_fs_realpath(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "#include " NL "#ifdef PATH_MAX" NL "char out_buf[PATH_MAX];" NL "#else" NL "char out_buf[32768];" NL "#endif" NL "int main() {" NL " if (realpath(\".\", out_buf) == out_buf)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for realpath()... "); logprintf(logdepth, "find_fs_realpath: trying to find realpath...\n"); logdepth++; if (try_icl(logdepth, "libs/fs/realpath", test_c, NULL, NULL, NULL)) return 0; if (try_icl(logdepth, "libs/fs/realpath", test_c, "#define _DEFAULT_SOURCE", NULL, NULL)) return 0; if (try_icl(logdepth, "libs/fs/realpath", test_c, "#define _BSD_SOURCE", NULL, NULL)) return 0; return try_fail(logdepth, "libs/fs/realpath"); } int find_fs__fullpath(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "#include " NL "#include " NL "int main() {" NL " char full[_MAX_PATH];" NL " if (_fullpath(full, \".\", _MAX_PATH) != NULL)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for _fullpath()... "); logprintf(logdepth, "find_fs__fullpath: trying to find _fullpath...\n"); logdepth++; if (try_icl(logdepth, "libs/fs/_fullpath", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "libs/fs/_fullpath"); } int find_fs_readdir(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "#include " NL "int main() {" NL " DIR *dirp;" NL " struct dirent *dp;" NL " int found = 0;" NL " if ((dirp = opendir(\".\")) == 0)" NL " return -1;" NL " while ((dp = readdir(dirp)) != 0)" NL " if (strcmp(dp->d_name, \"configure\") == 0)" NL " found++;" NL " closedir(dirp);" NL " if (found == 1)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; char *includes[] = { "#include ", "#include ", /* 4.2BSD */ NULL }; char **i; require("cc/cc", logdepth, fatal); report("Checking for readdir()... "); logprintf(logdepth, "find_fs_readdir: trying to find readdir...\n"); logdepth++; for (i = includes; *i != NULL; i++) if (try_icl(logdepth, "libs/fs/readdir", test_c, *i, NULL, NULL)) return 0; return try_fail(logdepth, "libs/fs/readdir"); } int find_fs_findnextfile(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "#include " NL "int main(int argc, char *argv[]) {" NL " WIN32_FIND_DATA fd;" NL " HANDLE h;" NL " int found=0;" NL " h = FindFirstFile(argv[0], &fd);" NL " if (h == INVALID_HANDLE_VALUE)" NL " return -1;" NL " while (FindNextFile(h, &fd) != 0);" NL " found++;" NL " FindClose(h);" NL " if (found > 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for FindNextFile()... "); logprintf(logdepth, "find_fs_findnextfile: trying to find FindNextFile...\n"); logdepth++; if (try_icl(logdepth, "libs/fs/findnextfile", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "libs/fs/findnextfile"); } int find_fs_access(const char *name, int logdepth, int fatal) { char *test_c = NL "int my_test() { return access(\".\", 0); }" NL "#include " NL "int main() {" NL " if (my_test() == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; const char* includes[] = { "#include ", "#include \n#include ", "#include ", NULL }; const char** inc; require("cc/cc", logdepth, fatal); report("Checking for access()... "); logprintf(logdepth, "find_fs_access: trying to find access...\n"); logdepth++; for (inc=includes; *inc; ++inc) if (try_icl(logdepth, "libs/fs/access", test_c, *inc, NULL, NULL)) return 0; return try_fail(logdepth, "libs/fs/access"); } int find_fs_access_macros(const char *rname, int logdepth, int fatal) { char *test_c_templ = NL "%s" NL "void my_test() { int a = %s; }" NL "#include " NL "int main() {" NL " my_test();" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; char test_c[256]; char *names[][3] = { {"F_OK", "F_OK", NULL}, {"R_OK", "R_OK", NULL}, {"W_OK", "W_OK", NULL}, {"X_OK", "X_OK", NULL}, {NULL, NULL, NULL} }; char **n; const char* access_includes; int name, pr; char nodename[128]; require("cc/cc", logdepth, fatal); if (require("libs/fs/access/*", logdepth, fatal)!=0 || !istrue(get("libs/fs/access/presents"))) { put("libs/fs/access/macros/presents", sfalse); return 1; } access_includes = get("libs/fs/access/includes"); report("Checking for access macros:\n"); logprintf(logdepth, "find_fs_access_macros: trying to find access macros...\n"); logdepth++; pr = 0; for(name = 0; *names[name] != NULL; name++) { report(" %s...\t", names[name][0]); for(n = &names[name][0]; *n != NULL; n++) { sprintf(test_c, test_c_templ, access_includes, *n); if (try_icl(logdepth, NULL, test_c, NULL, NULL, NULL)) { sprintf(nodename, "libs/fs/access/macros/%s", names[name][0]); put(nodename, *n); report("found as %s\n", *n); pr++; goto found; } } report("not found\n"); found:; } put("libs/fs/access/macros/presents", ((pr > 0) ? (strue) : (sfalse))); return (pr == 0); } int find_fs_stat_macros(const char *rname, int logdepth, int fatal) { char *test_c_templ = NL "#include " NL "#include " NL "void my_test() { int a = %s(0); }" NL "#include " NL "int main() {" NL " my_test();" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; char test_c[256]; char *names[][3] = { {"S_ISREG", "S_IFREG", NULL}, {"S_ISDIR", "S_IFDIR", NULL}, {"S_ISCHR", "S_IFCHR", NULL}, {"S_ISBLK", "S_IFBLK", NULL}, {"S_ISFIFO", "S_IFFIFO", NULL}, {"S_ISLNK", "S_IFLNK", NULL}, {"S_ISCHR", "S_IFCHR", NULL}, {"S_ISSOCK", "S_IFSOCK", NULL}, {NULL, NULL, NULL} }; char **n; int name, pr; char nodename[128]; require("cc/cc", logdepth, fatal); report("Checking for stat macros:\n"); logprintf(logdepth, "find_fs_stat_macros: trying to find stat macros...\n"); logdepth++; pr = 0; for(name = 0; *names[name] != NULL; name++) { report(" %s...\t", names[name][0]); for(n = &names[name][0]; *n != NULL; n++) { sprintf(test_c, test_c_templ, *n); if (try_icl(logdepth, NULL, test_c, NULL, NULL, NULL)) { sprintf(nodename, "libs/fs/stat/macros/%s", names[name][0]); put(nodename, *n); report("found as %s\n", *n); pr++; goto found; } } report("not found\n"); found:; } put("libs/fs/stat/macros/presents", ((pr > 0) ? (strue) : (sfalse))); return (pr == 0); } int find_fs_stat_fields(const char *rname, int logdepth, int fatal) { char *test_c_templ = NL "#include " NL "#include " NL "#include " NL "int main() {" NL " struct stat st;" NL " (void)st.%s;" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; char test_c[256]; char *names[] = {"st_blksize", "st_blocks", "st_rdev", "st_mtim", "st_mtime", "st_birthtim", "st_birthtime", NULL }; int name, pr; char nodename[128]; require("cc/cc", logdepth, fatal); report("Checking for stat macros:\n"); logprintf(logdepth, "find_fs_stat_fields: trying to find stat macros...\n"); logdepth++; pr = 0; for(name = 0; names[name] != NULL; name++) { report(" %s...\t", names[name]); sprintf(test_c, test_c_templ, names[name]); sprintf(nodename, "libs/fs/stat/fields/%s/presents", names[name]); if (try_icl(logdepth, NULL, test_c, NULL, NULL, NULL)) { put(nodename, strue); report("found\n"); pr++; } else { report("not found\n"); put(nodename, sfalse); } } return (pr == 0); } static int find_fs_any_lstat(const char *name, int logdepth, int fatal, char *fn) { /* make sure does not affect our lstat() detection */ const char *test_c_in = NL "void my_puts(const char *s);" NL "int main() {" NL " struct stat buf;" NL " if (%s(\".\", &buf) == 0)" NL " my_puts(\"OK\");" NL " return 0;" NL "}" NL "#include " NL "void my_puts(const char *s)" NL "{" NL " puts(s);" NL "}" NL; char test_c[384], node[64]; const char *incs[] = {"#include ", "#include ", "#include \n#include \n#include ", NULL}; const char **inc; require("cc/cc", logdepth, fatal); sprintf(node, "libs/fs/%s", fn); sprintf(test_c, test_c_in, fn); report("Checking for %s... ", fn); logprintf(logdepth, "find_fs_%s: trying to find lstat()...\n", fn); logdepth++; for (inc = incs; *inc; ++inc) { if (try_icl(logdepth, node, test_c, *inc, NULL, NULL)) return 0; } return try_fail(logdepth, node); } int find_fs_lstat(const char *name, int logdepth, int fatal) { return find_fs_any_lstat(name, logdepth, fatal, "lstat"); } int find_fs_statlstat(const char *name, int logdepth, int fatal) { return find_fs_any_lstat(name, logdepth, fatal, "statlstat"); } int find_fs_getcwd(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " char b[1024];" NL " if (getcwd(b, sizeof(b)) != NULL)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for getcwd... "); logprintf(logdepth, "find_fs_getcwd: trying to find getcwd()...\n"); logdepth++; if (try_icl(logdepth, "libs/fs/getcwd", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "libs/fs/getcwd"); } int find_fs__getcwd(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " char b[1024];" NL " if (_getcwd(b, sizeof(b)) != NULL)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for _getcwd... "); logprintf(logdepth, "find_fs__getcwd: trying to find _getcwd()...\n"); logdepth++; if (try_icl(logdepth, "libs/fs/_getcwd", test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, "libs/fs/_getcwd"); } int find_fs_getwd(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " char b[8192];" NL " if (getwd(b) != NULL)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for getwd... "); logprintf(logdepth, "find_fs_getwd: trying to find getwd()...\n"); logdepth++; if (try_icl(logdepth, "libs/fs/getwd", test_c, NULL, NULL, NULL)) return 0; return try_fail(logdepth, "libs/fs/getwd"); } int find_fs_mkdir(const char *name, int logdepth, int fatal) { char *dir; char test_c[1024]; char *test_c_in = NL "#include " NL "int main() {" NL no_implicit(int, "mkdir", "mkdir") NL " if (mkdir(\"%s\"%s) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); dir = tempfile_new(""); unlink(dir); report("Checking for mkdir... "); logprintf(logdepth, "find_fs_mkdir: trying to find mkdir()...\n"); logdepth++; /* POSIX, 2 arguments, standard includes */ sprintf(test_c, test_c_in, dir, ", 0755"); if (try_icl(logdepth, "libs/fs/mkdir", test_c, "#include \n#include \n", NULL, NULL)) { if (!is_dir(dir)) goto oops1; put("libs/fs/mkdir/num_args", "2"); rmdir(dir); return 0; } /* POSIX, 2 arguments, no includes */ oops1:; sprintf(test_c, test_c_in, dir, ", 0755"); if (try_icl(logdepth, "libs/fs/mkdir", test_c, NULL, NULL, NULL)) { if (!is_dir(dir)) goto oops2; put("libs/fs/mkdir/num_args", "2"); rmdir(dir); return 0; } /* win32, 1 argument, with */ oops2:; sprintf(test_c, test_c_in, dir, ""); if (try_icl(logdepth, "libs/fs/mkdir", test_c, "#include \n", NULL, NULL)) { if (!is_dir(dir)) goto oops3; put("libs/fs/mkdir/num_args", "1"); rmdir(dir); return 0; } oops3:; put("libs/fs/mkdir/includes", ""); put("libs/fs/mkdir/ldflags", ""); put("libs/fs/mkdir/cdflags", ""); rmdir(dir); return try_fail(logdepth, "libs/fs/mkdir"); } int find_fs__mkdir(const char *name, int logdepth, int fatal) { char *dir; char test_c[1024]; char *test_c_in = NL "#include " NL "int main() {" NL " if (_mkdir(\"%s\"%s) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); dir = tempfile_new(""); unlink(dir); report("Checking for _mkdir... "); logprintf(logdepth, "find_fs__mkdir: trying to find _mkdir()...\n"); logdepth++; /* win32, 2 arguments, standard includes */ sprintf(test_c, test_c_in, dir, ", 0755"); if (try_icl(logdepth, "libs/fs/_mkdir", test_c, "#include \n", NULL, NULL)) { if (!is_dir(dir)) goto oops1; put("libs/fs/_mkdir/num_args", "2"); rmdir(dir); return 0; } oops1:; /* win32, 1 argument, standard includes */ sprintf(test_c, test_c_in, dir, ""); if (try_icl(logdepth, "libs/fs/_mkdir", test_c, "#include \n", NULL, NULL)) { if (!is_dir(dir)) goto oops2; put("libs/fs/_mkdir/num_args", "1"); rmdir(dir); return 0; } oops2:; put("libs/fs/_mkdir/includes", ""); put("libs/fs/_mkdir/ldflags", ""); put("libs/fs/_mkdir/cdflags", ""); rmdir(dir); return try_fail(logdepth, "libs/fs/_mkdir"); } static int find_utime_impl(const char *name, int logdepth, int fatal, const char* key, const char* funcname, const char* typename) { char test_c[1024+4096]; const char *test_c_templ = NL "void puts_OK();" NL "int main(int argc, char* argv[])" NL "{" NL " struct %s buf;" NL " buf.actime = buf.modtime = 1610958044;" NL " if (%s(\"%s\", &buf) == 0)" NL " puts_OK();" NL " return 0;" NL "}" NL "#include " NL "void puts_OK()" NL "{" NL " puts(\"OK\");" NL "}" NL; const char* includes[] = { /* *NIX */ "#include \n#include ", /* windoz */ "#include ", NULL }; const char** inc; char* tmpf; tmpf = tempfile_new(".txt"); sprintf(test_c, test_c_templ, typename, funcname, tmpf); require("cc/cc", logdepth, fatal); report("Checking for %s... ", funcname); logprintf(logdepth, "find_fs_%s: trying to find %s()...\n", funcname, funcname); logdepth++; for (inc=includes; *inc; ++inc) { if (try_icl(logdepth, key, test_c, *inc, NULL, NULL)) { unlink(tmpf); free(tmpf); return 0; } } unlink(tmpf); free(tmpf); return try_fail(logdepth, key); } int find_fs_utime(const char *name, int logdepth, int fatal) { return find_utime_impl(name, logdepth, fatal, "libs/fs/utime", "utime", "utimbuf"); } int find_fs__utime(const char *name, int logdepth, int fatal) { return find_utime_impl(name, logdepth, fatal, "libs/fs/_utime", "_utime", "_utimbuf"); } int find_fs__utime64(const char *name, int logdepth, int fatal) { return find_utime_impl(name, logdepth, fatal, "libs/fs/_utime64", "_utime64", "__utimbuf64"); } int find_fs_mkdtemp(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "#include " NL "int main() {" NL " char fn[32], *o;" NL " strcpy(fn, \"scc.XXXXXX\");" NL " o = mkdtemp(fn);" NL " if ((o != NULL) && (strstr(o, \"scc.\") != NULL)) {" NL " remove(o);" NL " puts(\"OK\");" NL " }" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for mkdtemp... "); logprintf(logdepth, "find_fs_mkdtemp: trying to find mkdtemp()...\n"); logdepth++; if (try_icl(logdepth, "libs/fs/mkdtemp", test_c, "#include \n", NULL, NULL)) return 0; if (try_icl(logdepth, "libs/fs/mkdtemp", test_c, "#define _BSD_SOURCE\n#include \n", NULL, NULL)) return 0; return try_fail(logdepth, "libs/fs/mkdtemp"); } int find_fs_mmap(const char *name, int logdepth, int fatal) { char test_c[1024]; char *tmp; FILE *f; char *test_c_in = NL "#include " NL "#include " NL "#include " NL "#include " NL "#include " NL "#include " NL "int main() {" NL " int fd, size = 11;" NL " void *p;" NL " fd = open(\"%s\", O_RDONLY);" NL " p = mmap(0, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);" NL " if (p == NULL) {" NL " puts(\"mmap fail\");" NL " return 0;" NL " }" NL " if (strcmp(p, \"hello world\") != 0) {" NL " puts(\"strcmp fail\");" NL " return 0;" NL " }" NL " if (munmap(p, size) != 0) {" NL " puts(\"munmap fail\");" NL " return 0;" NL " }" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); tmp = tempfile_new(""); f = fopen(tmp, "w"); fprintf(f, "hello world"); fclose(f); sprintf(test_c, test_c_in, tmp); report("Checking for mmap... "); logprintf(logdepth, "find_fs_mmap: trying to find mmap()...\n"); logdepth++; if (try_icl(logdepth, "libs/fs/mmap", test_c, "#include \n", NULL, NULL)) { unlink(tmp); free(tmp); return 0; } unlink(tmp); free(tmp); return try_fail(logdepth, "libs/fs/mmap"); } /* Haiku/BeOS next_dev */ int find_fsmount_next_dev(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "int main()" NL "{" NL " int32 pos = 0;" NL " dev_t res = next_dev(&pos);" NL " if (res >= 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for next_dev... "); logprintf(logdepth, "find_fsmount_next_dev: trying to find next_dev()...\n"); logdepth++; if (try_icl(logdepth, "libs/fsmount/next_dev", test_c, "#include \n", NULL, NULL)) return 0; return try_fail(logdepth, "libs/fsmount/next_dev"); } int find_fsmount_fsstat_fields(const char *name, int logdepth, int fatal) { const char *fields[] = {"f_fstypename", NULL}; return try_icl_sfields(logdepth, "libs/fsmount/struct_fsstat", "struct fsstat", fields, "#include ", NULL, NULL, 0); } int find_fsmount_statfs_fields(const char *name, int logdepth, int fatal) { const char *fields[] = {"f_fstypename", "f_type", NULL}; return try_icl_sfields(logdepth, "libs/fsmount/struct_statfs", "struct statfs", fields, "#include ", NULL, NULL, 0); } int find_fsmount_statvfs_fields(const char *name, int logdepth, int fatal) { const char *fields[] = {"f_fstypename", "f_type", "f_basetype", NULL}; return try_icl_sfields(logdepth, "libs/fsmount/struct_statvfs", "struct statvfs", fields, "#include ", NULL, NULL, 0); } int find_fs_ustat(const char *name, int logdepth, int fatal) { const char *key = "libs/fs/ustat"; const char *test_c = NL "#include " NL "#include " NL "int main()" NL "{" NL " struct stat stat_buf;" NL " struct ustat ustat_buf;" NL " if (stat(\".\", &stat_buf) == 0 &&" NL " ustat(stat_buf.st_dev, &ustat_buf) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for ustat... "); logprintf(logdepth, "find_fs_ustat: trying to find ustat()...\n"); logdepth++; if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0; if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0; if (try_icl(logdepth, key, test_c, "#include \n#include ", NULL, NULL)) return 0; if (try_icl(logdepth, key, test_c, "#include \n#include \n#include ", NULL, NULL)) return 0; return try_fail(logdepth, key); } int find_fs_statfs(const char *name, int logdepth, int fatal) { const char *key = "libs/fs/statfs"; const char *test_c = NL "#include " NL "int main()" NL "{" NL " struct statfs statfs_buf;" NL " if (statfs(\".\", &statfs_buf) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for statfs... "); logprintf(logdepth, "find_fs_statfs: trying to find statfs()...\n"); logdepth++; if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0; if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, key); } int find_fs_statvfs(const char *name, int logdepth, int fatal) { const char *key = "libs/fs/statvfs"; const char *test_c = NL "#include " NL "int main()" NL "{" NL " struct statvfs statvfs_buf;" NL " if (statvfs(\".\", &statvfs_buf) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for statvfs... "); logprintf(logdepth, "find_fs_statvfs: trying to find statvfs()...\n"); logdepth++; if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, key); } int find_fs_flock(const char *name, int logdepth, int fatal) { const char *key = "libs/fs/flock"; const char *test_c = NL "#include " NL "int main()" NL "{" NL " if (flock(1, LOCK_UN) == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for flock... "); logprintf(logdepth, "find_fs_flock: trying to find flock()...\n"); logdepth++; if (try_icl(logdepth, key, test_c, "#include ", NULL, NULL)) return 0; return try_fail(logdepth, key); } int find_fs_makedev(const char *name, int logdepth, int fatal) { const char *key = "libs/fs/makedev"; const char *test_c = NL "#include " NL "int main() {" NL " if (1 == major(makedev(1, 2)) && 2 == minor(makedev(1, 2)))" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; const char *includes[] = { "#include ", NULL }; const char **i; require("cc/cc", logdepth, fatal); report("Checking for makedev()... "); logprintf(logdepth, "find_fs_makedev: trying to find makedev...\n"); logdepth++; for (i = includes; *i != NULL; i++) if (try_icl(logdepth, key, test_c, *i, NULL, NULL)) return 0; return try_fail(logdepth, key); } fungw-1.2.0/scconfig/src/default/lib_compile.c0000644000175100017510000001720213520613767017504 0ustar svnsvn/* scconfig - library functions for compiling and running test code Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include #include #include #include "log.h" #include "libs.h" #include "db.h" #include "dep.h" /* #define KEEP_TEST_SRCS */ int cross_blind = 0; static char *clone_flags(const char *input, const char *node) { char *output; const char *s; int len; if (input != NULL) { if (*input == '+') { s = get(node); if (s != NULL) { len = strlen(s); output = malloc(len + strlen(input) + 4); memcpy(output, s, len); output[len] = ' '; strcpy(output + len + 1, input + 1); } else output = strclone(input); } else output = strclone(input); } else { s = get(node); if (s != NULL) output = strclone(s); else output = strclone(""); } return output; } int compile_file_raw(int logdepth, const char *fn_input, char **fn_output, const char *cc, const char *cflags, const char *ldflags) { char *cmdline; char *cc_esc, *fn_input_esc, *fn_output_esc, *temp_out_esc, *temp_out; int ret; temp_out = tempfile_new(".out"); if (*fn_output == NULL) *fn_output = tempfile_new(get("sys/ext_exe")); else *fn_output = tempfile_new(*fn_output); unlink(*fn_output); cc_esc = shell_escape_dup(cc == NULL ? get("cc/cc") : cc); fn_input_esc = shell_escape_dup(fn_input); fn_output_esc = shell_escape_dup(*fn_output); temp_out_esc = shell_escape_dup(temp_out); cmdline = str_concat("", get("/host/sys/shell"), " \"", cc_esc, " ", cflags, " ", fn_input_esc, " ", \ ldflags, " -o ", fn_output_esc, " 2>&1\" >", temp_out_esc, NULL); free(cc_esc); free(fn_input_esc); free(fn_output_esc); free(temp_out_esc); logprintf(logdepth, "compile: '%s'\n", cmdline); ret = system(cmdline); free(cmdline); log_merge(logdepth + 1, temp_out); #ifndef KEEP_TEST_SRCS unlink(temp_out); #endif free(temp_out); logprintf(logdepth, "compile result: %d\n", ret); return ret; } int compile_file(int logdepth, const char *fn_input, char **fn_output, const char *cc, const char *cflags, const char *ldflags) { int ret; char *ldflags_, *cflags_; cflags_ = clone_flags(cflags, "cc/cflags"); ldflags_ = clone_flags(ldflags, "cc/ldflags"); ret = compile_file_raw(logdepth, fn_input, fn_output, cc, cflags_, ldflags_); free(cflags_); free(ldflags_); return ret; } int compile_code(int logdepth, const char *testcode, char **fn_output, const char *cc, const char *cflags, const char *ldflags) { char *temp_in; int ret; require("sys/ext_exe", logdepth, 1); assert(testcode != NULL); assert(fn_output != NULL); temp_in = tempfile_dump(testcode, ".c"); ret = compile_file(logdepth, temp_in, fn_output, cc, cflags, ldflags); #ifndef KEEP_TEST_SRCS unlink(temp_in); #endif free(temp_in); return ret; } int compile_code_raw(int logdepth, const char *testcode, char **fn_output, const char *cc, const char *cflags, const char *ldflags) { char *temp_in; int ret; require("sys/ext_exe", logdepth, 1); assert(testcode != NULL); assert(fn_output != NULL); temp_in = tempfile_dump(testcode, ".c"); ret = compile_file_raw(logdepth, temp_in, fn_output, cc, cflags, ldflags); #ifndef KEEP_TEST_SRCS unlink(temp_in); #endif free(temp_in); return ret; } char *shell_escape_dup(const char *in) { char *o, *out; const char *i; const char *esc = get("sys/shell_escape_char"); /* in the early phase, before detecting the shell, this happens */ if (esc == NULL) return strclone(in); out = malloc(strlen(in)*2+1); for(i = in, o = out; *i != '\0'; i++) { if (*i == *esc) { *o++ = *esc; } else if (!isalnum(*i)) { switch(*i) { case '/': case '_': case '-': case '.': break; default: *o++ = *esc; } } *o++ = *i; } *o = '\0'; return out; } int run(int logdepth, const char *cmd_, char **stdout_saved) { char *cmd; char *fn_out, *temp_out; char *fn_out_esc, *temp_out_esc; int ret; const char *emu; assert(cmd_ != NULL); /* blind cross compiling mode means we always assume success */ if (cross_blind) { if (stdout_saved != NULL) *stdout_saved = NULL; return 0; } emu = get("sys/emu"); /* emu == NULL means we need an emulator but we don't have one and we should pretend everything went well (and of course can't provide output.) */ if (emu == NULL) { if (stdout_saved != NULL) *stdout_saved = NULL; return 0; } /* emu == false means we need an emulator and we don't want to pretend -> fatal */ if (strcmp(emu, sfalse) == 0) { error("Trying to run unavailable emulator (db_cwd='%s')\n", db_cwd); abort(); } temp_out = tempfile_new(".out"); fn_out = tempfile_new(""); temp_out_esc = shell_escape_dup(temp_out); fn_out_esc = shell_escape_dup(fn_out); cmd = malloc(strlen(emu) + strlen(cmd_) + strlen(fn_out_esc) + strlen(temp_out_esc) + 32); sprintf(cmd, "%s %s >%s 2>>%s", emu, cmd_, fn_out_esc, temp_out_esc); free(temp_out_esc); free(fn_out_esc); logprintf(logdepth, "run: '%s'\n", cmd); ret = system(cmd); log_merge(logdepth + 1, temp_out); unlink(temp_out); free(temp_out); logprintf(logdepth, "run result: %d\n", ret); free(cmd); if (stdout_saved != NULL) { if (ret == 0) { *stdout_saved = load_file(fn_out); logprintf(logdepth, "stdout: '%s'\n", *stdout_saved); } else *stdout_saved = NULL; } unlink(fn_out); free(fn_out); return ret; } int run_shell(int logdepth, const char *cmd_, char **stdout_saved) { int ret; char *cmd, *cmd_esc; const char *emu; const char *shell; emu = get("sys/emulator"); if (emu == NULL) emu = ""; shell = get("sys/shell"); if (shell == NULL) { error("No shell was specified (db_cwd='%s')\n", db_cwd); abort(); } cmd_esc = shell_escape_dup(cmd_); cmd = malloc(strlen(emu) + strlen(shell) + strlen(cmd_esc) + 16); if (istrue(get("sys/shell_needs_quote"))) sprintf(cmd, "%s %s \"%s\"", emu, shell, cmd_); else sprintf(cmd, "%s %s %s", emu, shell, cmd_); free(cmd_esc); ret = run(logdepth, cmd, stdout_saved); free(cmd); return ret; } int compile_run(int logdepth, const char *testcode, const char *cc, const char *cflags, const char *ldflags, char **stdout_saved) { int ret; char *fn_output = NULL; ret = compile_code(logdepth+1, testcode, &fn_output, cc, cflags, ldflags); if (ret == 0) { char *fn_output_esc = shell_escape_dup(fn_output); ret = run(logdepth+1, fn_output_esc, stdout_saved); free(fn_output_esc); } if (fn_output != NULL) { unlink(fn_output); free(fn_output); } return ret; } int run_script(int logdepth, const char *interpreter, const char *script, const char *suffix, char **out) { char *temp, *cmd; int res; temp = tempfile_dump(script, suffix); cmd = malloc(strlen(temp) + strlen(interpreter) + 4); sprintf(cmd, "%s %s", interpreter, temp); res = run(logdepth, cmd, out); unlink(temp); free(temp); free(cmd); return res; } fungw-1.2.0/scconfig/src/default/Makefile.plugin0000644000175100017510000001554713220743067020023 0ustar svnsvnDEFAULT_NOMAIN_OBJS = \ $(BIN)/default/find_cc.o \ $(BIN)/default/lib_compile.o \ $(BIN)/default/lib_uniqinc.o \ $(BIN)/default/lib_file.o \ $(BIN)/default/lib_try.o \ $(BIN)/default/str.o \ $(BIN)/default/ht.o \ $(BIN)/default/log.o \ $(BIN)/default/arg.o \ $(BIN)/default/db.o \ $(BIN)/default/dep.o \ $(BIN)/default/deps_default.o \ $(BIN)/default/find_libs.o \ $(BIN)/default/find_fscalls.o \ $(BIN)/default/find_printf.o \ $(BIN)/default/find_proc.o \ $(BIN)/default/find_fstools.o \ $(BIN)/default/find_uname.o \ $(BIN)/default/find_target.o \ $(BIN)/default/find_thread.o \ $(BIN)/default/find_io.o \ $(BIN)/default/find_time.o \ $(BIN)/default/find_types.o \ $(BIN)/default/find_signal.o \ $(BIN)/default/find_environ.o \ $(BIN)/default/regex.o \ $(BIN)/default/lib_filelist.o \ $(BIN)/default/lib_srctree.o \ $(BIN)/default/lib_pkg_config.o \ $(BIN)/default/find_str.o \ $(BIN)/default/find_sys.o DEFAULT_MAIN_OBJS = \ $(BIN)/default/main.o \ $(BIN)/default/main_custom_args.o \ $(BIN)/default/main_lib.o DEFAULT_OBJS = $(DEFAULT_NOMAIN_OBJS) $(DEFAULT_MAIN_OBJS) $(BIN)/default/lib_compile.o: $(SRC)/default/lib_compile.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/lib_compile.c -o $(BIN)/default/lib_compile.o $(BIN)/default/lib_file.o: $(SRC)/default/lib_file.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/lib_file.c -o $(BIN)/default/lib_file.o $(BIN)/default/lib_try.o: $(SRC)/default/lib_try.c $(SRC)/default/log.h $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/lib_try.c -o $(BIN)/default/lib_try.o $(BIN)/default/str.o: $(SRC)/default/str.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/str.c -o $(BIN)/default/str.o $(BIN)/default/ht.o: $(SRC)/default/ht.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/ht.c -o $(BIN)/default/ht.o $(BIN)/default/log.o: $(SRC)/default/log.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/log.c -o $(BIN)/default/log.o $(BIN)/default/arg.o: $(SRC)/default/arg.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/arg.c -o $(BIN)/default/arg.o $(BIN)/default/db.o: $(SRC)/default/db.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/db.c -o $(BIN)/default/db.o $(BIN)/default/dep.o: $(SRC)/default/dep.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/dep.c -o $(BIN)/default/dep.o $(BIN)/default/deps_default.o: $(SRC)/default/deps_default.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/deps_default.c -o $(BIN)/default/deps_default.o $(BIN)/default/find_libs.o: $(SRC)/default/find_libs.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_libs.c -o $(BIN)/default/find_libs.o $(BIN)/default/find_fscalls.o: $(SRC)/default/find_fscalls.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_fscalls.c -o $(BIN)/default/find_fscalls.o $(BIN)/default/find_signal.o: $(SRC)/default/find_signal.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_signal.c -o $(BIN)/default/find_signal.o $(BIN)/default/find_printf.o: $(SRC)/default/find_printf.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_printf.c -o $(BIN)/default/find_printf.o $(BIN)/default/find_proc.o: $(SRC)/default/find_proc.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_proc.c -o $(BIN)/default/find_proc.o $(BIN)/default/find_fstools.o: $(SRC)/default/find_fstools.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_fstools.c -o $(BIN)/default/find_fstools.o $(BIN)/default/find_uname.o: $(SRC)/default/find_uname.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_uname.c -o $(BIN)/default/find_uname.o $(BIN)/default/find_target.o: $(SRC)/default/find_target.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_target.c -o $(BIN)/default/find_target.o $(BIN)/default/regex.o: $(SRC)/default/regex.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/regex.c -o $(BIN)/default/regex.o $(BIN)/default/lib_filelist.o: $(SRC)/default/lib_filelist.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/lib_filelist.c -o $(BIN)/default/lib_filelist.o $(BIN)/default/lib_srctree.o: $(SRC)/default/lib_srctree.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/lib_srctree.c -o $(BIN)/default/lib_srctree.o $(BIN)/default/lib_pkg_config.o: $(SRC)/default/lib_pkg_config.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/lib_pkg_config.c -o $(BIN)/default/lib_pkg_config.o $(BIN)/default/lib_uniqinc.o: $(SRC)/default/lib_uniqinc.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/lib_uniqinc.c -o $(BIN)/default/lib_uniqinc.o $(BIN)/default/find_sys.o: $(SRC)/default/find_sys.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_sys.c -o $(BIN)/default/find_sys.o $(BIN)/default/find_str.o: $(SRC)/default/find_str.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_str.c -o $(BIN)/default/find_str.o $(BIN)/default/find_cc.o: $(SRC)/default/find_cc.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_cc.c -o $(BIN)/default/find_cc.o $(BIN)/default/find_environ.o: $(SRC)/default/find_environ.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_environ.c -o $(BIN)/default/find_environ.o $(BIN)/default/find_io.o: $(SRC)/default/find_io.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_io.c -o $(BIN)/default/find_io.o $(BIN)/default/find_time.o: $(SRC)/default/find_time.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_time.c -o $(BIN)/default/find_time.o $(BIN)/default/find_types.o: $(SRC)/default/find_types.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_types.c -o $(BIN)/default/find_types.o $(BIN)/default/main.o: $(SRC)/default/main.c $(SRC)/default/dep.h $(SRC)/default/libs.h Makefile $(CC) $(CFLAGS) -c $(SRC)/default/main.c -o $(BIN)/default/main.o $(BIN)/default/main_custom_args.o: $(SRC)/default/main_custom_args.c $(CC) $(CFLAGS) -c $(SRC)/default/main_custom_args.c -o $(BIN)/default/main_custom_args.o $(BIN)/default/main_lib.o: $(SRC)/default/main_lib.c $(CC) $(CFLAGS) -c $(SRC)/default/main_lib.c -o $(BIN)/default/main_lib.o $(BIN)/default/find_thread.o: $(SRC)/default/find_thread.c $(SRC)/default/dep.h $(SRC)/default/libs.h $(CC) $(CFLAGS) -c $(SRC)/default/find_thread.c -o $(BIN)/default/find_thread.o fungw-1.2.0/scconfig/src/default/find_environ.c0000644000175100017510000001077313240634424017704 0ustar svnsvn/* scconfig - detection of environmental variable access features Copyright (C) 2014 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" int find_main_arg3(const char *name, int logdepth, int fatal) { char *out; char *test_c = NL "#include " NL "#include " NL "int main(int argc, char *argv[], char *env[])" NL "{" NL " char **e;" NL " int cnt;" NL " for(e = env, cnt = 0; *e != NULL; e++, cnt++) ;" NL " printf(\"%d\\n\", cnt);" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for main() with 3 arguments... "); logprintf(logdepth, "find_main_3args: checking for main() with 3 arguments\n"); if (compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) { if (atoi(out) > 1) { put("libs/env/main_3arg", strue); report("OK\n"); free(out); return 0; } free(out); report("not found (broken output).\n"); } else { report("not found (no output).\n"); } put("libs/env/main_3arg", sfalse); return 1; } int find_environ(const char *name, int logdepth, int fatal) { char *out; char *test_c = NL "#include " NL "#include " NL "extern char **environ;" NL "int main(int argc, char *argv[])" NL "{" NL " char **e;" NL " int cnt;" NL " for(e = environ, cnt = 0; *e != NULL; e++, cnt++) ;" NL " printf(\"%d\\n\", cnt);" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for extern environ... "); logprintf(logdepth, "find_environ: checking for extern environ\n"); if (compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) { if (atoi(out) > 1) { put("libs/env/environ", strue); report("OK\n"); free(out); return 0; } free(out); report("not found (broken output).\n"); } else { report("not found (no output).\n"); } put("libs/env/environ", sfalse); return 1; } int find_putenv(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main(int argc, char *argv[])" NL "{" NL " putenv(\"SCCONFIG_TEST=bad\");" NL " putenv(\"SCCONFIG_TEST=OK\");" NL " printf(\"%s\\n\", getenv(\"SCCONFIG_TEST\"));" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for putenv()... "); logprintf(logdepth, "find_putenv: trying to find putenv...\n"); logdepth++; if (try_icl(logdepth, "libs/env/putenv", test_c, "", NULL, NULL)) return 0; if (try_icl(logdepth, "libs/env/putenv", test_c, "#define _XOPEN_SOURCE", NULL, NULL)) return 0; if (try_icl(logdepth, "libs/env/putenv", test_c, "#define _SVID_SOURCE", NULL, NULL)) return 0; return try_fail(logdepth, "libs/env/putenv"); } int find_setenv(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main(int argc, char *argv[])" NL "{" NL " setenv(\"SCCONFIG_TEST\", \"bad\", 1);" NL " setenv(\"SCCONFIG_TEST\", \"OK\", 1);" NL " printf(\"%s\\n\", getenv(\"SCCONFIG_TEST\"));" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for setenv()... "); logprintf(logdepth, "find_setenv: trying to find setenv...\n"); logdepth++; if (try_icl(logdepth, "libs/env/setenv", test_c, "", NULL, NULL)) return 0; if (try_icl(logdepth, "libs/env/setenv", test_c, "#define _BSD_SOURCE", NULL, NULL)) return 0; if (try_icl(logdepth, "libs/env/setenv", test_c, "#define _POSIX_C_SOURCE 200112L", NULL, NULL)) return 0; if (try_icl(logdepth, "libs/env/setenv", test_c, "#define _XOPEN_SOURCE 600", NULL, NULL)) return 0; return try_fail(logdepth, "libs/env/setenv"); } fungw-1.2.0/scconfig/src/default/log.c0000644000175100017510000000577012757462723016023 0ustar svnsvn/* scconfig - logging Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "log.h" #include #include #include #include char *spaces = " "; FILE *logfile = NULL; char *fn_log = "config.log"; void log_init(void) { if (fn_log != NULL) { /* double open for truncate - for extreme portability, please do not "fix" */ logfile = fopen(fn_log, "w"); assert(logfile != NULL); fclose(logfile); logfile = fopen(fn_log, "a"); assert(logfile != NULL); } } void log_uninit(void) { if (logfile != NULL) fclose(logfile); } void logprintf(int depth, const char *format, ...) { va_list ap; va_start(ap, format); if (logfile != NULL) { fprintf(logfile, "%s", logprefix(depth)); vfprintf(logfile, format, ap); fflush(logfile); } va_end(ap); } void error(const char *format, ...) { va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); va_start(ap, format); if (logfile != NULL) { fprintf(logfile, "###error### "); vfprintf(logfile, format, ap); fflush(logfile); } va_end(ap); } void report(const char *format, ...) { va_list ap; va_start(ap, format); vprintf(format, ap); fflush(stdout); va_end(ap); va_start(ap, format); if (logfile != NULL) { fprintf(logfile, "###report### "); vfprintf(logfile, format, ap); fflush(logfile); } va_end(ap); } void log_merge(int logdepth, const char *fn) { FILE *f; char line[2048]; int lines; if (logfile == NULL) return; f = fopen(fn, "r"); if (f == NULL) { logprintf(logdepth, "scconfig error: couldn't open %s for merging.\n", fn); return; } lines = 0; while(!(feof(f))) { *line = '\0'; fgets(line, sizeof(line), f); if (*line != '\0') { if (lines == 0) logprintf(logdepth, "========= output dump start ============\n"); lines++; logprintf(logdepth, "%s", line); /* Make sure we have newline at the end of each line */ if (line[strlen(line)-1] != '\n') logprintf(0, "\n"); } } if (lines == 0) logprintf(logdepth, "========= empty stderr =================\n"); else logprintf(logdepth, "========= output dump end ==============\n"); fclose(f); } fungw-1.2.0/scconfig/src/default/db.c0000644000175100017510000002205413425232055015603 0ustar svnsvn/* scconfig - database Copyright (C) 2009..2012 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include #include #include "db.h" #include "log.h" #include "libs.h" ht_t *DBs = NULL; char *db_cwd = NULL; void append(const char *key, const char *value) { const char *orig; char *new; int l1, l2; assert(key != NULL); assert(value != NULL); if (*value == '\0') return; orig = get(key); if (orig == NULL) { put(key, value); return; } l1 = strlen(orig); l2 = strlen(value); new = malloc(l1 + l2 + 1); memcpy(new, orig, l1); memcpy(new + l1, value, l2); new[l1+l2] = '\0'; put(key, new); } static const char *db_split_path(const char *key, ht_t **ht, char **fld) { size_t fld_len; const char *path; char first_level_dir[32]; path = str_chr((char *)(key+1), '/'); assert(path != NULL); fld_len = path - key; path++; if (*path == '\0') { *ht = NULL; if (fld != NULL) *fld = NULL; return NULL; } assert(fld_len < sizeof(first_level_dir)); strncpy(first_level_dir, key, fld_len); first_level_dir[fld_len] = '\0'; *ht = ht_get(DBs, first_level_dir+1); if (fld != NULL) *fld = first_level_dir; return path; } static void export_qs(FILE *f, const char *s) { fputc('"', f); for(;*s != '\0';s++) { switch(*s) { case '"': fputc('\\', f); fputc('"', f); break; case '\n': fputc('\\', f); fputc('n', f); break; case '\r': fputc('\\', f); fputc('r', f); break; case '\t': fputc('\\', f); fputc('t', f); break; default: fputc(*s, f); } } fputc('"', f); fputc('\n', f); } static int needs_quote(const char *s) { for(; *s != '\0'; s++) if ((*s < 32) || (*s > 126) || (*s == '"')) return 1; return 0; } int export_(FILE *f, int export_empty, ht_t *table, const char *fld) { ht_entry_t *h; for(h = ht_first(table); h != NULL; h = ht_next(table, h)) if (export_empty || ((h->value != NULL) && (*(char *)h->value != '\0'))) { fprintf(f, "/%s/%s=", fld, h->key); if (h->value != NULL) { if (needs_quote((char *)h->value)) export_qs(f, (const char *)h->value); else fprintf(f, "%s\n", (char *)h->value); } else fprintf(f, "\n"); } return 0; } int export(const char *fn, int export_empty, const char *root) { FILE *f; int ret = 0; /* ht_t *table; */ ht_entry_t *h; if (fn != NULL) { f = fopen(fn, "w"); if (f == NULL) return -1; } else f = stdout; if ((root == NULL) || ((root[0] == '/') && (root[1] == '\0'))) { /* export all directories */ for(h = ht_first(DBs); h != NULL; h = ht_next(DBs, h)) ret += export_(f, export_empty, h->value, h->key); } else { error("not yet implemented\n"); abort(); /* db_split_path(); */ } if (f != stdout) fclose(f); return ret; } /* append a single character, grow the buffer as needed */ #define qappend(chr) \ do { \ if (used >= alloced) { \ alloced += 256; \ res = realloc(res, alloced); \ } \ res[used] = chr; \ used++; \ } while(0) /* read until end of quote and interpret backslash sequences if do_esc is non-zero */ static char *readq(FILE *f, char *str, long strmax, int quote, int do_esc, int *num_lines, const char *fn) { int bs = 0; long used = 0, alloced = 0; char *res = NULL, *s; for(;;) { for(s = str; *s != '\0'; s++) { if (*s == '\n') (*num_lines)++; if (bs) { /* character escaped by backslash */ switch(*s) { case '\\': qappend('\\'); break; case 'n': qappend('\n'); break; case 'r': qappend('\r'); break; case 't': qappend('\t'); break; default: qappend(*s); break; } bs = 0; } else if (*s == quote) { /* end */ qappend('\0'); if ((s[1] != '\r') && (s[1] != '\n') && (s[1] != '\0')) fprintf(stderr, "Warning: trailing text after quote ignored in %s:%d\n", fn, (*num_lines)+1); return res; } else if (do_esc && (*s == '\\')) bs = 1; /* backslash start */ else qappend(*s); /* plain character */ } /* get the next chunk */ fgets(str, strmax, f); } return NULL; /* can't get here */ } int import(const char *fn) { char line[1024]; char *key, *value, *nl, *slash; int num_records, num_lines; FILE *f; f = fopen(fn, "r"); if (f == NULL) return -1; for(num_records = 0, num_lines = 0; !feof(f); num_lines++) { *line = '\0'; fgets(line, sizeof(line) - 1, f); if ((*line != '#') && (*line != '\n') && (*line != '\r') && (*line != '\0')) { int quote, do_esc=0; key = line; value = str_chr(key, '='); if (value == NULL) { error("Error importing: missing '=' in line %d in file %s.\n", num_lines, fn); abort(); } num_records++; *value = '\0'; value++; if (*value == '"') { quote=*value; value++; do_esc=1; } else if (*value == '\'') { quote=*value; value++; } else quote=0; if (!quote) { nl = str_chr(value, '\n'); if (nl != NULL) *nl = '\0'; } else value = readq(f, value, sizeof(line) - (value - line) - 4, quote, do_esc, &num_lines, fn); slash = str_chr(key+1, '/'); if (slash == NULL) { error("Error importing: no directory name for %s.\n", key); abort(); } *slash = '\0'; db_mkdir(key); *slash = '/'; put(key, value); logprintf(0, "(Import from '%s': '%s'='%s')\n", fn, key, value); if (quote) free(value); } } fclose(f); return num_records; } int import_args(const char *key, const char *fn) { (void) key; /* suppress compiler warnings for unused key; needed because function pointers to this function from arg.c */ db_mkdir("/target"); db_mkdir("/host"); return import(fn) < 0; } static const char *db_get(const char *key) { const char *path; ht_t *ht; path = db_split_path(key, &ht, NULL); if (ht == NULL) return NULL; return ht_get(ht, path); } static const char *db_put(const char *key, const char *value) { const char *path; ht_t *ht; path = db_split_path(key, &ht, NULL); if (ht == NULL) { error("db_put: can't find top level hash for '%s'\n", key); abort(); } return ht_set(ht, path, (void *)value); } #define assamble_path \ assert(strlen(key) + strlen(db_cwd) < sizeof(tmp)-1); \ sprintf(tmp, "%s/%s", db_cwd, key); const char *get(const char *key) { char tmp[256]; if (*key == '/') return db_get(key); assamble_path; return db_get(tmp); } const char *put(const char *key, const char *value) { char tmp[256]; if (*key == '/') return db_put(key, value); assamble_path; return db_put(tmp, value); } void db_init(void) { DBs = ht_resize(ht_alloc(0), 16); } void db_uninit(void) { ht_entry_t *h; ht_t *dir; for(h = ht_first(DBs); h != NULL; h = ht_next(DBs, h)) { dir = h->value; dir->refcount--; if (dir->refcount == 0) ht_free(dir); } ht_free(DBs); if (db_cwd != NULL) free(db_cwd); /* Just in case someone calls db_init again... */ db_cwd = NULL; DBs = NULL; } void db_cd(const char *path) { assert(*path == '/'); if (db_cwd != NULL) free(db_cwd); db_cwd = strclone(path); } void db_mkdir(const char *path) { ht_t *ht, *target; assert(*path == '/'); target = ht_get(DBs, path+1); if (target == NULL) { ht = ht_resize(ht_alloc(1), 256); ht_set(DBs, path+1, ht); } } void db_rmdir(const char *path) { ht_t *ht; assert(*path == '/'); ht = ht_get(DBs, path+1); if (ht == NULL) return; ht_del(DBs, path+1); /* ht_free(ht); */ } void db_link(const char *existing, const char *new) { ht_t *ht; assert(*new == '/'); ht = ht_get(DBs, existing+1); assert(ht != NULL); ht_set(DBs, new+1, ht); ht->refcount++; } char *concat_nodes(const char *prefix, ...) { char *buff; const char *node, *value; int allocated = 256, len, totallen; va_list ap; va_start(ap, prefix); buff = malloc(allocated); if (prefix != NULL) { strcpy(buff, prefix); totallen = strlen(prefix); buff[totallen] = ' '; totallen++; } else totallen = 0; while((node = va_arg(ap, const char *)) != NULL) { value = get(node); if (value != NULL) { len = strlen(value); if (totallen + len >= allocated) { allocated = totallen + len + 256; buff = realloc(buff, allocated); } memcpy(buff + totallen, value, len); totallen += len; buff[totallen] = ' '; totallen++; buff[totallen] = '\0'; } } buff[totallen - 1] = '\0'; va_end(ap); return buff; } int node_istrue(const char *key) { const char *s = get(key); if (s == NULL) return 0; return istrue(s); } fungw-1.2.0/scconfig/src/default/find_printf.c0000644000175100017510000001704513240634424017525 0ustar svnsvn/* scconfig - detection of printf-related features Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" static int tryx(int logdepth, const char *test_c, const char *trying, const char *expected) { char *out = NULL; char buff[512]; logprintf(logdepth, "trying '%s'\n", trying); sprintf(buff, test_c, trying, trying); if (compile_run(logdepth+1, buff, NULL, NULL, NULL, &out) == 0) { if (strncmp(out, expected, strlen(expected)) == 0) { free(out); return 1; } free(out); } return 0; } static int tryc(int logdepth, const char *test_c, const char *trying) { char *out = NULL; char buff[512]; char *spc, *end; logprintf(logdepth, "trying '%s'\n", trying); sprintf(buff, test_c, trying); if (compile_run(logdepth+1, buff, NULL, NULL, NULL, &out) == 0) { spc = str_chr(out, ' '); if (spc == NULL) return 0; *spc = '\0'; spc++; end = str_chr(spc, ' '); if (end == NULL) return 0; *end = '\0'; if (strcmp(out, spc) == 0) { free(out); put("libs/printf_ptrcast", trying); report("OK (%s)\n", trying); return 1; } free(out); } return 0; } int find_printf_x(const char *name, int logdepth, int fatal) { const char *pfx; char *test_c = NL "#include " NL "#include " NL "int main() {" NL " printf(\"'%s%%x'/'%s%%x'\\n\", (size_t)0x1234, NULL);" NL " return 0;" NL "}" NL; char *expected = "'0x1234'/'0x0'"; require("cc/cc", logdepth, fatal); report("Checking for printf %%x prefix... "); logprintf(logdepth, "find_printf_x: trying to find printf %%x prefix...\n"); logdepth++; pfx = get("/arg/libs/printf_x"); if (pfx == NULL) { if (tryx(logdepth, test_c, "", expected)) { put("libs/printf_x", ""); report("OK ()\n"); return 0; } if (tryx(logdepth, test_c, "0x", expected)) { put("libs/printf_x", "0x"); report("OK (0x)\n"); return 0; } } else { report("User provided... "); if (tryx(logdepth, test_c, pfx, expected)) { put("libs/printf_x", pfx); report("OK (%s)\n", pfx); return 0; } } return 1; } int find_printf_ptrcast(const char *name, int logdepth, int fatal) { const char *cast; char *test_c = NL "#include " NL "#include " NL "int main() {" NL " printf(\"%%d %%d \\n\", sizeof(void *), sizeof(%s));" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for printf %%x pointer cast... "); logprintf(logdepth, "find_printf_ptrcast: trying to find printf %%x pointer cast...\n"); logdepth++; cast = get("/arg/libs/printf_ptrcast"); if (cast == NULL) { if (tryc(logdepth, test_c, "unsigned int")) return 0; if (tryc(logdepth, test_c, "unsigned long")) return 0; if (tryc(logdepth, test_c, "unsigned long long")) return 0; } else { report("User provided... "); if (tryc(logdepth, test_c, cast)) return 0; } return 1; } int find_snprintf(const char *name, int logdepth, int fatal) { char *out; char *test_c = NL "#include " NL "#include " NL "int main() {" NL " char buff[9];" NL " char *s = buff+2;" NL NL " /* build a fence */" NL " buff[0] = 0;" NL " buff[1] = 65;" NL " buff[7] = 66;" NL " buff[8] = 0;" NL NL " snprintf(s, 4, \"%d\", 123456);" NL " if ((buff[0] == 0) && (buff[1] == 65) && (buff[7] == 65) && (buff[8] == 0))" NL " printf(\"%s\\n\", s);" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for snprintf... "); logprintf(logdepth, "find_snprintf_works: trying to find snprintf...\n"); logdepth++; logprintf(logdepth, "trying snprintf...\n"); if (compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) { if (cross_blind) { put("libs/snprintf", strue); report("OK (can't check if safe)\n"); free(out); return 0; } if (strcmp(out, "123")) { put("libs/snprintf", strue); put("libs/snprintf_safe", strue); report("OK (safe)\n"); free(out); return 0; } if (strcmp(out, "1234")) { put("libs/snprintf", strue); put("libs/snprintf_safe", sfalse); report("OK (UNSAFE)\n"); free(out); return 0; } free(out); report("not found (broken output).\n"); } else { report("not found (no output).\n"); } put("libs/snprintf", sfalse); return 1; } int find_dprintf(const char *name, int logdepth, int fatal) { char *out; char *test_c = NL "#include " NL "#include " NL "int main() {" NL " dprintf(1, \"OK\\n\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for dprintf... "); logprintf(logdepth, "find_dprintf: trying to find dprintf...\n"); logdepth++; logprintf(logdepth, "trying dprintf...\n"); if ((compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) && (strcmp(out, "OK"))) { put("libs/dprintf", strue); report("found\n"); free(out); return 0; } put("libs/dprintf", sfalse); report("not found\n"); return 1; } int find_vdprintf(const char *name, int logdepth, int fatal) { char *out; char *test_c = NL "#include " NL "#include " NL "#include " NL "void local_dprintf(int fd, const char *fmt, ...)" NL "{" NL " va_list ap;" NL " va_start(ap, fmt);" NL " vdprintf(fd, fmt, ap);" NL "}" NL "int main() {" NL " local_dprintf(1, \"OK\\n\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for vdprintf... "); logprintf(logdepth, "find_vdprintf: trying to find vdprintf...\n"); logdepth++; logprintf(logdepth, "trying vdprintf...\n"); if ((compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) && (strcmp(out, "OK"))) { put("libs/vdprintf", strue); report("found\n"); free(out); return 0; } put("libs/vdprintf", sfalse); report("not found\n"); return 1; } int find_vsnprintf(const char *name, int logdepth, int fatal) { char *out; char *test_c = NL "#include " NL "#include " NL "#include " NL "void local_vsnprintf(char *s, int len, const char *fmt, ...)" NL "{" NL " va_list ap;" NL " va_start(ap, fmt);" NL " vsnprintf(s, len, fmt, ap);" NL "}" NL "int main() {" NL " char s[16];" NL " *s = '\\0';" NL " local_vsnprintf(s, 14, \"OK\\n\");" NL " printf(\"%s\", s);" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for vsnprintf... "); logprintf(logdepth, "find_vsnprintf: trying to find vsnprintf...\n"); logdepth++; logprintf(logdepth, "trying vsnprintf...\n"); if ((compile_run(logdepth+1, test_c, NULL, NULL, NULL, &out) == 0) && (strcmp(out, "OK"))) { put("libs/vsnprintf", strue); report("found\n"); free(out); return 0; } put("libs/vsnprintf", sfalse); report("not found\n"); return 1; } fungw-1.2.0/scconfig/src/util/0000755000175100017510000000000014047742764014416 5ustar svnsvnfungw-1.2.0/scconfig/src/util/ls.c0000644000175100017510000000335011257357714015176 0ustar svnsvn/* scconfig - ls built on dirent Copyright (C) 2009 Szabolcs Nagy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #ifdef _WIN32 #include static int ls(char *arg) { WIN32_FIND_DATA fd; HANDLE h; char p[MAX_PATH]; int i; for (i = 0; i+2 < MAX_PATH; i++) if (arg[i]) p[i] = arg[i]; if (i+2 < MAX_PATH) { p[i] = '\\'; p[i+1] = '*'; p[i+2] = 0; } else return -1; h = FindFirstFile(p, &fd); if (h == INVALID_HANDLE_VALUE) return -1; printf("%s\n", fd.cFileName); while (FindNextFile(h, &fd) != 0); printf("%s\n", fd.cFileName); FindClose(h); return 0; } #else #include static int ls(char *arg) { DIR *dirp; struct dirent *dp; if ((dirp = opendir(arg)) == 0) return -1; while ((dp = readdir(dirp)) != 0) printf("%s\n", dp->d_name); closedir(dirp); return 0; } #endif int main(int argc, char *argv[]) { int i; char *p = "."; if (argc > 1) p = argv[1]; return ls(p); } fungw-1.2.0/scconfig/src/util/arg_auto_set.c0000644000175100017510000000475613026416716017241 0ustar svnsvn/* scconfig - set nodes from tables upon user CLI arguments Copyright (C) 2015 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include "log.h" #include "libs.h" #include "db.h" #include "arg_auto_set.h" const arg_auto_set_node_t arg_lib_nodes[] = { {"presents", sfalse}, {"cflags", ""}, {"ldflags", ""}, {NULL, NULL} }; const arg_auto_set_node_t arg_true[] = { {"", strue}, {NULL, NULL} }; const arg_auto_set_node_t arg_false[] = { {"", sfalse}, {NULL, NULL} }; int arg_auto_set(const char *key, const char *value, const arg_auto_set_t *table) { const arg_auto_set_t *lib; const arg_auto_set_node_t *node; for(lib = table; lib->arg_key != NULL; lib++) { if (strcmp(key, lib->arg_key) == 0) { report("Executing lib table: %s\n", lib->arg_key); for(node = lib->subvals; node->name != NULL; node++) { char *s; const char *setval; setval = node->value; if (strcmp(setval, "$") == 0) setval = value; if (*node->name != '\0') { s = str_concat("/", lib->subtree, node->name, NULL); put(s, setval); free(s); } else put(lib->subtree, setval); } return 1; } } return 0; } void arg_auto_print_options(FILE *fout, const char *line_prefix, const char *padding, const arg_auto_set_t *table) { const arg_auto_set_t *t; int pl; pl = strlen(padding); for(t = table; t->arg_key != NULL; t++) { if (t->help_txt == NULL) continue; if (*t->help_txt == '$') { int kl = strlen(t->arg_key); if (kl > pl) kl = pl; fprintf(fout, "%s--%s%s%s\n", line_prefix, t->arg_key, padding+kl, t->help_txt+1); } else fprintf(fout, "%s%s\n", line_prefix, t->help_txt); } } fungw-1.2.0/scconfig/src/util/cquote.c0000644000175100017510000000641313304164013016042 0ustar svnsvn/* scconfig - quote file and pack it in a C string (ANSI C code) Copyright (C) 2016, 2018 Tibor 'Igor2' Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include void copy_strlit(const char *inds, const char *varname, const char *dprefix) { int c, nl = 0, qt = 1, ind = 1; const char *spc = (*dprefix == '\0') ? "" : " "; printf("#define NL \"\\n\"\n"); printf("%s%sconst char *%s = \\\n", dprefix, spc, varname); while((c = getc(stdin)) != EOF) { if (ind) { printf("%s", inds); if (!nl) printf(" "); ind = 0; } if (nl) { printf("NL "); nl = 0; } if (qt) { printf("\""); qt = 0; } switch(c) { case '\t': printf(" "); break; case '\n': printf("\"\n"); nl = qt = ind = 1; break; case '\r': break; case '\\': printf("\\\\"); break; case '"': printf("\\\""); break; default: if ((c < 32) || (c>126)) printf("\\%3o", c); else putc(c, stdout); } } if (!qt) printf("\""); if (nl) { if (ind) printf("%s", inds); printf("NL"); } printf(";\n"); } void copy_chrarr(const char *inds, const char *varname, const char *dprefix) { int c, col = 16000; const char *spc = (*dprefix == '\0') ? "" : " "; printf("/* (Using character array instead of string literal for long strings) */\n"); printf("%s%sconst char %s_arr[] = {", dprefix, spc, varname); while((c = getc(stdin)) != EOF) { if (col > 60) { printf("\n%s", inds); col = 0; } switch(c) { case '\t': col+=printf("'\\t',"); break; case '\r': break; case '\\': col+=printf("'\\\\',"); break; case '\'': col+=printf("'\\\'',"); break; case '\n': col+=printf("'\\n',"); col = 16000; break; default: if ((c < 32) || (c>126)) col+=printf("%d,", c); else col+=printf("'%c',", c); break; } } printf("\n%s0};\n", inds); printf("%s%sconst char *%s = %s_arr;\n", dprefix, spc, varname, varname); } int main(int argc, char *argv[]) { char *varname = "quoted_file"; char *inds = "\t"; char *banner = "/* Autogenerated by cquote.c - DO NOT EDIT */\n"; char *cmd, *arg, *dprefix = ""; int n, lit = 0; for(n = 1; n < argc; n++) { cmd = argv[n]; arg = argv[n+1]; while(*cmd == '-') cmd++; switch(*cmd) { case 'n': varname = arg; n++; break; case 'i': inds = arg; n++; break; case 'l': lit = 1; break; case 'p': dprefix = arg; n++; break; } } printf("%s", banner); if (lit) copy_strlit(inds, varname, dprefix); else copy_chrarr(inds, varname, dprefix); return 0; } fungw-1.2.0/scconfig/src/util/arg_auto_menu.h0000644000175100017510000000054112661013253017374 0ustar svnsvn#include "util/arg_auto_set.h" /* An optional bridge between auto_set and menulib: create a submenu from an arg_auto_set_t table using regex key matching. */ void append_settings_auto_set(scm_menu_entry_t *me, int max, const arg_auto_set_t *as, const char *include, const char *exclude, const char *remove_prefix, int entry_type, void *entry_data); fungw-1.2.0/scconfig/src/util/arg_auto_menu.c0000644000175100017510000000441712661013253017375 0ustar svnsvn#include #include #include "regex.h" #include "libs.h" #include "db.h" #include "menulib/scmenu.h" #include "arg_auto_menu.h" static const char *get_entry_val(const arg_auto_set_t *as) { char *path; const char *v, *res; if (as->subvals == arg_lib_nodes) { path = str_concat("/", as->subtree, "presents", NULL); v = get(path); if ((v != NULL) && (strcmp(v, sfalse) == 0)) res = "disable"; else res = "enable"; free(path); return res; } } #define next_word(curr, next) \ do { \ next = strchr(curr, '|'); \ if (next != NULL) { \ *next = '\0'; \ next++; \ } \ } while(0) void append_settings_auto_set(scm_menu_entry_t *me, int max, const arg_auto_set_t *as, const char *include, const char *exclude, const char *remove_prefix, int entry_type, void *entry_data) { const arg_auto_set_t *a; scm_menu_entry_t *e; int numa, n, ei; char *accept; /* count number of all settings, allocate accept[] */ numa = 0; for(a = as; a->arg_key != NULL; a++) numa++; accept = calloc(numa, 1); /* mark entries included in accept[] */ if (include != NULL) { char *all = strclone(include), *next = all, *curr = all; do { next_word(curr, next); re_comp(curr); for(a = as, n = 0; a->arg_key != NULL; a++,n++) if (re_exec(a->arg_key)) accept[n] = 1; curr = next; } while((next != NULL) && (*next != '\0')); free(all); } else memset(accept, 1, numa); /* mark entries excluded in accept[] */ if (exclude != NULL) { char *all = strclone(exclude), *next = all, *curr = all; do { next_word(curr, next); re_comp(curr); for(a = as, n = 0; a->arg_key != NULL; a++,n++) if (re_exec(a->arg_key)) accept[n] = 0; curr = next; } while((next != NULL) && (*next != '\0')); free(all); } /* find the terminator */ for(e = me, ei = 0; e->key != SCM_TERMINATOR; e++, ei++) ; re_comp(remove_prefix); printf("exclude:\n"); for(n = 0; n < numa; n++) { if (accept[n]) { char *sd; if (re_exec(as[n].arg_key)) sd = re_subs_dup(""); else sd = strclone(as[n].arg_key); e->type = entry_type; e->key = sd; e->value = get_entry_val(&as[n]); e->user_data = &as[n]; e->auto_data = entry_data; e++; ei++; if (ei > max - 2) break; } } e->type = SCM_TERMINATOR; free(accept); } fungw-1.2.0/scconfig/src/util/sccbox.c0000644000175100017510000006336013764167433016052 0ustar svnsvn/* scconfig - sccbox: portable copy, link, mkdir, remove, etc. Copyright (C) 2016, 2017, 2020 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include #include #include #include #include #include #include /*********************** DOCUMENTATION **************************/ static void help_generic(const char *prg) { printf("sccbox - Makefile helper\n\n"); printf("Invocation: %s cmd [switches] [paths]\n\n", prg); printf("sccbox and provides a basic set of file-system utilities as a single,\n"); printf("static linkable executable with dependency only on libc. It is implemented\n"); printf("in C89 and uses only a very thin layer of non-C89 OS calls that trace\n"); printf("back to early BSD days and are widely available on all platforms.\n\n"); printf("The intention is to replace 'mkdir -p' and 'rm -f' and similar,\n"); printf("non-portable commands with a less efficient, but more portable local\n"); printf("implementation. Using sccbox for mkdir/install/uninstall exclusively also\n"); printf("simplifies the Makefile rules because the semantics of uninstall\n"); printf("matches the semantics of install and the same list of source files\n"); printf("and destination can be passed and source files are not removed.\n\n"); printf("For more info read the following help topics using --help topic:\n"); printf(" error error handling\n"); printf(" rm remove files (e.g. for make clean)\n"); printf(" mkdir create directories (e.g. for make *install)\n"); printf(" mktemp create a temporary file/directory\n"); printf(" ln create symlink (e.g. for make *install)\n"); printf(" install copy-install files (e.g. for make install)\n"); printf(" linstall symlink-install files (e.g. for make linstall)\n"); printf(" uninstall remove files (e.g. for make uninstall)\n"); printf(" touch touch files (open for append)\n"); printf(" touchnew touch non-existing files, ignore existing files\n"); printf("\n"); } static void help_error(const char *prg) { printf("sccbox - error handling (all commands)\n\n"); printf("Any sccbox command by default will try to carry out the requested\n"); printf("operation assuming no errors, e.g. an mkdir will assume the target\n"); printf("directory does not exists but all parent directories are in place.\n"); printf("If such a command fails, the exit status is non-zero and error messages\n"); printf("are printed to stderr.\n\n"); printf("If the --quiet switch is specified, the command will try to do the same\n"); printf("but never prints error messages and always returns 0. This is useful\n"); printf("in situations when failure is expected (note: 2>/dev/null is not portable)\n\n"); printf("If the --force switch is specified, the command will make extre efforts,\n"); printf("sometimes even destructive ones, to fulfill the request. If it fails,\n"); printf("error messages are printed (unless --quiet).\n\n"); } static void help_rm_common(void) { printf(" --quiet don't print error messages and set exit status to 0\n"); printf(" --force a non-existing file is not an error\n"); } static void help_rm(const char *prg) { printf("sccbox rm [switches] paths\n\n"); printf("Remove one or more paths from the file system. Paths can be\n"); printf("files, symlinks and empty directories. Recursive removal is not\n"); printf("supported. If multiple paths specified, attempt to remove them all\n"); printf("even if some can not be removed.\n\n"); printf("Switches:\n"); help_rm_common(); printf("\n"); } static void help_mkdir(const char *prg) { printf("sccbox mkdir [switches] paths\n\n"); printf("Create one or more directories.\n\n"); printf("Switches:\n"); printf(" --quiet don't print error messages and set exit status to 0\n"); printf(" --force ignored (there's nothing to force)\n"); printf(" -p create parent directories automatically\n"); printf(" -i ignored\n"); printf(" -l ignored\n"); printf(" -c ignored\n"); printf(" -u do not do anyhting, exit 0\n"); printf(" -r do not do anyhting, exit 0\n\n"); } static void help_mktemp(const char *prg) { printf("sccbox mktemp [switches]\n\n"); printf("Create a temporary file or directory and print its path to stdout\n\n"); printf("Switches:\n"); printf(" --quiet don't print error messages and set exit status to 0\n"); printf(" --force ignored (there's nothing to force)\n"); printf(" -d create a directory, not a file\n"); printf(" -p tmp path to tmpdir\n"); printf(" -t tpl use tpl as file name template\n\n"); } static void help_ln_common(void) { printf(" --quiet don't print error messages and set exit status to 0\n"); printf(" --force attempt to remove target if it exists\n"); } static void help_ln(const char *prg) { printf("sccbox ln [switches] existing new\n"); printf("sccbox ln [switches] existing1 existing2 ... dir\n\n"); printf("Create one or more symlinks. Intended for linking installed paths\n"); printf("to other installed paths.\n\n"); printf("Switches:\n"); help_ln_common(); printf(" --absolute convert existing paths to absolute paths\n\n"); printf(" --relative make single symlink point to relative path\n\n"); } static void help_install(const char *prg) { printf("sccbox install [switches] src_file dst_file\n"); printf("sccbox install [switches] src_file1 src_file2 ... dir\n\n"); printf("Copies (or links or removes) one or more files. Intended to install\n"); printf("files from the source or build tree to the installation root.\n\n"); printf("Switches:\n"); printf(" --quiet don't print error messages and set exit status to 0\n"); printf(" --force install: attempt to remove destination\n"); printf(" --absolute convert existing paths to absolute paths\n"); printf(" --relative make single symlink point to relative path\n\n"); printf(" -d force destination to be a directory (even remove old dest)\n"); printf(" -i install (copy) files\n"); printf(" -c install (copy) files\n"); printf(" -u uninstall (remove) files - see --help uninstall\n"); printf(" -r uninstall (remove) files - see --help uninstall\n"); printf(" -l linstall (symlink) files - see --help linstall\n\n"); } static void help_linstall(const char *prg) { printf("sccbox linstall [switches] src_file dst_file\n"); printf("sccbox linstall [switches] src_file1 src_file2 ... dir\n\n"); printf("Installs (a) file(s) using symlinks. Automatically convert src_file\n"); printf("paths to absolute paths. Intended for developers: if a package is\n"); printf("symlink-installed, it can be run from the installation after a\n"); printf("recompilation without needing to reinstall.\n\n"); printf("Switches:\n"); help_ln_common(); printf(" --preserve do not convert source paths to absolute\n\n"); } static void help_uninstall(const char *prg) { printf("sccbox uninstall [switches] src_file dst_file\n"); printf("sccbox uninstall [switches] src_file1 src_file2 ... dir\n\n"); printf("Remove (an) installed file(s). In the first form, src_file is ignored.\n"); printf("In the second form dir is used to calculate destination paths.\n\n"); printf("The purpose of this command is to unify install and uninstall rules:\n"); printf("unlike cp and rm, an 'sccbox install' and 'sccbox uninstall' can\n"); printf("be used with the same parameter list to install and uninstall a set of files\n\n"); printf("Switches:\n"); help_rm_common(); printf("\n"); } static void help_touch(const char *prg) { printf("sccbox touch file1 file2 ... fileN\n"); printf("sccbox touchnew file1 file2 ... fileN\n\n"); printf("Open all files listed, for append, and close them immedately.\n"); printf("This makes sure the files exist and also bumps their modification date.\n"); printf("Command touchnew ignores existing files; its primary use is to make\n"); printf("sure a file exists without making changes to already existing files.\n"); } static int help(const char *prg, const char *topic) { if (topic == NULL) help_generic(prg); else if (strcmp(topic, "error") == 0) help_error(prg); else if (strcmp(topic, "rm") == 0) help_rm(prg); else if (strcmp(topic, "mkdir") == 0) help_mkdir(prg); else if (strcmp(topic, "mktemp") == 0) help_mktemp(prg); else if (strcmp(topic, "ln") == 0) help_ln(prg); else if (strcmp(topic, "install") == 0) help_install(prg); else if (strcmp(topic, "linstall") == 0) help_linstall(prg); else if (strcmp(topic, "uninstall") == 0) help_uninstall(prg); else if (strcmp(topic, "touch") == 0) help_touch(prg); else if (strcmp(topic, "touchnew") == 0) help_touch(prg); else { printf("No such help topic: %s\n", topic); return 1; } return 0; } /*********************** IMPLEMENTATION: low level **************************/ #ifdef PATH_MAX # define MY_PATH_MAX PATH_MAX #else # define MY_PATH_MAX 32768 #endif typedef enum { INSTALL, /* install with cp */ LINSTALL, /* install with symlinks */ UNINSTALL /* remove installed files */ } scc_mode_t; #define kill_flag argv[n] = NULL #define kill_flag2 do { argv[n] = NULL; n++; argv[n] = NULL; } while(0) #define load_flags(code) \ do { \ int n, flags = 1; \ for(n = 1; n < argc; n++) { \ char *arg = argv[n]; \ char *payload = argv[n+1]; \ (void)payload; \ if ((*arg == '-') && (flags)) { \ if ((arg[1] == '-') && (arg[2] == '\0')) { \ kill_flag; \ flags = 0; \ continue; \ } \ while(*arg == '-') arg++; \ { code; } \ } \ } \ } while(0) \ #define load_args_minus(delta, code) \ do { \ int n; \ for(n = 1; n < argc-delta; n++) {\ char *arg = argv[n]; \ if (arg != NULL) \ { code; } \ } \ } while(0); #define load_args(code) load_args_minus(0, code)\ static int safe_isdir(const char *path) { struct stat st; if (stat(path, &st) != 0) return 0; return S_ISDIR(st.st_mode); } static int safe_isfile(const char *path) { struct stat st; if (stat(path, &st) != 0) return 0; return S_ISREG(st.st_mode); } static int safe_remove(const char *src, int need_existing_fd, int quiet) { struct stat st; int exists; if (quiet) return remove(src); exists = stat(src, &st) == 0; if ((need_existing_fd) && (!exists)) { fprintf(stderr, "sccbox: Can't remove %s: no such file or directory\n", src); return 1; } if (exists && (remove(src) != 0)) { perror("sccbox"); fprintf(stderr, "sccbox: Can't remove %s\n", src); return 1; } return 0; } #define issep(chr) (((chr) == '/') || ((chr) == '\\')) static char *my_basename(const char *path) { const char *s = path + strlen(path); for(s--; s >= path; s--) { if (issep(*s)) return (char *)s+1; } return (char *)path; } static int path_concat(char *out, size_t out_len, const char *dir, const char *file) { char *bn = my_basename(file); size_t dlen = strlen(dir), blen = strlen(bn); if (dlen+blen+2 > out_len) return 1; memcpy(out, dir, dlen); out[dlen] = '/'; dlen++; memcpy(out+dlen, bn, blen+1); return 0; } static int safe_mkdir_p(const char *path); static int safe_copy(const char *src, const char *dst, int force, int quiet, int dst_is_dir) { char buff[16384], dst2[MY_PATH_MAX]; const char *dst_name = NULL; FILE *fd, *fs; struct stat st, dst_st; int err = 0, remove_dst = 1; if (stat(src, &st) != 0) { if (!quiet) fprintf(stderr, "sccbox: can't stat %s\n", src); return 1; } /* make sure symlinks/sockets/etc. are not copied into: remove any dst that's not a dir or a regular file */ if (stat(dst, &dst_st) == 0) { int try_remove = dst_is_dir ? (!S_ISDIR(dst_st.st_mode)) : (!S_ISDIR(dst_st.st_mode) || !S_ISREG(dst_st.st_mode)); if (try_remove) { if (remove(dst) != 0) { if (!quiet) fprintf(stderr, "sccbox: filed to remove non-regular/non-directory target %s\n", dst); return 1; } } } if (dst_is_dir) { remove_dst = 0; if (safe_mkdir_p(dst) != 0) { if (!quiet) fprintf(stderr, "sccbox: filed to create target directory %s\n", dst); return 1; } } fs = fopen(src, "rb"); if (fs == NULL) { if (!quiet) fprintf(stderr, "sccbox: can't copy %s to %s: can not open source for write\n", src, dst2); return 1; } if ((force) && (remove_dst)) remove(dst); fd = fopen(dst, "wb"); if (fd == NULL) { /* no luck opening the dest file, maybe dst is a directory, try to create a file under it */ if (path_concat(dst2, sizeof(dst2), dst, src) != 0) { if (!quiet) fprintf(stderr, "sccbox: can't copy %s to %s: resulting path is too long\n", src, dst); fclose(fs); return 1; } fd = fopen(dst2, "wb"); if ((fd == NULL) && (force)) { remove(dst2); fd = fopen(dst2, "wb"); if (fd == NULL) { /* dest was not a directory or couldn't host our new file; if force, try to overwrite whatever dst was */ remove(dst); fd = fopen(dst, "wb"); if (fd == NULL) { if (!quiet) fprintf(stderr, "sccbox: can't copy %s to %s: destination can not be overwritten\n", src, dst2); fclose(fs); return 1; } dst_name = dst; } } if (fd == NULL) { if (!quiet) fprintf(stderr, "sccbox: can't copy %s to %s: can not open destination for write\n", src, dst2); fclose(fs); return 1; } if (dst_name == NULL) dst_name = dst2; } else dst_name = dst; /* manual copy - the only portable way */ for(;;) { size_t len = fread(buff, 1, sizeof(buff), fs); if (len == 0) break; if (fwrite(buff, 1, len, fd) != len) { if (!quiet) { perror("sccbox"); fprintf(stderr, "sccbox: can't copy %s to %s\n", src, dst_name); } err = 1; if (!force) break; } } chmod(dst_name, st.st_mode); /* this may fail on windows or on strange FS, don't check the return value */ fclose(fs); fclose(fd); return err; } static const char *safe_get_pwd(int quiet) { static char pwd_buff[MY_PATH_MAX]; static int valid = 0; if (!valid) { FILE *f; char *end; f = popen("pwd", "r"); if (f == NULL) { if (!quiet) perror("sccbox: running pwd"); return NULL; } if (fgets(pwd_buff, sizeof(pwd_buff), f) == NULL) { if (!quiet) perror("sccbox: reading pwd's output"); pclose(f); return NULL; } pclose(f); end = pwd_buff + strlen(pwd_buff) - 1; while((end >= pwd_buff) && ((*end == '/') || (*end == '\n') || (*end == '\r'))) { *end = '\0'; end--; } if (*pwd_buff != '/') { if (!quiet) fprintf(stderr, "sccbox: invalid pwd: '%s'\n", pwd_buff); return NULL; } valid = 1; } return pwd_buff; } /* Both 'path' and 'from' are absolute paths; recalculate path to be a relative path from 'from' and copy the result to buf. Returns buf. */ char *relativize(char *buf, const char *path, const char *from) { const char *p, *f, *c, *lastp, *lastf; char *end = buf; int cnt = 0, rem = MY_PATH_MAX-1, len; for(lastp = p = path, lastf = f = from; *p == *f; p++,f++) { /* skip double separators */ if (issep(p[0])) { while(issep(p[0]) && issep(p[1])) p++; lastp = p+1; } if (issep(f[0])) { while(issep(f[0]) && issep(f[1])) f++; lastf = f+1; } } p = lastp; f = lastf; for(c = f; *c != '\0'; c++) { if (issep(c[0])) { cnt++; while(issep(c[0]) && issep(c[1])) c++; } } while(cnt > 0) { if (rem < 3) return NULL; strcpy(end, "../"); end += 3; rem -= 3; cnt--; } len = strlen(p); if (len >= rem) return NULL; strcpy(end, p); return buf; } /* create a symlink - needs to work only on UNIX but needs to compile on win32, so use system("ln -s") and avoid link-related syscalls */ static int safe_link(const char *src, const char *dst, int absolute, int relative, int force, int quiet, int dst_is_dir) { char cmd[MY_PATH_MAX*2+16], full_src_tmp[MY_PATH_MAX], rel_src_tmp[MY_PATH_MAX]; const char *full_src, *supp; int remove_dst = 1; if ((absolute) && (*src != '/')) { /* fix up relative source paths */ const char *pwd = safe_get_pwd(quiet); int pwdlen, srclen; if (pwd == NULL) { if (!quiet) fprintf(stderr, "sccbox: can't figure current working directory for relative symlink %s to %s\n", src, dst); return 1; } if ((src[0] == '.') && (src[1] == '/')) src += 2; pwdlen = strlen(pwd); srclen = strlen(src); if ((pwdlen + srclen + 2) >= sizeof(full_src_tmp)) { if (!quiet) fprintf(stderr, "sccbox: can't link %s to %s: resulting path is too long\n", src, dst); return 1; } memcpy(full_src_tmp, pwd, pwdlen); full_src_tmp[pwdlen] = '/'; memcpy(full_src_tmp+pwdlen+1, src, srclen+1); full_src = full_src_tmp; } else full_src = src; if (dst_is_dir) { struct stat dst_st; if ((stat(dst, &dst_st) == 0) && (!S_ISDIR(dst_st.st_mode))) { if (remove(dst) != 0) { if (!quiet) fprintf(stderr, "sccbox: filed to remove non-directory target %s\n", dst); return 1; } } remove_dst = 0; if (safe_mkdir_p(dst) != 0) { if (!quiet) fprintf(stderr, "sccbox: filed to create target directory %s\n", dst); return 1; } } if (force) remove(dst); supp = quiet ? " 2>/dev/null" : ""; if (relative) { const char *fdst; char full_dst_tmp[MY_PATH_MAX]; if (*dst != '/') { const char *pwd = safe_get_pwd(quiet); if (pwd == NULL) { if (!quiet) fprintf(stderr, "sccbox: can't figure current working directory for relative path '%s'\n", dst); return 1; } if (path_concat(full_dst_tmp, sizeof(full_dst_tmp), pwd, dst) != 0) { if (!quiet) fprintf(stderr, "sccbox: can't link %s to %s: resulting path is too long\n", src, dst); return 1; } fdst = full_dst_tmp; } else fdst = dst; full_src = relativize(rel_src_tmp, full_src, fdst); } sprintf(cmd, "ln -s \"%s\" \"%s\"%s", full_src, dst, supp); return system(cmd); } static int safe_mkdir_p(const char *path) { char *curr, *next, *s; int res = 1, len = strlen(path); char *p = malloc(len+1); memcpy(p, path, len+1); /* do not create existing directories */ if (safe_isdir(path)) return 0; curr = p; if ((isalpha(p[0]) && (p[1] == ':') && issep(p[2]))) { /* windows special case: c:\ */ curr += 3; } /* remove trailing path separators so we don't create empty dirs at the end */ s = p+len-1; while((s >= p) && (issep(*s))) { *s = '\0'; s--; } for(next = curr; next != NULL; curr = next) { char old; next = strpbrk(curr, "/\\"); if (next != NULL) { old = *next; *next = '\0'; } res = mkdir(p, 0755); if (next != NULL) { *next = old; next++; } } if (res != 0) { perror("sccbox"); fprintf(stderr, "sccbox: failed to make directory with parents: %s\n", path); } free(p); return res; /* return the result of the last mkdir only, previous failures probably meant existing parents */ } static int safe_touch(const char *fn) { FILE *f; f = fopen(fn, "a"); if (f == NULL) return -1; fclose(f); return 0; } /*********************** IMPLEMENTATION: high level **************************/ int cmd_rm(int argc, char *argv[]) { int err = 0, quiet = 0, need_existing_fd = 1; load_flags( switch(*arg) { case 'f': need_existing_fd = 0; kill_flag; break; /* nop: can't do more than remove() */ case 'q': quiet = 1; kill_flag; break; } ); load_args( err |= safe_remove(arg, need_existing_fd, quiet); ); if (quiet) return 0; return err; } int cmd_install(int argc, char *argv[], scc_mode_t mode, int absolute) { int force = 0, err = 0, quiet = 0, dst_is_dir, force_dst_dir = 0, relative = 0; const char *last; load_flags( if ((arg[0] == 'r') && (arg[1] == 'e')) arg[0] = 'R'; /* --relative */ switch(*arg) { case 'c': /* --copy */ case 'i': /* --install */ mode = INSTALL; kill_flag; break; case 'l': /* --ln or --linstall */ mode = LINSTALL; kill_flag; break; case 'r': /* --rm */ case 'u': /* --uninstall */ mode = UNINSTALL; kill_flag; break; case 'f': /* --force */ force = 1; kill_flag; break; case 'd': /* --directory */ force_dst_dir = 1; kill_flag; break; case 'q': /* --quiet */ quiet = 1; kill_flag; break; case 'p': /* --preserve */ absolute = 0; kill_flag; break; case 'a': /* --absolute */ absolute = 1; kill_flag; break; case 'R': /* --relative */ relative = 1; kill_flag; break; } ); last = argv[argc-1]; dst_is_dir = safe_isdir(last); load_args_minus(1, /*printf("arg=%s last=%s force=%d q=%d d=%d\n", arg, last, force, quiet, force_dst_dir);*/ switch(mode) { case INSTALL: err |= safe_copy(arg, last, force, quiet, force_dst_dir); break; case LINSTALL: err |= safe_link(arg, last, absolute, relative, force, quiet, force_dst_dir); break; case UNINSTALL: if (dst_is_dir) { char path[MY_PATH_MAX]; if (path_concat(path, sizeof(path), last, arg) != 0) { if (!quiet) { fprintf(stderr, "sccbox: can't remove %s/%s: resulting path is too long\n", last, arg); err |= 1; } } else err |= safe_remove(path, !force, quiet); } break; } if ((mode == UNINSTALL) && (!dst_is_dir)) err |= safe_remove(last, !force, quiet); ); if (quiet) return 0; return err; } int cmd_mkdir(int argc, char *argv[]) { int parents = 0, err = 0, quiet = 0; load_flags( switch(*arg) { case 'q': /* --quiet */ quiet = 1; kill_flag; break; case 'f': kill_flag; break; case 'i': case 'l': case 'c': kill_flag; break; /* ignore these for compatibility with install */ case 'r': /* --rm */ case 'u': /* --uninstall */ return 0; /* don't do anything for uninstall */ case 'p': parents = 1; kill_flag; break; } ); if (!parents) { load_args( if ((mkdir(arg, 0755) != 0) && (!quiet)) { perror("sccbox"); fprintf(stderr, "sccbox: failed to make directory %s\n", arg); err |= 1; } ) } else { load_args( err |= safe_mkdir_p(arg); ); } if (quiet) return 0; return err; } int rand_chr(void) { static int seeded = 0; static const char map[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; if (!seeded) { FILE *f; long seed; f = fopen("/dev/urandom", "rb"); if (f != NULL) { int n; for(n = 0; n < 4; n++) { seed <<= 8; seed |= fgetc(f); } fclose(f); } else seed = time(NULL); srand(seed); seeded = 1; } return map[(int)((double)rand() / (double)RAND_MAX * (sizeof(map)-1))]; } int cmd_mktemp(int argc, char *argv[]) { int want_dir = 0, err = 0, quiet = 0, retry; const char *template = "scctmp.^^^^^^^^"; const char *tmpdir = "."; /* assume . is the safe place to create temp files the unsafe way */ char path[MY_PATH_MAX]; load_flags( switch(*arg) { case 'q': /* --quiet */ quiet = 1; kill_flag; break; case 'd': /* --quiet */ want_dir = 1; kill_flag; break; case 't': /* --template */ template = payload; kill_flag2; break; case 'p': /* --path */ tmpdir = payload; kill_flag2; break; case 'f': kill_flag; break; /* ignore */ } ); for(retry = 0; retry < 8; retry++) { int dlen = strlen(tmpdir), rc = 0; const char *t; char *end; /* generate a path */ memcpy(path, tmpdir, dlen); end = path+dlen; for(*end++ = '/', t = template; *t != '\0'; end++, t++) { if (*t == '^') { *end = rand_chr(); rc++; } else *end = *t; } while(rc < 8) { /* make sure there are at least 8 random characters */ *end++ = rand_chr(); rc++; } *end = '\0'; if (want_dir) { if (mkdir(path, 0755) == 0) { printf("%s\n", path); return 0; } } else { FILE *f = fopen(path, "w"); if (f != NULL) { fclose(f); printf("%s\n", path); return 0; } } } if (quiet) return 0; fprintf(stderr, "sccbox: failed to create temporary %s\n", want_dir ? "directory" : "file"); return err; } int cmd_touch(int argc, char *argv[], int only_new) { int n, res = 0; for(n = 1; n < argc; n++) { if ((only_new) && (safe_isfile(argv[n]))) continue; /* skip existing file in touchnew */ if (safe_touch(argv[n]) != 0) { res = 1; fprintf(stderr, "sccbox: failed to touch %s\n", argv[n]); } } return res; } int main(int argc, char *argv[]) { const char *prg = argv[0]; if (argc > 1) { if (strstr(argv[0], "sccbox") != NULL) { argv++; argc--; } if (strcmp(argv[0], "--help") == 0) return help(prg, argv[1]); if (strcmp(argv[0], "help") == 0) return help(prg, argv[1]); if (strcmp(argv[0], "-h") == 0) return help(prg, argv[1]); if (strcmp(argv[0], "rm") == 0) return cmd_rm(argc, argv); if (strcmp(argv[0], "install") == 0) return cmd_install(argc, argv, INSTALL, 1); if (strcmp(argv[0], "linstall") == 0) return cmd_install(argc, argv, LINSTALL, 1); if (strcmp(argv[0], "uninstall") == 0) return cmd_install(argc, argv, UNINSTALL, 1); if (strcmp(argv[0], "mkdir") == 0) return cmd_mkdir(argc, argv); if (strcmp(argv[0], "mktemp") == 0) return cmd_mktemp(argc, argv); if (strcmp(argv[0], "ln") == 0) return cmd_install(argc, argv, LINSTALL, 0); if (strcmp(argv[0], "touch") == 0) return cmd_touch(argc, argv, 0); if (strcmp(argv[0], "touchnew") == 0) return cmd_touch(argc, argv, 1); fprintf(stderr, "sccbox: unknown command %s\n", argv[0]); } else fprintf(stderr, "sccbox: need arguments\n"); fprintf(stderr, "sccbox: try --help\n"); return 1; } fungw-1.2.0/scconfig/src/util/Makefile0000644000175100017510000000015513031705302016032 0ustar svnsvn#CC=i586-mingw32msvc-gcc #CFLAGS = -Wall -g sccbox: sccbox.c Makefile $(CC) $(CFLAGS) sccbox.c -o sccbox fungw-1.2.0/scconfig/src/util/arg_auto_set.h0000644000175100017510000000401512750535606017235 0ustar svnsvn#ifndef ARG_AUTO_SET_H #define ARG_AUTO_SET_H /* Handle a list of --disable-LIBs automatically. The user sets up a table like: static const arg_auto_set_t disable_libs[] = { {"disable-gtk", "libs/gui/gtk2", arg_lib_nodes}, {"disable-lesstif", "libs/gui/lesstif2", arg_lib_nodes}, {NULL, NULL, NULL} }; and at the end of hook_custom_arg() executes: return arg_auto_set(key, value, disable_libs); The call will set all nodes listed in arg_lib_nodes to disable gtk or lesstif. Mechanism: this happens before require()s on these nodes; require() will find them already set and won't run the detection. Thus it is suitable for disabling features (but not for enabling them). */ /* A table of node name-values to be set under a subtree */ typedef struct { const char *name; const char *value; } arg_auto_set_node_t; /* A table of argument->subtree->subtree_values */ typedef struct { const char *arg_key; /* command line argument without the -- prefix */ const char *subtree; /* subtree path affected, e.g. libs/gui/gtk2 */ const arg_auto_set_node_t *subvals; /* a set of values to be put() under the subtree */ const char *help_txt; } arg_auto_set_t; /* node set table for resetting the usual nodes under a library subtree: presents, cflags, ldflags */ extern const arg_auto_set_node_t arg_lib_nodes[]; /* set the node true or false */ extern const arg_auto_set_node_t arg_true[]; extern const arg_auto_set_node_t arg_false[]; /* Execute table: find a match on key and set all subvals of the match */ int arg_auto_set(const char *key, const char *value, const arg_auto_set_t *table); /* Print options help from the table, one entry per line; if help text starts with $, replace that with --arg_key and insert padding after that; padding should be a string filled with spaces, as long as the longest argument key plus the separator spaces. */ void arg_auto_print_options(FILE *fout, const char *line_prefix, const char *padding, const arg_auto_set_t *table); #endif fungw-1.2.0/scconfig/src/scripts/0000755000175100017510000000000014047742764015130 5ustar svnsvnfungw-1.2.0/scconfig/src/scripts/scripts.h0000644000175100017510000000354414036737442016771 0ustar svnsvn#include #include #include #include "libs.h" #include "log.h" #include "db.h" #include "dep.h" int brute_force_include(int logdepth, const char *language, const char *test_c, const char *ldflags_base, const char *basedir); /* script detection */ int find_script_gpmi(const char *name, int logdepth, int fatal); int find_script_tcl(const char *name, int logdepth, int fatal); int find_script_tk(const char *name, int logdepth, int fatal); int find_script_ruby(const char *name, int logdepth, int fatal); int find_script_mruby(const char *name, int logdepth, int fatal); int find_script_python(const char *name, int logdepth, int fatal); int find_script_python3(const char *name, int logdepth, int fatal); int find_script_perl(const char *name, int logdepth, int fatal); int find_script_mawk(const char *name, int logdepth, int fatal); int find_script_lua(const char *name, int logdepth, int fatal); int find_script_guile(const char *name, int logdepth, int fatal); int find_script_stutter(const char *name, int logdepth, int fatal); int find_script_estutter(const char *name, int logdepth, int fatal); int find_script_funlisp(const char *name, int logdepth, int fatal); int find_script_duktape(const char *name, int logdepth, int fatal); int find_script_mujs(const char *name, int logdepth, int fatal); int find_script_fungw(const char *name, int logdepth, int fatal); int find_script_fungw_user_call_ctx(const char *name, int logdepth, int fatal); int find_script_fungw_cfg_pupdir(const char *name, int logdepth, int fatal); int find_script_fungw_all(const char *name, int logdepth, int fatal); int find_script_m4(const char *name, int logdepth, int fatal); int find_script_welltype(const char *name, int logdepth, int fatal); int find_script_wtc(const char *name, int logdepth, int fatal); int find_script_picol(const char *name, int logdepth, int fatal); fungw-1.2.0/scconfig/src/scripts/find_tcl.c0000644000175100017510000001654314003561707017053 0ustar svnsvn/* scconfig - tcl lib detection Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include static int all_vers[] = { 86, 85, 84, 0, -1 }; int find_script_tcl_(const char *name, int logdepth, int fatal, int *vers, int fallback) { char *out, *temp, *temp2, *cmd, *I, *L, *end, **tclsh; int *v; char *tclshs[] = { "tclsh", "tclsh86", "tclsh85", "tclsh84", "tclsh8.6", "tclsh8.5", "tclsh8.4", NULL }; char *test_c = NL "#include " NL "#include " NL "int main() {" NL " Tcl_Obj *res;" NL " Tcl_Interp *interp;" NL " interp = Tcl_CreateInterp();" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; char *tcl_config = NL "proc tclConfigFile {} {" NL " set d [info library]" NL " set f [file join $d \"tclConfig.sh\"]" NL " if {[file exists $f]} {return $f}" NL "" NL " set d [file dirname $d]" NL " set f [file join $d \"tclConfig.sh\"]" NL " if {[file exists $f]} {return $f}" NL "" NL " set d [file dirname $d]" NL " set f [file join $d \"tclConfig.sh\"]" NL " if {[file exists $f]} {return $f}" NL "" NL " set d [file dirname $d]" NL " set f [file join $d \"tclConfig.sh\"]" NL " if {[file exists $f]} {return $f}" NL "}" NL "" NL "puts [tclConfigFile]" NL; /* Look at some standard places */ for(v = vers; *v != -1; v++) { int major = *v / 10, minor = *v % 10; char ifl[64], lfl[64]; if (*v == 0) { report("plain... "); if (try_icl(logdepth, "libs/script/tcl", test_c, NULL, NULL, "-ltcl")) return 0; continue; } report("%d.%d... ", major, minor); sprintf(lfl, "-ltcl%d", *v); if (try_icl(logdepth, "libs/script/tcl", test_c, NULL, NULL, lfl)) return 0; sprintf(lfl, "-ltcl%d.%d", major, minor); if (try_icl(logdepth, "libs/script/tcl", test_c, NULL, NULL, lfl)) return 0; sprintf(lfl, "-ltcl%d", *v); sprintf(ifl, "-I/usr/include/tcl%d", *v); if (try_icl(logdepth, "libs/script/tcl", test_c, NULL, ifl, lfl)) return 0; sprintf(lfl, "-ltcl%d.%d", major, minor); sprintf(ifl, "-I/usr/include/tcl%d.%d", major, minor); if (try_icl(logdepth, "libs/script/tcl", test_c, NULL, ifl, lfl)) return 0; } if (!fallback) return 1; report(" not found by version, trying tclsh.\n"); /* Try the config script */ logprintf(logdepth, "running config tcl\n"); temp = tempfile_dump(tcl_config, ".tcl"); cmd = malloc(strlen(temp) + 16); for(tclsh = tclshs; *tclsh != NULL; tclsh++) { sprintf(cmd, "%s %s", *tclsh, temp); report("Trying: %s\n", cmd); if (run(logdepth+1, cmd, &out) == 0) { free(cmd); cmd = malloc(strlen(out) + 256); sprintf(cmd, "#!/bin/sh\n. %s\necho $TCL_INCLUDE_SPEC\necho $TCL_LIB_SPEC\n", out); temp2 = tempfile_dump(cmd, ".sh"); free(out); out = malloc(strlen(temp2) + 32); sprintf(out, "chmod +x %s", temp2); system(out); free(out); if (run(logdepth+1, temp2, &out) == 0) { remove(temp2); I = out; L = strchr(I, '\n'); if (L != NULL) { *L = '\0'; L++; end = strchr(L, '\n'); if (end != NULL) *end = '\0'; } if (try_icl(logdepth, "libs/script/tcl", test_c, NULL, I, L)) { remove(temp); free(cmd); if (out != NULL) free(out); return 0; } } remove(temp2); } } remove(temp); free(cmd); if (out != NULL) free(out); return 1; } int find_script_tcl(const char *name, int logdepth, int fatal) { int res, rver[2], *vers = all_vers, fallback = 1; const char *reqver; require("cc/cc", logdepth, fatal); report("Checking for tcl... "); logprintf(logdepth, "find_tcl: trying to find tcl...\n"); logdepth++; reqver = get("/arg/tcl-version"); if ((reqver != NULL) && (reqver[0] != '\0')) { if (reqver[1] == '.') rver[0] = (reqver[0] - '0') * 10 + (reqver[2] - '0'); else rver[0] = atoi(reqver); rver[1] = -1; vers = rver; fallback = 0; } res = find_script_tcl_(name, logdepth, fatal, vers, fallback); if (res == 0) return 0; return try_fail(logdepth, "libs/script/tcl"); } static int try_icl_tk(int logdepth, const char *prefix, const char *test_c_in, const char *includes, const char *cflags, const char *ldflags, int ver) { const char *XL = get("libs/gui/xopendisplay/Lflags"); const char *Xc = get("libs/gui/xopendisplay/cflags"); const char *tl, *tc; char *tmpl, *tmpc; int res; tl = get("libs/script/tcl/ldflags"); tc = get("libs/script/tcl/cflags"); if (XL == NULL) XL = ""; if (Xc == NULL) Xc = ""; if (tl == NULL) tl = ""; if (tc == NULL) tc = ""; if ((*XL == '\0') && (*Xc == '\0') && (*tl == '\0') && (*tc == '\0')) return try_icl(logdepth, prefix, test_c_in, includes, cflags, ldflags); tmpl = str_concat(" ", tl, XL, ldflags, NULL); tmpc = str_concat(" ", tc, Xc, cflags, NULL); res = try_icl(logdepth, prefix, test_c_in, includes, tmpc, tmpl); free(tmpl); free(tmpc); return res; } int find_script_tk(const char *name, int logdepth, int fatal) { int *v, rver[2], *vers = all_vers; const char *reqver, *tclreqver; char *test_c = NL "#include " NL "#include " NL "int main() {" NL " if (Tk_GetNumMainWindows() == 0)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); require("libs/gui/xopendisplay/cflags", logdepth, fatal); require("libs/gui/xopendisplay/Lflags", logdepth, fatal); report("Checking for tk... "); logprintf(logdepth, "find_tk: trying to find tk...\n"); logdepth++; reqver = get("/arg/tk-version"); if ((reqver != NULL) && (reqver[0] != '\0')) { tclreqver = get("/arg/tcl-version"); if ((tclreqver != NULL) && (tclreqver[0] != '\0')) { if (strcmp(tclreqver, reqver) != 0) { report("\n\nFatal argument error: if both tcl and tk version are forced, they must match\n\n"); exit(1); } } if (reqver[1] == '.') rver[0] = (reqver[0] - '0') * 10 + (reqver[2] - '0'); else rver[0] = atoi(reqver); rver[1] = -1; vers = rver; } /* Look at some standard places, per version */ for(v = vers; *v != -1; v++) { int major = *v / 10, minor = *v % 10; char lfl[64]; int tvers[2]; tvers[0] = *v; tvers[1] = -1; if (find_script_tcl_("libs/script/tcl", logdepth+1, 0, tvers, 0) != 0) { report("(no tcl) "); continue; } if (*v == 0) { if (try_icl_tk(logdepth, "libs/script/tk", test_c, NULL, NULL, "-ltk", 0)) return 0; continue; } sprintf(lfl, "-ltk%d", *v); if (try_icl_tk(logdepth, "libs/script/tk", test_c, NULL, NULL, lfl, *v)) return 0; sprintf(lfl, "-ltk%d.%d", major, minor); if (try_icl_tk(logdepth, "libs/script/tk", test_c, NULL, NULL, lfl, *v)) return 0; } return try_fail(logdepth, "libs/script/tk"); } fungw-1.2.0/scconfig/src/scripts/find_welltype.c0000644000175100017510000000544214032275224020130 0ustar svnsvn/* scconfig - welltype lib detection Copyright (C) 2021 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include #define SAVE_VER(name) \ next = ver; \ while(*next != ' ') next++; \ *next = '\0'; \ put("libs/script/welltype/ver/" name, ver); \ ver = next + 1; \ while(*ver == ' ') ver++; static int accept_wt_res(char *ver) { char *next; if (!strncmp(ver, "OK ", 3) == 0) return 0; ver += 3; SAVE_VER("compacted"); SAVE_VER("major"); SAVE_VER("minor"); SAVE_VER("build"); SAVE_VER("revsn"); return 1; } int find_script_welltype(const char *name, int logdepth, int fatal) { /* The test script compares runtime lib version to compile time header version and accepts proposed compilation flags only if they match */ char *test_c = NL "#include " NL "#include " NL "int main() {" NL " wt_u32 ver = wtrGetSuiteVersion();" NL " if (ver == WTR_VERSION)" NL " printf(\"OK %lu %d %d %d %d \", ver, WT_GETMAJOR(ver), WT_GETMINOR(ver), WT_GETBUILD(ver), WT_GETREVSN(ver));" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for welltype... "); logprintf(logdepth, "find_welltype: trying to find welltype...\n"); logdepth++; /* Look at some standard places */ if (try_icl_(logdepth, "libs/script/welltype", test_c, NULL, NULL, "-lwtruntime", 1, accept_wt_res)) return 0; return try_fail(logdepth, "libs/script/welltype"); } int find_script_wtc(const char *name, int logdepth, int fatal) { char cmd[] = "wtc --version"; char *out = NULL; int ret = 1; report("Checking for welltype wtc... "); logprintf(logdepth, "find_welltype wtc: trying to find welltype wtc...\n"); logdepth++; run_shell(logdepth, cmd, &out); if ((out != NULL) && (strncmp(out, "Welltype compiler v", 19) == 0)) { put("libs/script/welltype/wtc/presents", strue); put("libs/script/welltype/wtc/cmd", "wtc"); report("OK (wtc)\n"); ret = 0; } else report("not found\n"); free(out); return ret; } fungw-1.2.0/scconfig/src/scripts/find_perl.c0000644000175100017510000000463614003561707017233 0ustar svnsvn/* scconfig - perl lib detection Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" int find_script_perl(const char *name, int logdepth, int fatal) { char *cflags, *ldflags, *s; int res; char *test_c = NL "#include " NL "#include " NL "#include " NL "int main() {" NL " PerlInterpreter *interp;" NL NL " interp = perl_alloc();" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("sys/class", logdepth, fatal); require("cc/cc", logdepth, fatal); require("/internal/filelist/method", logdepth, fatal); report("Checking for perl... "); logprintf(logdepth, "find_perl: trying to find perl...\n"); logdepth++; res = run(logdepth, "perl -MExtUtils::Embed -e ccopts", &cflags); if (res) { logprintf(logdepth, "perl executable not found or broken (%d) at cflags\n", res); report("FAILED (perl exec fail)\n"); return 1; } res = run(logdepth, "perl -MExtUtils::Embed -e ldopts", &ldflags); if (res) { logprintf(logdepth, "perl executable not found or broken (%d) aat ldflags\n", res); report("FAILED (perl exec fail)\n"); free(cflags); return 1; } /* workarounds for windows [TODO] */ if (strcmp(get("sys/class"), "win32") == 0) { for(s = cflags; *s != '\0'; s++) if (*s == '\\') *s = '/'; for(s = ldflags; *s != '\0'; s++) if (*s == '\\') *s = '/'; /* TODO: do we need to remove double quotes as well? */ } res = try_icl(logdepth, "libs/script/perl", test_c, NULL, strip(cflags), strip(ldflags)); free(cflags); free(ldflags); if (res) return 0; return try_fail(logdepth, "libs/script/perl"); } fungw-1.2.0/scconfig/src/scripts/find_mujs.c0000644000175100017510000000305314036737442017246 0ustar svnsvn/* scconfig - mujs lib detection Copyright (C) 2021 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include int find_script_mujs(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " js_State *J = js_newstate(NULL, NULL, JS_STRICT);\n" NL " if (J != NULL)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for mujs... "); logprintf(logdepth, "find_mujs: trying to find mujs...\n"); logdepth++; /* Look at the standard place */ if (try_icl(logdepth, "libs/script/mujs", test_c, NULL, NULL, "-lmujs -lm")) return 0; return try_fail(logdepth, "libs/script/mujs"); } fungw-1.2.0/scconfig/src/scripts/find_stutter.c0000644000175100017510000000521614003561707017776 0ustar svnsvn/* scconfig - stutter lib detection Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include int find_script_stutter(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " varctx *global;" NL " global = varctx_create(NULL, 256);" NL NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for stutter... "); logprintf(logdepth, "find_stutter: trying to find stutter...\n"); logdepth++; /* TODO: do we need -ldl? */ if ( try_icl(logdepth, "libs/script/stutter", test_c, NULL, NULL, "-lstutter") || try_icl(logdepth, "libs/script/stutter", test_c, NULL, "-I/usr/include/stutter", "-lstutter") || try_icl(logdepth, "libs/script/stutter", test_c, NULL, "-I/usr/local/include/stutter", "-lstutter") ) return 0; return try_fail(logdepth, "libs/script/stutter"); } int find_script_estutter(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " stt_ctx_t stt;" NL " stt.next_serial = 4242;" NL " stt_init(&stt);" NL " if (stt.next_serial != 4242)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for estutter... "); logprintf(logdepth, "find_estutter: trying to find estutter...\n"); logdepth++; /* TODO: do we need -ldl? */ if ( try_icl(logdepth, "libs/script/estutter", test_c, NULL, NULL, "-lestutter") || try_icl(logdepth, "libs/script/estutter", test_c, NULL, "-I/usr/include/estutter", "-lestutter") || try_icl(logdepth, "libs/script/estutter", test_c, NULL, "-I/usr/local/include/estutter", "-lestutter") ) return 0; return try_fail(logdepth, "libs/script/estutter"); } fungw-1.2.0/scconfig/src/scripts/find_gpmi.c0000644000175100017510000000517413240634424017222 0ustar svnsvn/* scconfig - libgpmi detection Copyright (C) 2015 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include int find_script_gpmi(const char *name, int logdepth, int fatal) { char *out, *cmd, *end, **cfg; const char *usr; char *cfgs[] = { NULL, "gpmi-config", "/usr/bin/gpmi-config", "/usr/local/bin/gpmi-config", "~/bin/gpmi-config", NULL }; (void) fatal; /* not used */ report("Checking for gpmi... "); logprintf(logdepth, "find_gpmi: trying to find gpmi...\n"); logdepth++; /* Try the config script */ logprintf(logdepth, "running config tcl\n"); cmd = malloc(128); usr = get("/arg/gpmi/prefix"); if (usr != NULL) { const char *rp1 = NULL; require("cc/wlrpath", logdepth, 0); rp1 = get("cc/wlrpath"); cfgs[0] = str_concat("/", usr, "bin/gpmi-config", NULL); put("libs/script/gpmi/ldflags", str_concat("", "-L", usr, "/lib", " -lgpmi ", rp1, usr, "/lib", NULL)); put("libs/script/gpmi/cflags", str_concat("", "-I", usr, "/include", NULL)); cfg = cfgs; } else { cfg = cfgs+1; put("libs/script/gpmi/cflags", ""); put("libs/script/gpmi/ldflags", "-lgpmi"); } for(; *cfg != NULL; cfg++) { sprintf(cmd, "%s --version", *cfg); if (run(logdepth+1, cmd, &out) == 0) { put("libs/script/gpmi/gpmi-config", *cfg); put("libs/script/gpmi/presents", strue); end = strrchr(out, ' '); if (end != NULL) put("libs/script/gpmi/version", strip(end)); free(out); sprintf(cmd, "%s --id", *cfg); if (run(logdepth+1, cmd, &out) == 0) { end = strrchr(out, ' '); if (end != NULL) put("libs/script/gpmi/configapi", strip(end)); free(out); } free(cmd); if (cfgs[0] != NULL) free(cfgs[0]); return 0; } } free(cmd); if (out != NULL) free(out); if (cfg[0] != NULL) free(cfg[0]); return try_fail(logdepth, "libs/script/gpmi"); } fungw-1.2.0/scconfig/src/scripts/find_guile.c0000644000175100017510000000370414003561707017371 0ustar svnsvn/* scconfig - guile lib detection Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include int find_script_guile(const char *name, int logdepth, int fatal) { char *cflags, *ldflags; /* temp hack: guile/gh makes sure we have the old, 1.8 version */ char *test_c = NL "#include " NL "#include " NL "#include " NL "int main(int argc, char *argv[]) {" NL " scm_init_guile();" NL NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for guile... "); logprintf(logdepth, "find_guile: trying to find guile...\n"); logdepth++; if (run(logdepth, "guile-config compile", &cflags) + run(logdepth, "guile-config link", &ldflags) > 0) { free(cflags); free(ldflags); put("libs/script/guile/presents", sfalse); report("FAILED (guile-config failed)\n"); return 1; } /* TODO: do we need -ldl? */ if (try_icl(logdepth, "libs/script/guile", test_c, NULL, cflags, ldflags)) { free(ldflags); free(cflags); return 0; } free(ldflags); free(cflags); return try_fail(logdepth, "libs/script/guile"); } fungw-1.2.0/scconfig/src/scripts/find_ruby.c0000644000175100017510000000723714003561707017252 0ustar svnsvn/* scconfig - ruby lib detection Copyright (C) 2009..2015 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" static int brute_force(int logdepth, const char *test_c, const char *basedir) { char **files, **ifiles, *tmp, *ldflags; int fileno, n, m, ifileno, res; res = 0; filelist(logdepth, basedir, &fileno, &files); if (fileno <= 0) return res; for(n = 0; (n < fileno) && !res; n++) { tmp = malloc(strlen(basedir) + strlen(files[n]) + 4); sprintf(tmp, "%s/%s", basedir, files[n]); filelist(logdepth+1, tmp, &ifileno, &ifiles); free(tmp); for(m = 0; (m < ifileno) && !res; m++) { tmp = malloc(strlen(basedir) + strlen(files[n]) + strlen(ifiles[m]) + 16); sprintf(tmp, "%s/%s/%s/ruby.h", basedir, files[n], ifiles[m]); if (is_file(tmp)) { sprintf(tmp, "-I%s/%s/%s", basedir, files[n], ifiles[m]); ldflags = malloc(strlen(files[n]) + 16); sprintf(ldflags, "-lruby%s", files[n]); res = try_icl(logdepth, "libs/script/ruby", test_c, NULL, tmp, ldflags); free(ldflags); } free(tmp); } filelist_free(&ifileno, &ifiles); } filelist_free(&fileno, &files); return res; } int find_script_ruby(const char *name, int logdepth, int fatal) { int require18; const char *require18s; char *test_c = NL "#include " NL "#include " NL "int main() {" NL " ruby_init();" NL " ruby_finalize();" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; char *test_c18 = NL "#include " NL "#include " NL "#include " NL "int main() {" NL " ruby_init();" NL " ruby_frame->orig_func;" NL " ruby_finalize();" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for ruby... "); logprintf(logdepth, "find_ruby: trying to find ruby...\n"); logdepth++; require18s = get("libs/script/ruby/require_18"); if (require18s != NULL) require18 = istrue(require18s); else require18 = 0; if ((!require18) && (try_icl_pkg_config(logdepth, "libs/script/ruby", test_c, NULL, "ruby-*", get("/arg/ruby-version")))) return 0; if (try_icl_pkg_config(logdepth, "libs/script/ruby", test_c18, NULL, "ruby-*", get("/arg/ruby-version"))) return 0; /* Look at some standard places */ if ((!require18) && (try_icl(logdepth, "libs/script/ruby", test_c, NULL, NULL, "-lruby"))) return 0; if (try_icl(logdepth, "libs/script/ruby", test_c18, NULL, NULL, "-lruby")) return 0; require("/internal/filelist/method", logdepth, fatal); /* no luck - try to find by brute force, listing directory names */ if ((!require18) && (brute_force(logdepth, test_c, "/usr/lib/ruby") || brute_force(logdepth, test_c, "/usr/local/lib/ruby"))) { return 0; } if (brute_force(logdepth, test_c18, "/usr/lib/ruby") || brute_force(logdepth, test_c18, "/usr/local/lib/ruby")) { return 0; } return try_fail(logdepth, "libs/script/ruby"); } fungw-1.2.0/scconfig/src/scripts/find_python.c0000644000175100017510000000672513415314261017610 0ustar svnsvn/* scconfig - python lib detection Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" static int find_script_python_(const char *name, int logdepth, int fatal, int vermajor, const char *nodedir) { char *ldflags_base, *cflags, *ldflags, *nodebn; int iswin32, res; char test_c[1024]; char *test_c_in = NL "#include " NL "#include " NL "int main() {" NL " char *s;" NL " Py_Initialize();" NL NL " s = PY_VERSION;" NL " if (s[0] != '%d') return 1;" NL " if ((s[2] >= '0') && (s[2] <= '9')) puts(\"OK\");" NL " return 0;" NL "}" NL; char *inc_py = NL "import distutils.sysconfig;" NL "print '-I' + distutils.sysconfig.get_python_inc().replace('\\\\','/')" NL; char *lib_py = NL "import distutils.sysconfig;" NL "print '-L' + distutils.sysconfig.PREFIX.replace('\\\\','/') + '/libs',;" NL "import sys;" NL "print '-lpython' + str(sys.version_info[0]) + str(sys.version_info[1])" NL; require("sys/class", logdepth, fatal); iswin32 = strcmp(get("sys/class"), "win32") == 0; if (iswin32) require("libs/lpthread", logdepth, fatal); require("cc/cc", logdepth, fatal); require("/internal/filelist/method", logdepth, fatal); sprintf(test_c, test_c_in, vermajor); report("Checking for python (%d)... ", vermajor); logprintf(logdepth, "find_python: trying to find python %d...\n", vermajor); logdepth++; if (iswin32) ldflags_base = strclone(get("libs/lpthread")); else ldflags_base = strclone("-L/usr/bin -L/usr/local/bin"); /* Look at some standard places */ if (try_icl(logdepth, nodedir, test_c, NULL, NULL, "-lpython")) return 0; /* Ask python using the python executables on path; use + so both runs and can free out of both */ if (run_script(logdepth, "python", inc_py, ".py", &cflags) + run_script(logdepth, "python", lib_py, ".py", &ldflags) == 0) res = try_icl(logdepth, nodedir, test_c, NULL, strip(cflags), strip(ldflags)); else res = 0; free(cflags); free(ldflags); if (res) return 0; /* no luck - try to find by brute force, listing directory names */ nodebn = strrchr(nodedir, '/'); nodebn++; if (brute_force_include(logdepth, nodebn, test_c, ldflags_base, "/usr/include") || brute_force_include(logdepth, nodebn, test_c, ldflags_base, "/usr/local/include")) { free(ldflags_base); return 0; } free(ldflags_base); return try_fail(logdepth, nodedir); } int find_script_python(const char *name, int logdepth, int fatal) { return find_script_python_(name, logdepth, fatal, 2, "libs/script/python"); } int find_script_python3(const char *name, int logdepth, int fatal) { return find_script_python_(name, logdepth, fatal, 3, "libs/script/python3"); } fungw-1.2.0/scconfig/src/scripts/INIT.c0000644000175100017510000000002711255324267016026 0ustar svnsvn deps_scripts_init(); fungw-1.2.0/scconfig/src/scripts/INIT.h0000644000175100017510000000003211255324267016027 0ustar svnsvnvoid deps_scripts_init(); fungw-1.2.0/scconfig/src/scripts/find_mruby.c0000644000175100017510000000320614003561707017417 0ustar svnsvn/* scconfig - ruby lib detection Copyright (C) 2015 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" int find_script_mruby(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " mrb_state *ctx = mrb_open();" NL " mrb_close(ctx);" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for mruby... "); logprintf(logdepth, "find_mruby: trying to find mruby...\n"); logdepth++; if (try_icl_pkg_config(logdepth, "libs/script/mruby", test_c, NULL, "mruby-*", get("/arg/mruby-version"))) return 0; /* Look at the most standard place */ if (try_icl(logdepth, "libs/script/mruby", test_c, NULL, NULL, "-lmruby -lm")) return 0; return try_fail(logdepth, "libs/script/ruby"); } fungw-1.2.0/scconfig/src/scripts/scripts.c0000644000175100017510000000722714036737442016766 0ustar svnsvn/* scconfig - helper functions for detecting script libs Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include #include "find.h" void deps_scripts_init() { dep_add("libs/script/gpmi/*", find_script_gpmi); dep_add("libs/script/tcl/*", find_script_tcl); dep_add("libs/script/tk/*", find_script_tk); dep_add("libs/script/ruby/*", find_script_ruby); dep_add("libs/script/mruby/*", find_script_mruby); dep_add("libs/script/python/*", find_script_python); dep_add("libs/script/python3/*", find_script_python3); dep_add("libs/script/perl/*", find_script_perl); dep_add("libs/script/mawk/*", find_script_mawk); dep_add("libs/script/lua/*", find_script_lua); dep_add("libs/script/guile/*", find_script_guile); dep_add("libs/script/stutter/*", find_script_stutter); dep_add("libs/script/estutter/*", find_script_estutter); dep_add("libs/script/funlisp/*", find_script_funlisp); dep_add("libs/script/duktape/*", find_script_duktape); dep_add("libs/script/mujs/*", find_script_mujs); dep_add("libs/script/m4/bin/*", find_script_m4); dep_add("libs/script/welltype/*", find_script_welltype); dep_add("libs/script/welltype/wtc/*", find_script_wtc); dep_add("libs/script/picol/*", find_script_picol); dep_add("libs/script/fungw/presents", find_script_fungw); dep_add("libs/script/fungw/user_call_ctx/*", find_script_fungw_user_call_ctx); dep_add("libs/script/fungw/cfg_pupdir/*", find_script_fungw_cfg_pupdir); dep_add("libs/script/fungw/*", find_script_fungw_all); } int brute_force_include(int logdepth, const char *language, const char *test_c, const char *ldflags_base, const char *basedir) { char **files, *cflags, *ldflags; char nodename[1024], deflink[sizeof(nodename)]; int fileno, n, res; size_t llen; if (ldflags_base == NULL) ldflags_base = ""; llen = strlen(language); assert(llen < sizeof(nodename) - 16); sprintf(nodename, "libs/script/%s", language); sprintf(deflink, "-l%s", language); res = 0; filelist(logdepth, basedir, &fileno, &files); if (fileno > 0) { for(n = 0; (n < fileno) && !res; n++) { if (strncmp(files[n], language, llen) == 0) { ldflags = malloc(strlen(files[n]) + strlen(ldflags_base) + 16); sprintf(ldflags, "%s -l%s", ldflags_base, files[n]); cflags = malloc(strlen(files[n]) + strlen(basedir) + 16); sprintf(cflags, "-I%s/%s", basedir, files[n]); if (try_icl(logdepth, nodename, test_c, NULL, cflags, ldflags) || try_icl(logdepth, nodename, test_c, NULL, cflags, deflink)) { filelist_free(&fileno, &files); free(cflags); free(ldflags); return 1; } free(cflags); free(ldflags); } } filelist_free(&fileno, &files); } return res; } fungw-1.2.0/scconfig/src/scripts/find_lua.c0000644000175100017510000000552514003561707017050 0ustar svnsvn/* scconfig - lua lib detection Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include int find_script_lua(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "#include " NL "int main() {" NL " lua_State *state;" NL " state = luaL_newstate();" NL " luaL_loadfile(state, \"nothing\");" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); require("/internal/filelist/method", logdepth, fatal); report("Checking for lua... "); logprintf(logdepth, "find_lua: trying to find lua...\n"); logdepth++; /* Look at some standard places */ /* TODO: do we need -ldl? */ if (try_icl(logdepth, "libs/script/lua", test_c, NULL, NULL, "-llua -llualib -lm")) return 0; /* lualib doesn't exist in lua 5.1.1 */ if (try_icl(logdepth, "libs/script/lua", test_c, NULL, NULL, "-llua -lm")) return 0; /* OS specific include dir */ if (try_icl(logdepth, "libs/script/lua", test_c, NULL, "-I/usr/include", "-llua -llualib -lm")) return 0; if (try_icl(logdepth, "libs/script/lua", test_c, NULL, "-I/usr/include/lua5.3", "-llua5.3 -lm")) return 0; if (try_icl(logdepth, "libs/script/lua", test_c, NULL, "-I/usr/include/lua5.2", "-llua5.2 -lm")) return 0; if (try_icl(logdepth, "libs/script/lua", test_c, NULL, "-I/usr/local/include", "-llua -llualib -lm")) return 0; if (try_icl(logdepth, "libs/script/lua", test_c, NULL, "-I/usr/include", "-llua -lm")) return 0; if (try_icl(logdepth, "libs/script/lua", test_c, NULL, "-I/usr/local/include", "-llua -lm")) return 0; /* This one is for OSX (by Bjarni) */ if (try_icl(logdepth, "libs/script/lua", test_c, NULL, "-I/sw/include", "-llua -llualib -lm")) return 0; /* no luck - try to find by brute force, listing directory names */ if (brute_force_include(logdepth, "lua", test_c, NULL, "/usr/include") || brute_force_include(logdepth, "lua", test_c, NULL, "/usr/local/include")) { return 0; } return try_fail(logdepth, "libs/script/lua"); } fungw-1.2.0/scconfig/src/scripts/find_mawk.c0000644000175100017510000000316513246534262017230 0ustar svnsvn/* scconfig - mawk lib detection Copyright (C) 2009 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include int find_script_mawk(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "#include " NL "int main() {" NL " int argc = 1;" NL " char *argv[] = {" NL " \"mawk\"," NL " NULL" NL " };" NL " libmawk_initialize(argc, argv);" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for mawk... "); logprintf(logdepth, "find_mawk: trying to find mawk...\n"); logdepth++; /* Look at the standard place */ if (try_icl(logdepth, "libs/script/mawk", test_c, NULL, NULL, "-lmawk")) return 0; return try_fail(logdepth, "libs/script/mawk"); } fungw-1.2.0/scconfig/src/scripts/find_picol.c0000644000175100017510000000321714036457566017406 0ustar svnsvn/* scconfig - picol detection Copyright (C) 2021 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include int find_script_picol(const char *name, int logdepth, int fatal) { char *test_c = NL "#define PICOL_IMPLEMENTATION" NL "#include \"picol.h\"" NL "int main() {" NL " picolInterp *interp = picolCreateInterp();" NL " picolEval(interp, \"puts {OK}\");" NL " picolFreeInterp(interp);" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for picol... "); logprintf(logdepth, "find_picol: trying to find picol...\n"); logdepth++; /* Look at the standard places: /usr/include and /usr/local/include and whatever the C compiler sees fit */ if (try_icl(logdepth, "libs/script/picol", test_c, NULL, NULL, "")) return 0; return try_fail(logdepth, "libs/script/picol"); } fungw-1.2.0/scconfig/src/scripts/find_fungw.c0000644000175100017510000000765514003561707017423 0ustar svnsvn/* scconfig - lua lib detection Copyright (C) 2018 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include int find_script_fungw(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " fgw_ctx_t ctx;" NL " fgw_obj_t *obj;" NL " fgw_init(&ctx, \"host\");" NL " obj = fgw_obj_reg(&ctx, \"foo\");" NL " if (obj != NULL)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for fungw... "); logprintf(logdepth, "find_fungw: trying to find fungw...\n"); logdepth++; /* Look at some standard place */ if (try_icl(logdepth, "libs/script/fungw", test_c, NULL, NULL, "-lfungw -lm -lgenht")) return 0; if (try_icl(logdepth, "libs/script/fungw", test_c, NULL, "-I/usr/local/include", "-L/usr/local/lib -lfungw -lm -lgenht")) return 0; return try_fail(logdepth, "libs/script/fungw"); } int find_script_fungw_user_call_ctx(const char *name, int logdepth, int fatal) { const char *lf, *cf, *inc; char *test_c = NL "#include " NL "int main() {" NL " fgw_value_t val;" NL " val.argv0.user_call_ctx = &val;" NL " if (0) fgw_uvcall(NULL, NULL, NULL, \"funcname\", NULL);" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); require("libs/script/fungw/presents", logdepth, fatal); report("Checking for fungw user_call_ctx... "); logprintf(logdepth, "find_fungw_user_call_ctx: trying to find fungw...\n"); logdepth++; inc = get("libs/script/fungw/includes"); lf = get("libs/script/fungw/ldflags"); cf = get("libs/script/fungw/cflags"); if (try_icl(logdepth, "libs/script/fungw/user_call_ctx", test_c, inc, cf, lf)) return 0; return try_fail(logdepth, "libs/script/fungw/user_call_ctx"); } int find_script_fungw_cfg_pupdir(const char *name, int logdepth, int fatal) { const char *lf, *cf; char *out; char *test_c = NL "#include " NL "int main() {" NL " puts(FGW_CFG_PUPDIR);" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); require("libs/script/fungw/presents", logdepth, fatal); report("Checking for fungw configured pupdir... "); logprintf(logdepth, "find_fungw_cfg_pupdir: trying to find fungw pupdir...\n"); logdepth++; /* inc = get("libs/script/fungw/includes");*/ lf = get("libs/script/fungw/ldflags"); cf = get("libs/script/fungw/cflags"); if ((compile_run(logdepth, test_c, NULL, cf, lf, &out) == 0) && (out != NULL)) { char *end = strpbrk(out, "\r\n"); if (end != NULL) *end = '\0'; report("Found: %s\n", out); logprintf(logdepth+1, "found: %s\n", out); put("libs/script/fungw/cfg_pupdir/path", out); put("libs/script/fungw/cfg_pupdir/presents", strue); free(out); return 1; } report("Not found\n"); logprintf(logdepth+1, "not found\n"); put("libs/script/fungw/cfg_pupdir/presents", sfalse); return 0; } int find_script_fungw_all(const char *name, int logdepth, int fatal) { if (require("libs/script/fungw/presents", logdepth, fatal) != 0) return 1; require("libs/script/fungw/user_call_ctx/*", logdepth, 0); return 0; }fungw-1.2.0/scconfig/src/scripts/find_funlisp.c0000644000175100017510000000321314003561707017737 0ustar svnsvn/* scconfig - stutter lib detection Copyright (C) 2018 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include int find_script_funlisp(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " lisp_runtime *rt = lisp_runtime_new();" NL " if (rt != NULL)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for funlisp... "); logprintf(logdepth, "find_funlisp: trying to find funlisp...\n"); logdepth++; if ( try_icl(logdepth, "libs/script/funlisp", test_c, NULL, NULL, "/usr/local/lib/libfunlisp.a") || try_icl(logdepth, "libs/script/funlisp", test_c, NULL, NULL, "/usr/lib/libfunlisp.a") ) return 0; return try_fail(logdepth, "libs/script/funlisp"); } fungw-1.2.0/scconfig/src/scripts/Makefile.plugin0000644000175100017510000000664114036737442020067 0ustar svnsvnSCRIPT_CFLAGS = -DPLUGIN_SCRIPTS SCRIPT_OBJS = \ $(BIN)/scripts/scripts.o \ $(BIN)/scripts/find_gpmi.o \ $(BIN)/scripts/find_tcl.o \ $(BIN)/scripts/find_ruby.o \ $(BIN)/scripts/find_mruby.o \ $(BIN)/scripts/find_python.o \ $(BIN)/scripts/find_perl.o \ $(BIN)/scripts/find_mawk.o \ $(BIN)/scripts/find_lua.o \ $(BIN)/scripts/find_guile.o \ $(BIN)/scripts/find_stutter.o \ $(BIN)/scripts/find_funlisp.o \ $(BIN)/scripts/find_duktape.o \ $(BIN)/scripts/find_mujs.o \ $(BIN)/scripts/find_fungw.o \ $(BIN)/scripts/find_m4.o \ $(BIN)/scripts/find_welltype.o \ $(BIN)/scripts/find_picol.o $(BIN)/scripts/scripts.o: $(SRC)/scripts/scripts.c $(CC) $(CFLAGS) -c $(SRC)/scripts/scripts.c -o $(BIN)/scripts/scripts.o $(BIN)/scripts/find_tcl.o: $(SRC)/scripts/find_tcl.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_tcl.c -o $(BIN)/scripts/find_tcl.o $(BIN)/scripts/find_gpmi.o: $(SRC)/scripts/find_gpmi.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_gpmi.c -o $(BIN)/scripts/find_gpmi.o $(BIN)/scripts/find_ruby.o: $(SRC)/scripts/find_ruby.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_ruby.c -o $(BIN)/scripts/find_ruby.o $(BIN)/scripts/find_mruby.o: $(SRC)/scripts/find_mruby.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_mruby.c -o $(BIN)/scripts/find_mruby.o $(BIN)/scripts/find_python.o: $(SRC)/scripts/find_python.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_python.c -o $(BIN)/scripts/find_python.o $(BIN)/scripts/find_perl.o: $(SRC)/scripts/find_perl.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_perl.c -o $(BIN)/scripts/find_perl.o $(BIN)/scripts/find_mawk.o: $(SRC)/scripts/find_mawk.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_mawk.c -o $(BIN)/scripts/find_mawk.o $(BIN)/scripts/find_lua.o: $(SRC)/scripts/find_lua.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_lua.c -o $(BIN)/scripts/find_lua.o $(BIN)/scripts/find_guile.o: $(SRC)/scripts/find_guile.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_guile.c -o $(BIN)/scripts/find_guile.o $(BIN)/scripts/find_stutter.o: $(SRC)/scripts/find_stutter.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_stutter.c -o $(BIN)/scripts/find_stutter.o $(BIN)/scripts/find_m4.o: $(SRC)/scripts/find_m4.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_m4.c -o $(BIN)/scripts/find_m4.o $(BIN)/scripts/find_welltype.o: $(SRC)/scripts/find_welltype.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_welltype.c -o $(BIN)/scripts/find_welltype.o $(BIN)/scripts/find_duktape.o: $(SRC)/scripts/find_duktape.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_duktape.c -o $(BIN)/scripts/find_duktape.o $(BIN)/scripts/find_mujs.o: $(SRC)/scripts/find_mujs.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_mujs.c -o $(BIN)/scripts/find_mujs.o $(BIN)/scripts/find_funlisp.o: $(SRC)/scripts/find_funlisp.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_funlisp.c -o $(BIN)/scripts/find_funlisp.o $(BIN)/scripts/find_fungw.o: $(SRC)/scripts/find_fungw.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_fungw.c -o $(BIN)/scripts/find_fungw.o $(BIN)/scripts/find_picol.o: $(SRC)/scripts/find_picol.c $(SRC)/scripts/scripts.h $(CC) $(CFLAGS) -c $(SRC)/scripts/find_picol.c -o $(BIN)/scripts/find_picol.o fungw-1.2.0/scconfig/src/scripts/find_duktape.c0000644000175100017510000000310214003561707017711 0ustar svnsvn/* scconfig - duktape lib detection Copyright (C) 2018 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include int find_script_duktape(const char *name, int logdepth, int fatal) { char *test_c = NL "#include " NL "#include " NL "int main() {" NL " duk_context *ctx = duk_create_heap_default();" NL " if (ctx != NULL)" NL " puts(\"OK\");" NL " return 0;" NL "}" NL; require("cc/cc", logdepth, fatal); report("Checking for duktape... "); logprintf(logdepth, "find_duktape: trying to find duktape...\n"); logdepth++; /* Look at the standard place */ if (try_icl(logdepth, "libs/script/duktape", test_c, NULL, NULL, "-lduktape -lm")) return 0; return try_fail(logdepth, "libs/script/duktape"); } fungw-1.2.0/scconfig/src/scripts/find_m4.c0000644000175100017510000000431113240634424016576 0ustar svnsvn/* scconfig - m4 binary detection Copyright (C) 2015 Tibor Palinkas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Project page: http://repo.hu/projects/scconfig Contact via email: scconfig [at] igor2.repo.hu */ #include "scripts.h" #include static int test_m4(const char *name, int logdepth, int fatal, char *bin) { char *script = NL "define(baz, foo)" NL "baz bar" NL; char *out, *cmd, *tmpf, *tmpf_esc; (void) fatal; /* not used */ tmpf = tempfile_dump(script, ".m4"); tmpf_esc = shell_escape_dup(tmpf); cmd = str_concat(" ", bin, tmpf_esc, NULL); free(tmpf_esc); run(logdepth, cmd, &out); if (out != NULL) { char *s = out; while((*s == ' ') || (*s == '\r') || (*s == '\n')) s++; if (strncmp(s, "foo bar", 7) == 0) { unlink(tmpf); free(tmpf); free(out); report("found (%s)\n", bin); logprintf(logdepth, "found (%s)", bin); put("libs/script/m4/bin/presents", strue); put("libs/script/m4/bin/path", bin); return 1; } free(out); } unlink(tmpf); free(tmpf); free(cmd); return 0; } int find_script_m4(const char *name, int logdepth, int fatal) { char *m4_paths[] = { "/usr/bin/m4", "/usr/local/bin/m4", "/bin/m4", "/usr/bin/gm4", "/usr/local/bin/gm4", "/bin/gm4", NULL }; char **s; report("Checking for m4 binary... "); logprintf(logdepth, "find_m4: trying to find m4 binary...\n"); logdepth++; for(s = m4_paths; *s != NULL; s++) if (test_m4(name, logdepth, fatal, *s)) return 0; return try_fail(logdepth, "libs/script/m4/bin"); } fungw-1.2.0/scconfig/src/tmpasm/0000755000175100017510000000000014047742764014742 5ustar svnsvnfungw-1.2.0/scconfig/src/tmpasm/tmpasm_scconfig.h0000644000175100017510000000047213366254556020272 0ustar svnsvn#include #include "tmpasm.h" int tmpasm(const char *wdir, const char *input, const char *output); void scc_tmpasm_parse(tmpasm_t *ctx, const char *cwd, FILE *fin, FILE *fout); FILE *tmpasm_fout(tmpasm_t *ctx); tmpasm_instr *scc_resolve(tmpasm_t *ctx, const char *name); extern tmpasm_cb_t scc_cb; fungw-1.2.0/scconfig/src/tmpasm/regression/0000755000175100017510000000000014047742764017122 5ustar svnsvnfungw-1.2.0/scconfig/src/tmpasm/regression/err_if_else.ref0000644000175100017510000000020512217256676022072 0ustar svnsvnerror: unexpected 'else' - must be in a 'then' block before an else at 2:5 if *NULL - broken AST* [at 1:1] then: (NOP) else: (NOP) fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor03_blocks.ref0000644000175100017510000000057712217241351022425 0ustar svnsvnprint [at 7:1] arg: {this is a string} arg: { } print [at 9:1] arg: {-- } put [at 11:1] arg: myblk arg: {a block of multiline data. } print [at 16:1] arg: myblk put [at 24:1] arg: myvar arg: {world} print [at 25:1] arg: {-- } print [at 26:1] arg: [~ hello ~myvar~! ~] arg: { } print [at 27:1] arg: {-- } print [at 28:1] arg: { hi @myvar@! } print [at 31:1] arg: {-- } fungw-1.2.0/scconfig/src/tmpasm/regression/foreach.ref0000644000175100017510000000100612217241351021203 0ustar svnsvnput [at 1:1] arg: a arg: {1} put [at 2:1] arg: foo arg: {example-FOO} put [at 3:1] arg: bar arg: {example-BAR} put [at 4:1] arg: baz arg: {example-BAZ} put [at 5:1] arg: hah arg: {haha} foreach n in [~foo ~bar~ baz~] [at 9:1] print [at 10:2] arg: {n=} arg: n arg: { } print [at 11:2] arg: {a11} arg: [~a12 ~hah~ a14~] arg: { } print [at 12:2] arg: {a21} arg: {a22 a23} arg: { } print [at 13:2] arg: {a31 } print [at 15:1] arg: {a41} arg: {a42} arg: {a43} arg: { } print [at 16:1] fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor05_switch.ref0000644000175100017510000000171412217241351022445 0ustar svnsvnput [at 15:1] arg: myvar arg: {foobar} switch myvar [at 16:1] case {baz} put [at 17:14] arg: res arg: {1} print [at 17:27] arg: {this is baz. } case {^oob} put [at 18:14] arg: res arg: {2} print [at 18:27] arg: {did you mean out-of-band? } case {^f} put [at 19:14] arg: res arg: {3} print [at 19:27] arg: {starts with f. } case {oob} put [at 20:14] arg: res arg: {4} print [at 20:27] arg: {OOB! } default put [at 21:14] arg: res arg: {0} print [at 21:27] arg: {none. } print [at 24:1] arg: {result is: } arg: res arg: { } put [at 28:1] arg: patt arg: {^number$} put [at 29:1] arg: REF arg: {3} switch [~num ~res~ ber~] [at 30:1] case patt print [at 31:23] arg: {empty } case [~^num ~REF~~] print [at 32:23] arg: {reference } put [at 38:1] arg: cond arg: {blobb} switch cond [at 39:1] case {lob} print [at 40:15] arg: {"then" } default print [at 41:15] arg: {"else" } fungw-1.2.0/scconfig/src/tmpasm/regression/order.gasm0000644000175100017510000000163713155152146021101 0ustar svnsvn# test if without an else put list {one two three four} print list {\n} print {\nnothing:\n} # do nothing: order matches order out list {one} {before} {two} print out {\n} order out list {one} {before} {one} print out {\n} order out list {one} {after} {one} print out {\n} order out list {two} {after} {one} print out {\n} # do nothing: not on list matches order out list {nine} {after} {one} print out {\n} order out list {one} {after} {nine} print out {\n} print {\nbefore:\n} order out list {two} {before} {one} print out {\n} order out list {four} {before} {one} print out {\n} order out list {four} {before} {three} print out {\n} order out list {three} {before} {two} print out {\n} print {\nafter:\n} order out list {one} {after} {two} print out {\n} order out list {one} {after} {four} print out {\n} order out list {two} {after} {three} print out {\n} order out list {two} {after} {four} print out {\n} fungw-1.2.0/scconfig/src/tmpasm/regression/order.ref0000644000175100017510000000333713155152617020730 0ustar svnsvnput [at 2:1] arg: list arg: {one two three four} print [at 4:1] arg: list arg: { } print [at 6:1] arg: { nothing: } order [at 9:1] arg: out arg: list arg: {one} arg: {before} arg: {two} print [at 10:1] arg: out arg: { } order [at 12:1] arg: out arg: list arg: {one} arg: {before} arg: {one} print [at 13:1] arg: out arg: { } order [at 15:1] arg: out arg: list arg: {one} arg: {after} arg: {one} print [at 16:1] arg: out arg: { } order [at 18:1] arg: out arg: list arg: {two} arg: {after} arg: {one} print [at 19:1] arg: out arg: { } order [at 22:1] arg: out arg: list arg: {nine} arg: {after} arg: {one} print [at 23:1] arg: out arg: { } order [at 24:1] arg: out arg: list arg: {one} arg: {after} arg: {nine} print [at 25:1] arg: out arg: { } print [at 27:1] arg: { before: } order [at 28:1] arg: out arg: list arg: {two} arg: {before} arg: {one} print [at 29:1] arg: out arg: { } order [at 31:1] arg: out arg: list arg: {four} arg: {before} arg: {one} print [at 32:1] arg: out arg: { } order [at 34:1] arg: out arg: list arg: {four} arg: {before} arg: {three} print [at 35:1] arg: out arg: { } order [at 37:1] arg: out arg: list arg: {three} arg: {before} arg: {two} print [at 38:1] arg: out arg: { } print [at 40:1] arg: { after: } order [at 41:1] arg: out arg: list arg: {one} arg: {after} arg: {two} print [at 42:1] arg: out arg: { } order [at 44:1] arg: out arg: list arg: {one} arg: {after} arg: {four} print [at 45:1] arg: out arg: { } order [at 47:1] arg: out arg: list arg: {two} arg: {after} arg: {three} print [at 48:1] arg: out arg: { } order [at 50:1] arg: out arg: list arg: {two} arg: {after} arg: {four} print [at 51:1] arg: out arg: { } fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor10_include_redir.ref0000644000175100017510000000053412217253432023752 0ustar svnsvnprint [at 14:1] arg: {this goes to the default output } redir [at 18:1] arg: {Tutor10.inc} print [at 19:1] arg: {# this is a generated file.} print [at 20:1] arg: { print {hello world from my include!\n} } redir [at 25:1] print [at 26:1] arg: {back at default output. } print [at 45:1] arg: {Include: } include [at 46:1] arg: {Tutor10.inc} fungw-1.2.0/scconfig/src/tmpasm/regression/err_switch_nocond.gasm0000644000175100017510000000004612217260230023460 0ustar svnsvnswitch case data print {foo}; end end fungw-1.2.0/scconfig/src/tmpasm/regression/test.gasm0000644000175100017510000000176312750535606020753 0ustar svnsvn### set up internal variables ### put /local/cflags {-std=c99 -Wall} put /local/ldflags {-lm} put /local/objs {main.o foo.o bar.o} # turn off optimization and add -g in debug mode if /local/debug then append /local/cflags {-g} else append /local/cflags {-O2} end # if somelib is selected, add -I and -l isempty /local/r /local/somelib invert /local/r if /local/r then append /local/cflags { -I/usr/include/somelib} append /local/ldflags { -lsomelib} end ### Generate the Makefile ### print [@ # Makefile generated by scconfig - DO NOT EDIT - please edit Makefile.in CFLAGS=@/local/cflags@ LDFLAGS=@/local/ldflags@ OBJS=@/local/objs@ all: main main: $(OBJS) $(CC) $(LDFLAGS) @] # loop over each object and generate an explicit rule # (we are generating a dumb Makefile that would work with any # old version of make) foreach /local/o in /local/objs put /local/c /local/o sub /local/c {.o$} {.c} print [@ @/local/o@: @/local/c@ $(CC) -c $(CFLAGS) @/local/c@ -o @/local/o@ @] end print {#end\n} fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor01_hello.gasm0000644000175100017510000000113012216005160022401 0ustar svnsvn# Comments start with # and end at the end of the line; comments # can be started anywhere outside of strings and blocks # Print will print each argument without appending a newline. An argument # is a data, string literals are written in brace. Escape sequences # are as usual print {hello world!\n} # Print doesn't have any hidden side effect: no separators printed # between arguments print {hello} {world!} {\n} # Print works without an argument as well: it just prints nothing print # instructions are separated by newlines and/or semicolons: print {HELLO}; print { WORLD!};;;; print {\n}; fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor07_sub.gasm0000644000175100017510000000173212750535606022125 0ustar svnsvn# The following regex sub utils are scconfig specific. # Regex: substitute the first match of a pattern with str in a variable: # sub address pattern str put myvar {Hello world!\n} sub myvar {l} {2} print myvar # Address must resolve to an existing variable, pattern and str are data; # this means address can be a string that holds a variable name, it's the # same as if it was an addreess: sub {myvar} {l} {3} print myvar # Or it can be a block, which makes indirect addressing possible: # in [@@pointer@@] the @pointer@ part will be substituted with # the value of pointer, which is "myvar". put pointer {myvar} sub [@@pointer@@] {l} {4} print myvar # Since pattern and str are also data, address and blocks work there as well # (but this is _not_ a regex backreference): put punctuation {[!?.]} sub [@@pointer@@] punctuation [@ PUNCT:@punctuation@@] print myvar # gsub does the same, but substutites all matches, not only the first: gsub [@@pointer@@] {o} {_0_} print myvar fungw-1.2.0/scconfig/src/tmpasm/regression/if.gasm0000644000175100017510000000014712215755502020360 0ustar svnsvnput a 1 if a then if b then print {then-then} else print {then-else} end else print {else} end fungw-1.2.0/scconfig/src/tmpasm/regression/err_switch_end.gasm0000644000175100017510000000001312217260230022740 0ustar svnsvnswitch end fungw-1.2.0/scconfig/src/tmpasm/regression/err_no_end.ref0000644000175100017510000000013412217263041021707 0ustar svnsvnpritn [at 1:1] arg: {foo} switch {cond} [at 2:1] case {1} print [at 3:11] arg: {foo} fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor09_ui.gasm0000644000175100017510000000115512217250705021742 0ustar svnsvn# The following user interface utils are scconfig specific. # The report infrstructure is the main UI in scconfig. It prints # messages to the console. The "report" instruction works similar # to print, but its output is always the console, immune to # redirections and default file output (tmpasm is most commonly # used for generating files, so the default output file is not # the console but a file being generated) put myvar {!\n} report {hello } {world} myvar report [@hello world@myvar@@] # If an error is detected during generation of a file, the script should abort. # This is a direct call to abort(2). abort fungw-1.2.0/scconfig/src/tmpasm/regression/foreach.gasm0000644000175100017510000000053412215771633021374 0ustar svnsvnput a {1} put foo {example-FOO} put bar {example-BAR} put baz {example-BAZ} put hah {haha} # should set n to the string foo, the value of bar and the string baz # per iteration foreach n in [~foo ~bar~ baz~] print {n=} n {\n} print {a11} [@a12 @hah@ a14@] {\n} print {a21} {a22 a23} {\n} print {a31\n} end print {a41} {a42} {a43} {\n} print fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor04_if.gasm0000644000175100017510000000122612216005033021704 0ustar svnsvnput myvar {true} # The simplest flow control is an if. It takes its first argument and # calls the environment to decide if it is true or false. If it's true # the "then" branch is executed, if it's false, the "else" branch runs. if myvar then print {myvar is true (1)\n} else print {myvar is false (1)\n} end # it is possible to omit the else branch if myvar then print {myvar is true (2)\n} end # the then branch may be empty: if myvar then else print {myvar is false (3)\n} end # embedding controls is legal: put foo {false} if myvar then if foo then print {myvar and bar are true (4)\n} else print {myvar is true, bar is false (4)\n} end end fungw-1.2.0/scconfig/src/tmpasm/regression/Makefile.in0000644000175100017510000000155613256100515021155 0ustar svnsvn# list of tests put tests [@ Tutor01_hello Tutor02_vars Tutor03_blocks Tutor04_if Tutor05_switch Tutor06_foreach Tutor07_sub Tutor08_uniq Tutor09_ui Tutor10_include_redir Tutor11_missing Tutor12_halt comment foreach if switch test then append order err_if_end err_if_else err_excess_end err_switch_end err_switch_nocond err_no_end @] uniq tests put test_diffs tests gsub test_diffs {[ \t\r\n]+} { } gsub test_diffs {\\>} {.diff} # TODO: replace this with wrap sub test_diffs {^ *} {} sub test_diffs { *$} {} print [@### PLEASE DO NOT EDIT THIS FILE, it has been generated from Makefile.in. ### TESTER=../tester all: @test_diffs@ # Explicit test rules @] foreach test in tests print [@ @test@.out: @test@.gasm $(TESTER) Makefile $(TESTER) < @test@.gasm > @test@.out 2>&1 @test@.diff: @test@.ref @test@.out diff -u @test@.ref @test@.out @] end fungw-1.2.0/scconfig/src/tmpasm/regression/comment.gasm0000644000175100017510000000015012215756762021427 0ustar svnsvnprint data1 # this is a comment; until the end of this line print data2 # this shall be a second print fungw-1.2.0/scconfig/src/tmpasm/regression/if.ref0000644000175100017510000000027412217241351020200 0ustar svnsvnput [at 1:1] arg: a arg: 1 if a [at 2:6] then: if b [at 3:7] then: print [at 4:3] arg: {then-then} else: print [at 6:3] arg: {then-else} else: print [at 9:2] arg: {else} fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor11_missing.gasm0000644000175100017510000000031713155152514022767 0ustar svnsvn# conditional/safe variable reference: name prefixed by ? # if the variable does not exist, no error is thrown but empty # string is returned if ?a then print {empty} else print {not empty} end put b ?a fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor08_uniq.gasm0000644000175100017510000000330613216664404022305 0ustar svnsvn# The following string util is scconfig specific. # Uniq: filter a list of words and remove duplicate items. This instruction # is useful for using text nodes as lists. # The simplest syntax is "uniq address" which will do the filtering on # the content of a database address. The default separator is \n put list [@this is a list of words, a list of words. @] print {original:\n} list {\n} uniq list print {uniq:\n} list {\n} # If the original list needs to be left intact, the alternative syntax is # "uniq dest-addr src-addr": put foo [@this foo is a this foo @] uniq tmp foo print {original:\n} foo {\nuniq:\n} tmp {\n} # Note: the algorithm of uniq is slow, and will not be efficient for very long # lists. Uniq preserves the order of words (by their first appearance). # Sortuniq performs the same action, except it also orders the list using # qsort() (so it is even slower on big lists). sortuniq tmp foo print {\nsortuniq:\n} tmp {\n} # A typical use case is having #defines and #includes on a list; #defines # should end up on the top, but order of #incldues should be preserved, so # sortuniq is not an option. When uniq is called with more than 2 argument,s # the extra arguments specify group regexps; the input is first organized into # groups then uniq is ran on these groups. Anything that doesn't match the # groups listed are put in a "misc" group that ends up as the last group. put list [@#define foo #include "foo20.h" #include "foo10.h" /* misc1 */ #define bar #include "bar1.h" #include "bar2.h" /* misc2 */ @] # set input field separator to \n so uniq is splitting by lines, not by # words put /tmpasm/IFS {\n} uniq tmp list {^#define} {^#include} print {original:\n} list {\ngrouped uniq:\n} tmp {\n} fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor10_include_redir.gasm0000644000175100017510000000400512217253432024122 0ustar svnsvn# NOTE: THIS EXAMPLE WILL NOT WORK IN THE ON-LINE WEB VERSION # These features are scconfig specific. # There is a default output file coming from the environment; this # is the file being generated. In the most common cases this is the # only file the script will ever write. Any "print" instruction will # write this file by default. However, sometimes it is handy # to generate a small misc file during generating a large file. Thus # the output file that "print" writes is not hardwired. Instead, there # is the default output file and the current output file. Instruction # "redir" can change the current output file. print {this goes to the default output\n} # redirect to Tutor10.inc; any "print" until the next "redir" will # write that file redir {Tutor10.inc} print {# this is a generated file.} print [@ print {hello world from my include!\n} @] # switch back to the default output redir print {back at default output.\n} # Dynamic include: the script may include another script, runtime. When # an include instruction is executed, the referred file is open, read, # parsed and executed, recusively. # # Include being dynamic (or runtime) is unusual, but has the following # advantages: # - file name for inclusion can be calculated # - conditional include is easily possible without an extra preprocessing layer # - it is possible to generate a script on the fly and include it (sort of eval) # Drawbacks: # - it is possible to end up in an infinite loop that will only stop when # resources run out (open fds or memory) # - it is slow, e.g. if the body of a foreach contains include, the whole # read-parse-execute procedure is repeated for each item # Redir files are overwritten when first open from an execution. print {Include:\n} include {Tutor10.inc} # NOTE: the above script works only because "redir" has a side effect: # whenever redirection switches away from a file, that file is flushed. # This happens even if the new current output is the same as the old # current output (no actual switch takes place). fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor04_if.ref0000644000175100017510000000105112217241351021533 0ustar svnsvnput [at 1:1] arg: myvar arg: {true} if myvar [at 6:10] then: print [at 7:2] arg: {myvar is true (1) } else: print [at 9:2] arg: {myvar is false (1) } if myvar [at 14:10] then: print [at 15:2] arg: {myvar is true (2) } else: (NOP) if myvar [at 19:10] then: (NOP) else: print [at 20:2] arg: {myvar is false (3) } put [at 24:1] arg: foo arg: {false} if myvar [at 25:10] then: if foo [at 26:9] then: print [at 27:3] arg: {myvar and bar are true (4) } else: print [at 29:3] arg: {myvar is true, bar is false (4) } else: (NOP) fungw-1.2.0/scconfig/src/tmpasm/regression/append.gasm0000644000175100017510000000013412217512074021222 0ustar svnsvnappend tmp {foo} append tmp {bar} append tmp {baz} foreach n in tmp print {-> } n {\n} end fungw-1.2.0/scconfig/src/tmpasm/regression/err_excess_end.gasm0000644000175100017510000000002412217257260022744 0ustar svnsvnif {1} then end end fungw-1.2.0/scconfig/src/tmpasm/regression/err_if_else.gasm0000644000175100017510000000003412217256676022245 0ustar svnsvnif v else print {else} end fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor12_halt.ref0000644000175100017510000000033613256100515022071 0ustar svnsvnput [at 6:1] arg: tmp arg: {true} if tmp [at 7:8] then: foreach item in {foo bar true baz} [at 8:2] print [at 9:3] arg: item arg: { } if item [at 10:11] then: halt [at 11:4] else: (NOP) else: (NOP) fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor02_vars.gasm0000644000175100017510000000157113237242401022270 0ustar svnsvn# Variables are stored in a hash. Variable names follow the normal # identifier rules with less restrictions (can start with number) # To set the value of a variable, use: put var value put myvar {Hello world!\n} # Referencing the variable is done by using its name print myvar # In most context arguments are data; data can be both string literal and # variable reference: print {Hello universe! } myvar # the second var of put is just data, can be string or variable; copying # a variable: put str {cats raining from the sky} put tmp str print str {==} tmp {\n} # the ? prefix results in empty string if a variable doesnt exist, instead # of throwing a runtime error print {safe get: '} ?nonexist {'\n} # the & prefix evaluates to "true" or "false" depending on whether the node # exists in the tree or not print {exists (no): } &nonexist {\n} print {exists (yes): } &myvar {\n} fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor09_ui.ref0000644000175100017510000000023412217250705021564 0ustar svnsvnput [at 9:1] arg: myvar arg: {! } report [at 10:1] arg: {hello } arg: {world} arg: myvar report [at 11:1] arg: [~hello world~myvar~~] abort [at 15:1] fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor05_switch.gasm0000644000175100017510000000306112216215046022616 0ustar svnsvn# Switch is similar to case in posix shell and switch in C. It takes a # data argument and matches is against cases until the first match. It # executes the code for that match and stops executing the switch (unlike # in C, and like in sh, there is no fall-thru). A default case can be # defined as a catch-all rule. # # Scconfig uses regex matching (unlike sh (shell globbing) and C (integer)). # # The first word after the switch keyword is the string that is matched # against case patterns; the first word after a case is the pattern # the string is matched against. Each branch must be terminated by an "end", # just as the whole switch. Default doesn't have pattern, instructions start # immediately. put myvar {foobar} switch myvar case {baz} put res {1}; print {this is baz.\n}; end; case {^oob} put res {2}; print {did you mean out-of-band?\n}; end; case {^f} put res {3}; print {starts with f.\n}; end; case {oob} put res {4}; print {OOB!\n}; end; default put res {0}; print {none.\n}; end; end; print {result is: } res {\n} # data is data - can be block as well, anywhere, in switch or case: put patt {^number$} put REF {3} switch [@num @res@ ber@] case patt print {empty\n}; end; case [!^num !REF!!] print {reference\n}; end; end # one of the uses of switch is to construct an if-then-else that uses # matching instead of checking for true/false; the following example # demonstrates how an "if cond matches {lob}" is done with switch. put cond {blobb} switch cond case {lob} print {"then"\n}; end default print {"else"\n}; end end fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor11_missing.out0000644000175100017510000000017412752771512022657 0ustar svnsvnif ?a [at 5:7] then: print [at 6:2] arg: {empty} else: print [at 8:2] arg: {not empty} put [at 11:1] arg: b arg: ?a fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor02_vars.ref0000644000175100017510000000072213237242446022123 0ustar svnsvnput [at 4:1] arg: myvar arg: {Hello world! } print [at 7:1] arg: myvar print [at 11:1] arg: {Hello universe! } arg: myvar put [at 15:1] arg: str arg: {cats raining from the sky} put [at 16:1] arg: tmp arg: str print [at 17:1] arg: str arg: {==} arg: tmp arg: { } print [at 21:1] arg: {safe get: '} arg: ?nonexist arg: {' } print [at 25:1] arg: {exists (no): } arg: &nonexist arg: { } print [at 26:1] arg: {exists (yes): } arg: &myvar arg: { } fungw-1.2.0/scconfig/src/tmpasm/regression/switch.gasm0000644000175100017510000000051012215755502021255 0ustar svnsvnput swdata {lol} switch swdata case data1 print {1a} {11}; print {1b} 12; print {1c} 13; print {1d} 14; end; case [~data2 ~a~~] print {2a} 21; print {2b} 22; print {2c} 23; print {2d} 24; end; default print {3a} 31; print {3b} 32; print {3c} 33; print {3d} 34; end; end; print {i1} print {i2} fungw-1.2.0/scconfig/src/tmpasm/regression/err_no_end.gasm0000644000175100017510000000006512217263041022065 0ustar svnsvnpritn {foo} switch {cond} case {1} print {foo}; end fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor08_uniq.ref0000644000175100017510000000150313216663535022133 0ustar svnsvnput [at 7:1] arg: list arg: {this is a list of words, a list of words. } print [at 18:1] arg: {original: } arg: list arg: { } uniq [at 19:1] arg: list print [at 20:1] arg: {uniq: } arg: list arg: { } put [at 24:1] arg: foo arg: {this foo is a this foo } uniq [at 31:1] arg: tmp arg: foo print [at 32:1] arg: {original: } arg: foo arg: { uniq: } arg: tmp arg: { } sortuniq [at 39:1] arg: tmp arg: foo print [at 40:1] arg: { sortuniq: } arg: tmp arg: { } put [at 49:1] arg: list arg: {#define foo #include "foo20.h" #include "foo10.h" /* misc1 */ #define bar #include "bar1.h" #include "bar2.h" /* misc2 */ } put [at 61:1] arg: /tmpasm/IFS arg: { } uniq [at 63:1] arg: tmp arg: list arg: {^#define} arg: {^#include} print [at 64:1] arg: {original: } arg: list arg: { grouped uniq: } arg: tmp arg: { } fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor06_foreach.ref0000644000175100017510000000077712217241351022564 0ustar svnsvnforeach item in {this is a list of words} [at 7:1] print [at 8:2] arg: item arg: { } put [at 15:1] arg: nl arg: { } foreach item in {foo bar baz} [at 16:1] foreach w in [~next: ~item~~nl~~] [at 17:2] print [at 18:3] arg: w put [at 27:1] arg: libs arg: {-lsdl -ltcl8.4} foreach l in libs [at 28:1] print [at 29:2] arg: {l=} arg: l arg: { } switch l [at 30:2] case {^-lsdl} put [at 31:19] arg: libs arg: [~-lm ~libs~ -lsvga~] print [at 34:1] arg: {libs=} arg: libs arg: { } fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor11_missing.ref0000644000175100017510000000017412752771512022624 0ustar svnsvnif ?a [at 5:7] then: print [at 6:2] arg: {empty} else: print [at 8:2] arg: {not empty} put [at 11:1] arg: b arg: ?a fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor12_halt.gasm0000644000175100017510000000061713256100515022246 0ustar svnsvn# Instruction "halt" breaks the execution of the current script file # (returning from the C call to tmpasm() or stop processing an "include", # returning to executing the parent script). It is useful for cheap # termination of the script file from deep inside nested loops/ifs. put tmp {true} if tmp then foreach item in {foo bar true baz} print item {\n} if item then halt end end end fungw-1.2.0/scconfig/src/tmpasm/regression/err_switch_end.ref0000644000175100017510000000011312217260230022566 0ustar svnsvnerror: unexpected end of if switch statement; expected a data at 1:7 (NOP) fungw-1.2.0/scconfig/src/tmpasm/regression/err_if_end.gasm0000644000175100017510000000001112217256047022046 0ustar svnsvnif v end fungw-1.2.0/scconfig/src/tmpasm/regression/err_switch_nocond.ref0000644000175100017510000000011312217260230023300 0ustar svnsvnerror: unexpected end of if switch statement; expected a data at 1:7 (NOP) fungw-1.2.0/scconfig/src/tmpasm/regression/switch.ref0000644000175100017510000000122412217241351021077 0ustar svnsvnput [at 1:1] arg: swdata arg: {lol} switch swdata [at 2:1] case data1 print [at 3:24] arg: {1a} arg: {11} print [at 3:41] arg: {1b} arg: 12 print [at 3:56] arg: {1c} arg: 13 print [at 3:71] arg: {1d} arg: 14 case [~data2 ~a~~] print [at 4:24] arg: {2a} arg: 21 print [at 4:41] arg: {2b} arg: 22 print [at 4:56] arg: {2c} arg: 23 print [at 4:71] arg: {2d} arg: 24 default print [at 5:24] arg: {3a} arg: 31 print [at 5:41] arg: {3b} arg: 32 print [at 5:56] arg: {3c} arg: 33 print [at 5:71] arg: {3d} arg: 34 print [at 7:1] arg: {i1} print [at 8:1] arg: {i2} fungw-1.2.0/scconfig/src/tmpasm/regression/err_if_end.ref0000644000175100017510000000014112217256047021677 0ustar svnsvnerror: unexpected "end" in "if" - expected "then" at 2:4 if v [at 2:1] then: (NOP) else: (NOP) fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor03_blocks.gasm0000644000175100017510000000175312216003062022567 0ustar svnsvn# Blocks are special syntax to make it easier to handle large, # to-be-printed-verbatim blocks of data, which is essential in a template # language. Blocks are enclosed in [$ $], where $ is an arbitrary character # that shall be chosen by the programmer, per block; once a characher is # chosen, it can not appear in the string. There is no backslash escaping # in blocks. Blocks can be used anywhere where strings could be used. print [@this is a string@] {\n} print {--\n} put myblk [!a block of multiline data. !] print myblk # A special feature of the block is inline variable substitution using the same # separator character chosen at the opening brace. In the example below # myvar is substituted because it is sorrounded by the separator (@ in # the first case and $ in the second case). Whitespace and newlines # are preserved in the block, even in the inline variable name! put myvar {world} print {--\n} print [@ hello @myvar@! @] {\n} print {--\n} print [$ hi @myvar@! $] print {--\n} fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor07_sub.ref0000644000175100017510000000106112217243651021737 0ustar svnsvnput [at 6:1] arg: myvar arg: {Hello world! } sub [at 7:1] arg: myvar arg: {l} arg: {2} print [at 8:1] arg: myvar sub [at 13:1] arg: {myvar} arg: {l} arg: {3} print [at 14:1] arg: myvar put [at 19:1] arg: pointer arg: {myvar} sub [at 20:1] arg: [~~pointer~~] arg: {l} arg: {4} print [at 21:1] arg: myvar put [at 25:1] arg: punctuation arg: {[!?.]} sub [at 26:1] arg: [~~pointer~~] arg: punctuation arg: [~ PUNCT:~punctuation~~] print [at 27:1] arg: myvar gsub [at 30:1] arg: [~~pointer~~] arg: {o} arg: {_0_} print [at 31:1] arg: myvar fungw-1.2.0/scconfig/src/tmpasm/regression/then.ref0000644000175100017510000000017312217241351020536 0ustar svnsvnif cnd [at 2:8] then: print [at 3:2] arg: a1 arg: a2 arg: a3 else: (NOP) print [at 6:1] arg: a1 arg: a2 arg: a3 fungw-1.2.0/scconfig/src/tmpasm/regression/test.ref0000644000175100017510000000202712750535606020572 0ustar svnsvnput [at 2:1] arg: /local/cflags arg: {-std=c99 -Wall} put [at 3:1] arg: /local/ldflags arg: {-lm} put [at 4:1] arg: /local/objs arg: {main.o foo.o bar.o} if /local/debug [at 7:17] then: append [at 8:2] arg: /local/cflags arg: {-g} else: append [at 10:2] arg: /local/cflags arg: {-O2} isempty [at 14:1] arg: /local/r arg: /local/somelib invert [at 15:1] arg: /local/r if /local/r [at 16:13] then: append [at 17:2] arg: /local/cflags arg: { -I/usr/include/somelib} append [at 18:2] arg: /local/ldflags arg: { -lsomelib} else: (NOP) print [at 22:1] arg: [~ # Makefile generated by scconfig - DO NOT EDIT - please edit Makefile.in CFLAGS=~/local/cflags~ LDFLAGS=~/local/ldflags~ OBJS=~/local/objs~ all: main main: $(OBJS) $(CC) $(LDFLAGS) ~] foreach /local/o in /local/objs [at 38:1] put [at 39:2] arg: /local/c arg: /local/o sub [at 40:2] arg: /local/c arg: {.o$} arg: {.c} print [at 41:2] arg: [~ ~/local/o~: ~/local/c~ $(CC) -c $(CFLAGS) ~/local/c~ -o ~/local/o~ ~] print [at 47:1] arg: {#end } fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor06_foreach.gasm0000644000175100017510000000240212216217543022727 0ustar svnsvn# The only loop tmpasm implements is a foreach that iterates on a list. # How the list is split into items is up to the environment. In scconfig # the list is white space separated by default. The word following foreach # must be the name of a variable (that will be set to the next item before # each iteration). The word after "in" is data (string, variable, block). foreach item in {this is a list of words} print item {\n} end # Like any other control, foreach can be nested. The following # example will iterate item on foo, bar and baz, printing 3 words # for each from a block: {next:}, the item and a newline. The newline # is specified as a vairable since \ escaping does not work in blocks. put nl {\n} foreach item in {foo bar baz} foreach w in [@next: @item@@nl@@] print w end end # Foreach makes a copy of the list before the first iteration. This # is relevant if the list is a variable that may change during the # loop. The following exmaple takes a list of libs and if -lsdl is # present on the list, appends -lsvga to the list and inserts -lm; # these changes to "libs" will not alter the loop. put libs {-lsdl -ltcl8.4} foreach l in libs print {l=} l {\n} switch l case {^-lsdl} put libs [@-lm @libs@ -lsvga@]; end end end print {libs=} libs {\n} fungw-1.2.0/scconfig/src/tmpasm/regression/then.gasm0000644000175100017510000000011212215755502020710 0ustar svnsvn# test if without an else if cnd then print a1 a2 a3 end print a1 a2 a3 fungw-1.2.0/scconfig/src/tmpasm/regression/comment.ref0000644000175100017510000000006612217241351021243 0ustar svnsvnprint [at 1:1] arg: data1 print [at 2:1] arg: data2 fungw-1.2.0/scconfig/src/tmpasm/regression/append.ref0000644000175100017510000000027512217512074021055 0ustar svnsvnappend [at 1:1] arg: tmp arg: {foo} append [at 2:1] arg: tmp arg: {bar} append [at 3:1] arg: tmp arg: {baz} foreach n in tmp [at 4:1] print [at 5:2] arg: {-> } arg: n arg: { } fungw-1.2.0/scconfig/src/tmpasm/regression/err_excess_end.ref0000644000175100017510000000010512217257260022571 0ustar svnsvnerror: Excess "end" at 3:4 if {1} [at 1:8] then: (NOP) else: (NOP) fungw-1.2.0/scconfig/src/tmpasm/regression/Tutor01_hello.ref0000644000175100017510000000030612217241351022237 0ustar svnsvnprint [at 7:1] arg: {hello world! } print [at 11:1] arg: {hello} arg: {world!} arg: { } print [at 14:1] print [at 17:1] arg: {HELLO} print [at 17:16] arg: { WORLD!} print [at 17:36] arg: { } fungw-1.2.0/scconfig/src/tmpasm/tmpasm_scconfig.c0000644000175100017510000003443213430210202020234 0ustar svnsvn#include #include #include #include "tmpasm.h" #include "db.h" #include "regex.h" #include "openfiles.h" #include "libs.h" #include "tmpasm_scconfig.h" #include "log.h" #include "regex.h" #ifndef TMPASM_PATH #define TMPASM_PATH "/tmpasm" #endif #ifndef IFS_PATH #define IFS_PATH TMPASM_PATH "/IFS" #endif #ifndef IFS_DEFAULT #define IFS_DEFAULT " \t\r\n" #endif #ifndef OFS_PATH #define OFS_PATH TMPASM_PATH "/OFS" #endif #ifndef OFS_DEFAULT #define OFS_DEFAULT "\n" #endif typedef struct scc_s { openfiles_t ofl; FILE *fout, *default_fout; const char *cwd; } scc_t; static const char *scc_runtime_error_fmts[] = { /* -0 */ "success scc %s", /* -1 */ "\"put\" requires exactly two arguments (got %s)", /* -2 */ "not enough arguments for sub; should be \"sub node pattern str\"%s", /* -3 */ "regex syntax error: %s", /* -4 */ "not enough arguments for uniq; should be \"uniq destnode\" or \"uniq destnode src\"%s", /* -5 */ "redir: too many arguments%s", /* -6 */ "redir: can't open %s", /* -7 */ "exiting due to a previous runtime error occurred in included file %s", /* -8 */ "can't include '%s': can't open file", /* -9 */ "\"put\" requires two or three arguments (got %s)", /* -10 */ "\"order\" requires 4 or 5 arguments: \"order destnode word before|after word\" or \"order destnode src word before|after word\"", /* -11 */ "\"uniq\" got too many grouping regular expressions", NULL }; static int print_runtime_error(tmpasm_t *ctx, const char *ifn) { if (ctx->runtime_error != 0) { const char *fmt = tmpasm_runtime_error_fmt(ctx); fprintf(stderr, "Runtime error at %s %d:%d: ", ifn, ctx->runtime_error_line, ctx->runtime_error_col); fprintf(stderr, fmt, (ctx->runtime_error_data == NULL ? "" : ctx->runtime_error_data)); fprintf(stderr, "\n"); return -1; } return 0; } /* allocate and build a full path using ud->cwd and fn */ static char *scc_path(scc_t *ud, const char *fn) { if (ud->cwd == NULL) return strclone(fn); return str_concat("", ud->cwd, "/", fn, NULL); } /******** db binding ********/ static const char *scc_get(tmpasm_t *ctx, const char *addr) { (void) ctx; /* not used */ if (*addr == '&') { /* return whether exists */ if (get(addr+1) != NULL) return strue; return sfalse; } if (*addr == '?') { /* safe get: return "" instead of NULL to avoid runtime error */ const char *res = get(addr+1); if (res == NULL) return ""; return res; } return get(addr); } static void scc_set(tmpasm_t *ctx, const char *addr, const char *data) { (void) ctx; /* not used */ put(addr, data); } static int scc_is_true(tmpasm_t *ctx, const char *data) { (void) ctx; /* not used */ return ((strcmp(data, "1") == 0) || istrue(data)); } static int scc_match(tmpasm_t *ctx, const char *str, const char *pat) { (void) ctx; /* not used */ re_comp(pat); return re_exec(str); } static const char *scc_ifs(tmpasm_t *ctx) { const char *ifs = get(IFS_PATH); (void) ctx; /* not used */ if (ifs == NULL) return IFS_DEFAULT; return ifs; } static const char *scc_ofs(tmpasm_t *ctx) { const char *ofs = get(OFS_PATH); (void) ctx; /* not used */ if (ofs == NULL) return OFS_DEFAULT; return ofs; } static const char *scc_next(tmpasm_t *ctx, void **state) { char **s = (char **)state; char *start; const char *IFS; IFS = scc_ifs(ctx); /* strip leading whitespace */ while(chr_inset(**s, IFS)) (*s)++; /* at the end of the string, no more tokens */ if (**s == '\0') return NULL; start = *s; /* skip non-whitespace */ while(!(chr_inset(**s, IFS)) && (**s != '\0')) (*s)++; if (**s != '\0') { **s = '\0'; (*s)++; } return start; } static const char *scc_first(tmpasm_t *ctx, void **state, char *list) { *state = list; return scc_next(ctx, state); } /******** instructions ********/ static void instr_put(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { char *addr, *val; (void) iname; /* not used */ if (argc != 2) { char str[16]; sprintf(str, "%d", argc); tmpasm_runtime_error(ctx, -1, str); return; } addr = tmpasm_arg2str(ctx, argv[0], 1); val = tmpasm_arg2str(ctx, argv[1], 0); if (*addr != '\0') put(addr, val); free(addr); free(val); } static void instr_resolve(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { char *dst, *srca; const char *src; (void) iname; /* not used */ if (argc != 2) { char str[16]; sprintf(str, "%d", argc); tmpasm_runtime_error(ctx, -1, str); return; } dst = tmpasm_arg2str(ctx, argv[0], 1); srca = tmpasm_arg2str(ctx, argv[1], 0); src = scc_get(ctx, srca); if (*dst != '\0') put(dst, src); free(dst); free(srca); } static void instr_append(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { char *addr, *val; char *sep; (void) iname; /* not used */ if ((argc < 2) || (argc > 3)) { char str[16]; sprintf(str, "%d", argc); tmpasm_runtime_error(ctx, -9, str); return; } addr = tmpasm_arg2str(ctx, argv[0], 1); val = tmpasm_arg2str(ctx, argv[1], 0); if (argc >= 3) sep = tmpasm_arg2str(ctx, argv[2], 0); else sep = strclone(scc_ofs(ctx)); if (*addr != '\0') { append(addr, sep); append(addr, val); } free(addr); free(val); free(sep); } static void instr_report(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { int n; (void) iname; /* not used */ for(n = 0; n < argc; n++) { char *val; val = tmpasm_arg2str(ctx, argv[n], 0); report("%s", val); free(val); } } static void instr_abort(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { scc_t *ud = (scc_t *)ctx->user_data; (void) iname; /* not used */ (void) argc; /* not used */ (void) argv; /* not used */ report("Abort requested by template.\n"); if (ud->fout) fflush(ud->fout); fflush(stdout); fflush(stderr); abort(); } static void instr_halt(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { (void) iname; /* not used */ (void) argc; /* not used */ (void) argv; /* not used */ ctx->halt = 1; } static void instr_sub(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { char *node, *pat, *err, *csub, *buff, *end; const char *start; const char *val; int score, slen, global; if (argc < 3) { tmpasm_runtime_error(ctx, -2, NULL); return; } node = tmpasm_arg2str(ctx, argv[0], 1); pat = tmpasm_arg2str(ctx, argv[1], 0); csub = tmpasm_arg2str(ctx, argv[2], 0); global = (*iname == 'g'); val = get(node); if (val == NULL) val=""; err = re_comp(pat); if (err != NULL) { tmpasm_runtime_error(ctx, -3, err); return; } slen = strlen(csub); if (global) buff = malloc(strlen(val)*(slen+3)+32); /* big enough for worst case, when every letter and $ and ^ are replaced with sub */ else buff = malloc(strlen(val)+slen+32); /* only one replacement will be done */ strcpy(buff, val); start = buff; do { score = re_exec(start); if (score == 0) break; end = buff + strlen(buff); if (eopat[0] - bopat[0] != slen) { int mlen = end - eopat[0]+1; if (mlen > 0) memmove((char *)(bopat[0] + slen), eopat[0], mlen); } memcpy((char *)bopat[0], csub, slen); start = bopat[0] + slen; } while(global); buff = realloc(buff, strlen(buff)+1); put(node, buff); free(buff); free(node); free(pat); free(csub); } #define UNIQ_ERE_MAX 16 static char *uniq_eres[UNIQ_ERE_MAX]; static void instr_uniq(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { char *node, *strlist, *buff; int eres = 0; if (argc < 1) { tmpasm_runtime_error(ctx, -4, NULL); return; } node = tmpasm_arg2str(ctx, argv[0], 1); if (argc > 1) { int offs = 2; strlist = tmpasm_arg2str(ctx, argv[1], 0); if ((argc-offs) >= UNIQ_ERE_MAX) { tmpasm_runtime_error(ctx, -11, NULL); return; } while(argc > offs) uniq_eres[eres++] = tmpasm_arg2str(ctx, argv[offs++], 0); if (eres > 0) uniq_eres[eres++] = ".*"; } else strlist = strclone(get(node)); buff = uniq_inc_str(strlist, scc_ifs(ctx), scc_ofs(ctx), (*iname == 's'), eres, uniq_eres); put(node, buff); free(buff); free(strlist); free(node); } static void instr_order(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { char *node, *strlist, *buff, *w1, *dirs, *w2; int offs, dir; if ((argc != 4) && (argc != 5)) { tmpasm_runtime_error(ctx, -10, NULL); return; } node = tmpasm_arg2str(ctx, argv[0], 1); if (argc > 4) { strlist = tmpasm_arg2str(ctx, argv[1], 0); offs = 2; } else { strlist = strclone(get(node)); offs = 1; } w1 = tmpasm_arg2str(ctx, argv[offs], 0); dirs = tmpasm_arg2str(ctx, argv[offs+1], 0); w2 = tmpasm_arg2str(ctx, argv[offs+2], 0); if (strcmp(dirs, "before") == 0) dir = -1; else if (strcmp(dirs, "after") == 0) dir = +1; else { tmpasm_runtime_error(ctx, -10, NULL); return; } buff = order_inc_str(strlist, scc_ifs(ctx), w1, dir, w2); put(node, buff); free(buff); free(strlist); free(node); } static void instr_print(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { int n; scc_t *ud = (scc_t *)ctx->user_data; (void) iname; /* not used */ for(n = 0; n < argc; n++) { char *val; val = tmpasm_arg2str(ctx, argv[n], 0); fprintf(ud->fout, "%s", val); free(val); } } static void instr_print_ternary(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { char *s_cond, *s; scc_t *ud = (scc_t *)ctx->user_data; (void) iname; /* not used */ if ((argc < 2) || (argc > 3)) { char str[16]; sprintf(str, "%d", argc); tmpasm_runtime_error(ctx, -1, str); return; } s_cond = tmpasm_arg2str(ctx, argv[0], 0); if (ctx->cb->is_true(ctx, s_cond)) s = tmpasm_arg2str(ctx, argv[1], 0); else s = tmpasm_arg2str(ctx, argv[2], 0); fprintf(ud->fout, "%s", s); free(s_cond); free(s); } static void scc_tmpasm_parse_(tmpasm_t *ctx, const char *cwd, FILE *fin, FILE *default_fout, FILE *fout) { scc_t *ud = malloc(sizeof(scc_t)); memset(&ud->ofl, 0, sizeof(ud->ofl)); ctx->user_data = ud; ud->default_fout = default_fout; ud->fout = fout; ud->cwd = cwd; for(;;) { int c; c = fgetc(fin); if (c == EOF) break; tmpasm_gotchar(ctx, c); } } void scc_tmpasm_parse(tmpasm_t *ctx, const char *cwd, FILE *fin, FILE *fout) { scc_tmpasm_parse_(ctx, cwd, fin, fout, fout); } #ifndef NO_FILE_IO static void instr_include(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { scc_t *ud = (scc_t *)ctx->user_data; int n; (void) iname; /* not used */ for(n = 0; n < argc; n++) { char *fn, *path; FILE *fin; tmpasm_t *child; fn = tmpasm_arg2str(ctx, argv[n], 0); path = scc_path(ud, fn); fin = fopen(path, "r"); if (fin == NULL) { tmpasm_runtime_error(ctx, -8, path); free(fn); free(path); return; } child = tmpasm_init(ctx->cb); scc_tmpasm_parse_(child, ud->cwd, fin, ud->default_fout, ud->fout); tmpasm_execute(child); if (print_runtime_error(child, path) != 0) tmpasm_runtime_error(ctx, -7, path); tmpasm_uninit(child); fclose(fin); free(fn); free(path); } } static void instr_redir(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { char *path, *fn, *mode; scc_t *ud = (scc_t *)ctx->user_data; (void) iname; /* not used */ fflush(ud->fout); switch(argc) { case 0: ud->fout = ud->default_fout; return; /* set redirection to default */ case 1: mode = strclone("w"); break; case 2: mode = tmpasm_arg2str(ctx, argv[1], 0); break; default: tmpasm_runtime_error(ctx, -5, NULL); return; } fn = tmpasm_arg2str(ctx, argv[0], 0); path = scc_path(ud, fn); ud->fout = openfile_open(&ud->ofl, path, mode); if (ud->fout == NULL) { char *err = malloc(strlen(fn) + strlen(path) + strlen(mode) + 16); sprintf(err, "%s (%s) for %s", path, fn, mode); tmpasm_runtime_error(ctx, -6, err); free(err); free(path); return; } free(fn); free(mode); free(path); } #endif #ifdef TMPASM_TESTER static void instr_unknown(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]) { printf("ERROR: unknown instruction '%s'\n", iname); } #endif /******** interface ********/ tmpasm_instr *scc_resolve(tmpasm_t *ctx, const char *name) { (void) ctx; /* not used */ /* TODO: make this a hash */ if (strcmp(name, "put") == 0) return instr_put; if (strcmp(name, "resolve") == 0) return instr_resolve; if (strcmp(name, "append") == 0) return instr_append; if (strcmp(name, "print") == 0) return instr_print; if (strcmp(name, "print_ternary") == 0) return instr_print_ternary; #ifndef TMPASM_NO_FILE_IO if (strcmp(name, "redir") == 0) return instr_redir; if (strcmp(name, "include") == 0) return instr_include; #endif if (strcmp(name, "report") == 0) return instr_report; if (strcmp(name, "abort") == 0) return instr_abort; if (strcmp(name, "halt") == 0) return instr_halt; if (strcmp(name, "uniq") == 0) return instr_uniq; if (strcmp(name, "order") == 0) return instr_order; if (strcmp(name, "sortuniq") == 0) return instr_uniq; if ((strcmp(name, "sub") == 0) || (strcmp(name, "gsub") == 0)) return instr_sub; #ifndef TMPASM_TESTER return NULL; #else return instr_unknown; #endif } static const char *scc_err_fmt(tmpasm_t *ctx) { int code; code = -ctx->runtime_error; if ((code < 0) || ((size_t)code > (sizeof(scc_runtime_error_fmts)/sizeof(char *)))) return NULL; return scc_runtime_error_fmts[code]; } static void scc_preexec(tmpasm_t *ctx) { (void) ctx; /* not used */ db_mkdir(TMPASM_PATH); } static void scc_postexec(tmpasm_t *ctx) { scc_t *ud = (scc_t *)ctx->user_data; openfile_free(&ud->ofl); free(ud); } tmpasm_cb_t scc_cb = { scc_get, scc_set, scc_is_true, scc_match, scc_first, scc_next, scc_resolve, scc_preexec, scc_postexec, scc_err_fmt }; int tmpasm(const char *wdir, const char *input, const char *output) { tmpasm_t *ctx; FILE *fin, *fout; int ret; scc_t ud_tmp; char *path; ud_tmp.cwd = wdir; path = scc_path(&ud_tmp, input); fin = fopen(path, "r"); if (fin == NULL) { fprintf(stderr, "ERROR: tmpasm: can not open script '%s' (%s in %s)\n", path, input, wdir); free(path); return -1; } free(path); path = scc_path(&ud_tmp, output); fout = fopen(path, "w"); if (fout == NULL) { fprintf(stderr, "ERROR: tmpasm: can not open output '%s' (%s in %s)\n", path, output, wdir); free(path); return -1; } free(path); ctx = tmpasm_init(&scc_cb); scc_tmpasm_parse_(ctx, wdir, fin, fout, fout); if (!ctx->dead) tmpasm_execute(ctx); fclose(fin); fclose(fout); ret = print_runtime_error(ctx, input); tmpasm_uninit(ctx); return ret; } FILE *tmpasm_fout(tmpasm_t *ctx) { scc_t *ud = (scc_t *)ctx->user_data; return ud->fout; } fungw-1.2.0/scconfig/src/tmpasm/tmpasm.h0000644000175100017510000001317313342530154016402 0ustar svnsvn#ifndef TMPASM_H #define TMPASM_H #ifndef TMPASM_INSTR_MAXLEN #define TMPASM_INSTR_MAXLEN 32 #endif typedef struct tmpasm_s tmpasm_t; typedef struct tmpasm_arg_s tmpasm_arg_t; struct tmpasm_arg_s { tmpasm_arg_t *next; /* block: the resulting string is a list of strings and addresses */ char is_addr; /* 1: arg is a node address; 0: arg is a string immediate */ char data[1]; /* arg string - obviously longer than 1 char (but there's not special hack for that in C89), \0 terminated */ }; /* user specified instruction prototype */ typedef void tmpasm_instr(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]); typedef struct tmpasm_cb_s { /* return the value of a node at addr - NULL is an error */ const char *(*get)(tmpasm_t *ctx, const char *addr); /* set the value of a node at addr to data; data may be NULL */ void (*set)(tmpasm_t *ctx, const char *addr, const char *data); /* return 1 if data is true, 0 otherwise; data may be NULL (if an unknown variable is referenced) */ int (*is_true)(tmpasm_t *ctx, const char *data); /* return 1 if str matches pat, 0 otherwise; str and pat may be NULL */ int (*match)(tmpasm_t *ctx, const char *str, const char *pat); /* first iteration over list; return the first element (or NULL to end); the string returned is not free'd by the caller */ const char *(*first)(tmpasm_t *ctx, void **state, char *list); /* return next element of a list or NULL on end (in which case state shall be also free'd by the caller); the string returned is not free'd by the caller */ const char *(*next)(tmpasm_t *ctx, void **state); /* resolve an instruction name to a function pointer */ tmpasm_instr *(*resolve)(tmpasm_t *ctx, const char *name); /* optional: called once before execution of a context starts */ void (*preexec)(tmpasm_t *ctx); /* optional: called once before execution of a context starts */ void (*postexec)(tmpasm_t *ctx); /* optional: resolve the current runtime error, called only for negative error codes; should return a format string with exactly one %s in it or NULL. */ const char *(*runtime_error_fmt)(tmpasm_t *ctx); } tmpasm_cb_t; int tmpasm_gotchar(tmpasm_t *ctx, char c); tmpasm_t *tmpasm_init(const tmpasm_cb_t *cb); void tmpasm_uninit(tmpasm_t *ctx); /* return the string version of an arg in a newly malloc()'d string if keep_addr is non-zero and a is a single address, no get() is run but the address is returned as a string */ char *tmpasm_arg2str(tmpasm_t *ctx, tmpasm_arg_t *a, int keep_addr); /* execute the code recursively until it exits */ void tmpasm_execute(tmpasm_t *ctx); /* Set or get the runtime error of a context. 0 means no error, negative codes are user errors handled by the runtime_error_fmt() callback and positive codes are internal error. */ void tmpasm_runtime_error(tmpasm_t *ctx, int code, const char *data); const char *tmpasm_runtime_error_fmt(tmpasm_t *ctx); /* --- internals: not required for normal use --- */ typedef enum { ST_PRECMD, /* waiting for a command to start - ignore whitespace */ ST_CMD, ST_PREDATA, /* waiting for data */ ST_PREBLOCKSEP, /* waiting for a block sep when opening a block */ ST_BLOCKSEP, /* found a block sep within the block - either an address or a termination follows */ ST_BLOCK, /* in [@ @] block, text part */ ST_BLOCK_INLINE, /* in [@ @] block, within inline @@ part */ ST_STRING, /* in {} string */ ST_STRING_ESCAPE, /* in {} string, right after a \ */ ST_ADDRESS, /* shifting address bytes */ ST_COMMENT /* after #, until the next newline */ } tmpasm_state_t; typedef enum { KW_none, KW_IF, KW_THEN, KW_ELSE, KW_END, KW_FOREACH, KW_IN, KW_SWITCH, KW_CASE, KW_DEFAULT, KW_NOP /* virtual instruction */ } tmpasm_kw_t; /* execution structs */ typedef struct tmpasm_exec_s tmpasm_exec_t; typedef struct tmpasm_case_s tmpasm_case_t; struct tmpasm_case_s { tmpasm_arg_t *data; tmpasm_exec_t *body; tmpasm_case_t *next; }; struct tmpasm_exec_s { tmpasm_kw_t kw; /* kw_none means a hook instruction */ union { struct { /* normal instruction */ tmpasm_instr *call; char *call_name; /* temporary */ int argc; tmpasm_arg_t **argv; } instr; struct { tmpasm_arg_t *cond; tmpasm_exec_t *code_then; tmpasm_exec_t *code_else; } fc_if; struct { char *loop_var; /* must be a single address */ tmpasm_arg_t *data; /* what to loop in */ tmpasm_exec_t *code_body; } fc_foreach; struct { tmpasm_arg_t *cond; tmpasm_case_t *first; tmpasm_case_t *last; } fc_switch; } payload; int line, col; tmpasm_exec_t *next; }; /* parser structs */ typedef struct stack_s tmpasm_stack_t; struct stack_s { tmpasm_state_t state; /* tmpasm_state_t kwstate; internal states of composite keywords like switch */ char cmd_buff[TMPASM_INSTR_MAXLEN+1]; unsigned int cmdi; tmpasm_kw_t kw, old_kw; char block_sep; int kwcol, kwline; int args_used, args_alloced; /* number of arguments in argv[] */ tmpasm_arg_t **argv; /* an array of linked lists */ tmpasm_arg_t **argend; /* each argv[] is a linked list (for blocks); argend points to the tail */ int *arg_alloced; /* of argend */ int *arg_used; /* of argend */ tmpasm_exec_t *last_code; /* tail of the code list */ tmpasm_stack_t *next; }; struct tmpasm_s { tmpasm_stack_t *st; int dead; int col, line; tmpasm_exec_t *code; tmpasm_exec_t *executing; /* points to the code most recently executed (or being executed when in callbacks) */ const tmpasm_cb_t *cb; int halt; int runtime_error; char *runtime_error_data; int runtime_error_line; int runtime_error_col; void *user_data; }; #endif fungw-1.2.0/scconfig/src/tmpasm/TODO0000644000175100017510000000053512220203631015405 0ustar svnsvn- regression test syntax errors and improve syntax error reporting - switch: - data instead of case - multiple defaults - deafult must be at end of the list - case out of switch - default out of switch - forarch - tutorial - append - [[]] eval? what's the output? -> generate and include scripts (may need mktemp binding) - update docs fungw-1.2.0/scconfig/src/tmpasm/openfiles.h0000644000175100017510000000066613366471752017106 0ustar svnsvn#include #include #include #include typedef struct openfile_s { FILE *f; /* identify the file: */ dev_t dev; ino_t ino; char *mode; } openfile_t; typedef struct openfiles_s { int alloced, used; openfile_t *files; } openfiles_t; FILE *openfile_open(openfiles_t *of, const char *fn, const char *mode); void openfile_closeall(openfiles_t *of); void openfile_free(openfiles_t *of); fungw-1.2.0/scconfig/src/tmpasm/openfiles.c0000644000175100017510000000374013366471752017075 0ustar svnsvn#include #include #include "openfiles.h" #include "libs.h" static openfile_t *find_file_by_name(openfiles_t *of, const char *name, int alloc, const char *mode, int recursion) { int n; struct stat buf; FILE *f; if (recursion > 4) { fprintf(stderr, "scconfig internal error: openfiles infinite recursion for %s\n", name); abort(); } if (stat(name, &buf) != 0) { /* File does not exist - try to create it or return NULL */ if (*mode == 'w') { f = fopen(name, "w"); if (f == NULL) return NULL; fclose(f); return find_file_by_name(of, name, alloc, mode, recursion + 1); } return NULL; } /* look for an existing open file in the list */ for(n = 0; n < of->used; n++) if ((of->files[n].dev == buf.st_dev) && (of->files[n].ino == buf.st_ino) && (strcmp(of->files[n].mode, mode) == 0)) return &(of->files[n]); if (!alloc) return NULL; /* File exists but not on the list yet, allocate a new slot for it */ /* TODO: try to find an empty slot first */ if (of->used >= of->alloced) { of->alloced += 16; of->files = realloc(of->files, sizeof(openfile_t) * of->alloced); } n = of->used; of->files[n].dev = buf.st_dev; of->files[n].ino = buf.st_ino; of->files[n].f = NULL; of->files[n].mode = strclone(mode); of->used++; return &(of->files[n]); } void release(openfile_t *o) { if (o->mode != NULL) { free(o->mode); o->mode = NULL; } if (o->f != NULL) { fclose(o->f); o->f = NULL; } o->dev = -1; o->ino = -1; } FILE *openfile_open(openfiles_t *of, const char *fn, const char *mode) { openfile_t *o; o = find_file_by_name(of, fn, 1, mode, 0); if (o == NULL) return NULL; o->f = fopen(fn, mode); if (o->f == NULL) { release(o); return NULL; } return o->f; } void openfile_closeall(openfiles_t *of) { int n; if (of->files == NULL) return; for(n = 0; n < of->used; n++) release(&(of->files[n])); } void openfile_free(openfiles_t *of) { openfile_closeall(of); if (of->files != NULL) free(of->files); } fungw-1.2.0/scconfig/src/tmpasm/tmpasm.c0000644000175100017510000004575213342530154016405 0ustar svnsvn#include #include #include #include #include "tmpasm.h" #include "debug.h" #define is_space(c) (((c) == ' ') || ((c) == '\t')) #define is_sep(c) (((c) == '\n') || ((c) == '\r') || ((c) == ';')) #define is_addr(c) ( (((c) >= '0') && ((c) <= '9')) || (((c) >= 'a') && ((c) <= 'z')) || (((c) >= 'A') && ((c) <= 'Z')) || ((c) == '_') || ((c) == '?') || ((c) == '.') || ((c) == ',') || ((c) == ',') || ((c) == '-') || ((c) == '/') || ((c) == '&') ) /* this local copy is to make tmpasm compile independently */ static char *strclone(const char *str) { int l; char *ret; if (str == NULL) return NULL; l = strlen(str)+1; ret = malloc(l); memcpy(ret, str, l); return ret; } #define TOP ctx->st static const char *kw_names[] = {"-", "if", "then", "else", "end", "foreach", "in", "switch", "case", "default", "nop", NULL }; static tmpasm_kw_t kw_lookup(const char *str) { const char **k; tmpasm_kw_t i; /* slow linear search is enough: we have only a few keywords */ for(k = kw_names, i = KW_none; *k != NULL; k++,i++) if (strcmp(*k, str) == 0) return i; return KW_none; } tmpasm_exec_t *code_new(tmpasm_kw_t kw) { tmpasm_exec_t *c; c = calloc(sizeof(tmpasm_exec_t), 1); c->kw = kw; return c; } /*tmpasm_exec_t *code_end(tmpasm_exec_t *start) { while(start->next != NULL) start = start->next; return start; }*/ tmpasm_exec_t *code_append(tmpasm_t *ctx, tmpasm_kw_t kw) { tmpasm_exec_t *c; /* c = code_end(TOP->code);*/ c = TOP->last_code; if (TOP->last_code->kw != KW_NOP) { c->next = code_new(kw); return c->next; } c->kw = kw; return c; } static void error(tmpasm_t *ctx, char c, char *msg) { fprintf(stderr, "error: %s at %d:%d\n", msg, ctx->line, ctx->col); if (c != 0) fprintf(stderr, " character last seen: %c\n", c); ctx->dead = 1; } static void push(tmpasm_t *ctx, tmpasm_kw_t kw, tmpasm_state_t st, tmpasm_exec_t *code) { tmpasm_stack_t *new; new = calloc(sizeof(tmpasm_stack_t), 1); new->kw = kw; new->state = st; new->next = ctx->st; new->last_code = code; ctx->st = new; } static void pop_(tmpasm_t *ctx, int chk_underfl) { tmpasm_stack_t *old; old = ctx->st; ctx->st = old->next; /* stack underflow? */ if (chk_underfl) { if (TOP == NULL) { error(ctx, 0, "Excess \"end\""); TOP = old; return; } } if (old->argv != NULL) free(old->argv); if (old->argend != NULL) free(old->argend); if (old->arg_used != NULL) free(old->arg_used); if (old->arg_alloced != NULL) free(old->arg_alloced); free(old); } static void pop(tmpasm_t *ctx) { pop_(ctx, 1); } #define grow(arr, size) arr = realloc(arr, sizeof((arr)[0]) * size) static void arg_new(tmpasm_t *ctx, int is_addr) { if (TOP->args_used >= TOP->args_alloced) { TOP->args_alloced = TOP->args_alloced + 16; grow(TOP->argv, TOP->args_alloced); grow(TOP->argend, TOP->args_alloced); grow(TOP->arg_alloced, TOP->args_alloced); grow(TOP->arg_used, TOP->args_alloced); } TOP->arg_alloced[TOP->args_used] = 64; TOP->arg_used[TOP->args_used] = 0; TOP->argv[TOP->args_used] = malloc(TOP->arg_alloced[TOP->args_used]+sizeof(tmpasm_arg_t)); TOP->argv[TOP->args_used]->is_addr = is_addr; TOP->argv[TOP->args_used]->next = NULL; TOP->argend[TOP->args_used] = TOP->argv[TOP->args_used]; TOP->args_used++; } static void arg_append(tmpasm_t *ctx, char c) { int i = TOP->args_used - 1; if (TOP->arg_used[i] >= TOP->arg_alloced[i]) { tmpasm_arg_t *prev, *last; /* since argend[i] is also in the ->next pointer of the previous item in a block chain, we need to look it up */ for(prev = NULL, last = TOP->argv[i]; last->next != NULL; last = last->next) prev = last; TOP->arg_alloced[i] += 64; last = realloc(last, TOP->arg_alloced[i]+sizeof(tmpasm_arg_t)); if (prev == NULL) TOP->argv[i] = last; else prev->next = last; TOP->argend[i] = last; } TOP->argend[i]->data[TOP->arg_used[i]] = c; TOP->arg_used[i]++; } static void arg_free(tmpasm_arg_t *a) { tmpasm_arg_t *next; if (a == NULL) return; next = a->next; free(a); if (next != NULL) arg_free(next); } static void arg_new_next(tmpasm_t *ctx, int is_addr) { tmpasm_arg_t *a; int id; arg_append(ctx, '\0'); id = TOP->args_used - 1; assert(id>=0); TOP->arg_alloced[id] = 64; TOP->arg_used[id] = 0; a = malloc(TOP->arg_alloced[id]+sizeof(tmpasm_arg_t)); strcpy(a->data, "QWERT"); a->is_addr = is_addr; a->next = NULL; TOP->argend[id]->next = a; TOP->argend[id] = a; } static void arg_remove(tmpasm_t *ctx) { assert(TOP->args_used == 1); TOP->args_used = 0; TOP->argv[0] = NULL; TOP->argend[0] = NULL; TOP->arg_alloced[0] = 0; TOP->arg_used[0] = 0; } static int arg_is_addr(tmpasm_arg_t *a) { return (a->next == NULL) && (a->is_addr); } static void arg_end(tmpasm_t *ctx, int cmd_ctx) { tmpasm_arg_t *a; arg_append(ctx, '\0'); a = TOP->argv[TOP->args_used-1]; if (cmd_ctx) { /* when argument ends in a command context (not in a block inline), we may may need to switch back to command mode; example: after the cond of an "if cond then"*/ switch(TOP->kw) { case KW_IF: TOP->state = ST_PRECMD; break; case KW_FOREACH: if (!arg_is_addr(a)) { error(ctx, 0, "variable of a foreach must be an address"); return; } TOP->last_code->payload.fc_foreach.loop_var = strclone(a->data); arg_free(a); arg_remove(ctx); TOP->state = ST_PRECMD; break; case KW_IN: /* pop will free the argv[] array, but not the elements so "a" is safe to use after this line */ pop(ctx); /* in foreach context, after the IN-data */ TOP->last_code->payload.fc_foreach.data = a; /* we are in the body now, TOP is the foreach context, last_code is body */ TOP->last_code->payload.fc_foreach.code_body = code_new(KW_NOP); push(ctx, KW_none, ST_PRECMD, TOP->last_code->payload.fc_foreach.code_body); break; case KW_CASE: ctx->st->next->last_code->payload.fc_switch.last->data = a; arg_remove(ctx); push(ctx, KW_none, ST_PRECMD, TOP->last_code); break; case KW_SWITCH: TOP->last_code->payload.fc_switch.cond = a; arg_remove(ctx); TOP->state = ST_PRECMD; break; default: TOP->state = ST_PREDATA; } } } /* end of statement; update kw state for a composite control kw; for the rest just call the lib */ static void end_of_statement(tmpasm_t *ctx) { switch(TOP->kw) { case KW_none: case KW_THEN: case KW_ELSE: case KW_CASE: case KW_DEFAULT: TOP->last_code->payload.instr.argc = TOP->args_used; TOP->last_code->payload.instr.argv = TOP->argv; TOP->argv = NULL; free(TOP->argend); TOP->argend = NULL; TOP->args_used = 0; TOP->args_alloced = 0; break; default: /* don't mess with the payload */ ; } TOP->state = ST_PRECMD; } #define loc_update() \ do { \ TOP->last_code->line = TOP->kwline; \ TOP->last_code->col = TOP->kwcol; \ } while(0) static void got_kw(tmpasm_t *ctx, tmpasm_kw_t kw, int terminated) { switch(kw) { case KW_END: /* then-else threads have their own subcontext within the if subcontext; end needs to pop the innermost subcontext before terminating the if context */ if (TOP->kw == KW_IF) { error(ctx, 0, "unexpected \"end\" in \"if\" - expected \"then\""); goto bind_if_cond; } if ((TOP->kw == KW_ELSE) || (TOP->kw == KW_THEN)) pop(ctx); if (TOP->kw == KW_SWITCH) TOP->kw = TOP->old_kw; else { pop(ctx); if ((TOP->kw == KW_CASE) || (TOP->kw == KW_DEFAULT)) pop(ctx); } TOP->state = ST_PRECMD; /* have to restore context keyword after these */ if (TOP->kw == KW_FOREACH) TOP->kw = TOP->old_kw; break; case KW_IF: if (terminated) { error(ctx, 0, "unexpected end of if statement; expected a condition"); return; } TOP->last_code = code_append(ctx, KW_IF); TOP->last_code->payload.fc_if.code_then = code_new(KW_NOP); TOP->last_code->payload.fc_if.code_else = code_new(KW_NOP); loc_update(); TOP->state = ST_PRECMD; /* prepare for reading a condition */ push(ctx, KW_IF, ST_PREDATA, TOP->last_code); break; case KW_THEN: /* we are in an if context, right after reading a condition */ if (TOP->kw != KW_IF) { error(ctx, 0, "unexpected 'then' - must be in an 'if' after the condition"); return; } bind_if_cond:; TOP->last_code->payload.fc_if.cond = TOP->argv[0]; loc_update(); arg_remove(ctx); push(ctx, KW_THEN, ST_PRECMD, TOP->last_code->payload.fc_if.code_then); break; case KW_ELSE: /* we are in an if context, after and end */ if (TOP->kw != KW_THEN) { error(ctx, 0, "unexpected 'else' - must be in a 'then' block before an else"); return; } pop(ctx); /* that was the then branch */ push(ctx, KW_ELSE, ST_PRECMD, TOP->last_code->payload.fc_if.code_else); break; case KW_FOREACH: if (terminated) { error(ctx, 0, "unexpected end of if foreach statement; expected an address"); return; } TOP->last_code = code_append(ctx, KW_FOREACH); loc_update(); TOP->state = ST_PREDATA; TOP->old_kw = TOP->kw; TOP->kw = KW_FOREACH; break; case KW_IN: if (TOP->kw != KW_FOREACH) error(ctx, 0, "unexpected \"in\"; should be after the address in foreach"); else push(ctx, KW_IN, ST_PREDATA, NULL); break; case KW_SWITCH: if (terminated) { error(ctx, 0, "unexpected end of if switch statement; expected a data"); return; } TOP->last_code = code_append(ctx, KW_SWITCH); TOP->state = ST_PREDATA; TOP->old_kw = TOP->kw; TOP->kw = KW_SWITCH; loc_update(); break; case KW_CASE: case KW_DEFAULT: if (TOP->kw == KW_SWITCH) { tmpasm_case_t *c; c = malloc(sizeof(tmpasm_case_t)); c->body = code_new(KW_NOP); c->data = NULL; c->next = NULL; if (TOP->last_code->payload.fc_switch.last == NULL) { TOP->last_code->payload.fc_switch.first = c; TOP->last_code->payload.fc_switch.last = c; } else { TOP->last_code->payload.fc_switch.last->next = c; TOP->last_code->payload.fc_switch.last = c; } if (kw == KW_DEFAULT) { push(ctx, KW_DEFAULT, ST_PRECMD, c->body); push(ctx, KW_none, ST_PRECMD, c->body); c->data = NULL; } else push(ctx, KW_CASE, ST_PREDATA, c->body); } else error(ctx, 0, "unexpected \"case\" or \"default\"; should be in a switch (is the last case terminated by an \"end\"?)"); break; default: TOP->last_code = code_append(ctx, KW_none); TOP->last_code->payload.instr.call_name = strclone(TOP->cmd_buff); if (TOP->last_code->payload.instr.call_name != NULL) { TOP->last_code->payload.instr.call = ctx->cb->resolve(ctx, TOP->last_code->payload.instr.call_name); loc_update(); } if (terminated) TOP->state = ST_PRECMD; else TOP->state = ST_PREDATA; } } static void comment_start(tmpasm_t *ctx) { push(ctx, KW_none, ST_COMMENT, NULL); } int tmpasm_gotchar(tmpasm_t *ctx, char c) { if (ctx->dead) return -1; switch(TOP->state) { case ST_COMMENT: if ((c == '\n') || (c == '\r')) { pop(ctx); if (TOP->state == ST_PREDATA) end_of_statement(ctx); } break; case ST_PRECMD: if (c == '#') { comment_start(ctx); break; } if (is_space(c) || is_sep(c)) break; TOP->cmdi = 0; TOP->state = ST_CMD; TOP->kwline = ctx->line; TOP->kwcol = ctx->col; /* fall thru */ case ST_CMD: /* end of command or keyword */ if (is_space(c) || is_sep(c)) { TOP->cmd_buff[TOP->cmdi] = '\0'; got_kw(ctx, kw_lookup(TOP->cmd_buff), is_sep(c)); } else { TOP->cmd_buff[TOP->cmdi] = c; TOP->cmdi++; if (TOP->cmdi >= sizeof(TOP->cmd_buff)) error(ctx, 0, "keyword or instruction name is too long"); } break; case ST_PREDATA: if (c == '#') { comment_start(ctx); break; } if (is_space(c)) break; if (is_sep(c)) end_of_statement(ctx); else if (c == '{') { TOP->state = ST_STRING; arg_new(ctx, 0); } else if (c == '[') { TOP->state = ST_PREBLOCKSEP; arg_new(ctx, 0); } else if (is_addr(c)) { TOP->state = ST_ADDRESS; arg_new(ctx, 1); arg_append(ctx, c); } else error(ctx, c, "unexpected character; expected '{' for starting a string or an address"); break; case ST_PREBLOCKSEP: TOP->block_sep = c; TOP->state = ST_BLOCK; break; case ST_BLOCK: if (c == TOP->block_sep) TOP->state = ST_BLOCKSEP; else arg_append(ctx, c); break; case ST_BLOCKSEP: if (c != ']') { arg_new_next(ctx, 1); arg_append(ctx, c); TOP->state = ST_BLOCK_INLINE; } else arg_end(ctx, 1); break; case ST_BLOCK_INLINE: if (c == TOP->block_sep) { arg_new_next(ctx, 0); TOP->state = ST_BLOCK; } else arg_append(ctx, c); break; case ST_STRING: if (c == '}') arg_end(ctx, 1); else if (c == '\\') TOP->state = ST_STRING_ESCAPE; else arg_append(ctx, c); break; case ST_STRING_ESCAPE: { char co; switch(c) { case 'n': co = '\n'; break; case 'r': co = '\r'; break; case 't': co = '\t'; break; case '\\': co = '\\'; break; case 'o': co = '{'; break; case 'c': co = '}'; break; default: co = c; } arg_append(ctx, co); TOP->state = ST_STRING; } break; case ST_ADDRESS: if (is_space(c)) arg_end(ctx, 1); else if (is_sep(c)) { arg_end(ctx, 1); end_of_statement(ctx); } else if (is_addr(c)) arg_append(ctx, c); else error(ctx, c, "unexpected character; expected next character of the address"); break; } if (c == '\n') { ctx->line++; ctx->col = 1; } else ctx->col++; return 0; } tmpasm_t *tmpasm_init(const tmpasm_cb_t *cb) { tmpasm_t *ctx; ctx = calloc(sizeof(tmpasm_t), 1); ctx->line = 1; ctx->col = 1; ctx->code = code_new(KW_NOP); ctx->cb = cb; push(ctx, KW_none, ST_PRECMD, ctx->code); return ctx; } static void free_exec(tmpasm_exec_t *e) { int n; tmpasm_case_t *c, *c_next; tmpasm_exec_t *e_next; for(; e != NULL; e = e_next) { e_next = e->next; switch(e->kw) { case KW_none: if (e->payload.instr.call_name != NULL) free(e->payload.instr.call_name); for(n = 0; n < e->payload.instr.argc; n++) arg_free(e->payload.instr.argv[n]); free(e->payload.instr.argv); break; case KW_IF: arg_free(e->payload.fc_if.cond); free_exec(e->payload.fc_if.code_then); free_exec(e->payload.fc_if.code_else); break; case KW_FOREACH: free(e->payload.fc_foreach.loop_var); arg_free(e->payload.fc_foreach.data); free_exec(e->payload.fc_foreach.code_body); break; case KW_SWITCH: arg_free(e->payload.fc_switch.cond); for(c = e->payload.fc_switch.first; c != NULL; c = c_next) { c_next = c->next; if (c->data != NULL) arg_free(c->data); free_exec(c->body); free(c); } break; default:; } free(e); } } void tmpasm_uninit(tmpasm_t *ctx) { free_exec(ctx->code); while (ctx->st != NULL) pop_(ctx, 0); if (ctx->runtime_error_data != NULL) free(ctx->runtime_error_data); free(ctx); } /****************** runtime ********************/ static const char *tmpasm_runtime_error_fmts[] = { "success %s", "variable '%s' does not exist", "empty argument (broken AST)%s", "compilation error: control block without an \"end\"; premature end of script%s", "attempt to call unresolved instruction '%s'", NULL }; void tmpasm_runtime_error(tmpasm_t *ctx, int code, const char *data) { ctx->runtime_error = code; if (ctx->runtime_error_data != NULL) free(ctx->runtime_error_data); ctx->runtime_error_data = strclone(data); if (ctx->executing != NULL) { ctx->runtime_error_line = ctx->executing->line; ctx->runtime_error_col = ctx->executing->col; } else { ctx->runtime_error_line = 0; ctx->runtime_error_col = 0; } } const char *tmpasm_runtime_error_fmt(tmpasm_t *ctx) { if (ctx->runtime_error == 0) return NULL; if ((ctx->runtime_error < 0) && (ctx->cb->runtime_error_fmt != NULL)) { const char *fmt; fmt = ctx->cb->runtime_error_fmt(ctx); if (fmt != NULL) return fmt; } if ((ctx->runtime_error < 0) || ((size_t)ctx->runtime_error > (sizeof(tmpasm_runtime_error_fmts)/sizeof(char *)))) return "invalid error code %s"; return tmpasm_runtime_error_fmts[ctx->runtime_error]; } char *tmpasm_arg2str(tmpasm_t *ctx, tmpasm_arg_t *a, int keep_addr) { if (a == NULL) { tmpasm_runtime_error(ctx, 2, NULL); return strclone(""); } if (a->next != NULL) { /* block mode */ int alloced = 0, used = 0; char *s = NULL; const char *i; for(;a != NULL; a = a->next) { int l; if (a->is_addr) { i = ctx->cb->get(ctx, a->data); if (i == NULL) { i = ""; tmpasm_runtime_error(ctx, 1, strclone(a->data)); } } else i = a->data; l = strlen(i); if (used + l >= alloced) { alloced = used + l + 256; s = realloc(s, alloced); } memcpy(s+used, i, l); used += l; } s[used] = '\0'; return s; } /* non-block */ if (a->is_addr) { const char *i; if (keep_addr) i = a->data; else i = ctx->cb->get(ctx, a->data); if (i == NULL) { i = ""; tmpasm_runtime_error(ctx, 1, strclone(a->data)); } return strclone(i); } return strclone(a->data); } static void execute(tmpasm_t *ctx, tmpasm_exec_t *e) { tmpasm_case_t *c; void *state; char *cond, *list; const char *i; while((e != NULL) && (ctx->runtime_error == 0) && (ctx->halt == 0)) { ctx->executing = e; switch(e->kw) { case KW_none: if (e->payload.instr.call != NULL) e->payload.instr.call(ctx, e->payload.instr.call_name, e->payload.instr.argc, e->payload.instr.argv); else tmpasm_runtime_error(ctx, 4, e->payload.instr.call_name); break; case KW_IF: cond = tmpasm_arg2str(ctx, e->payload.fc_if.cond, 0); if (ctx->cb->is_true(ctx, cond)) execute(ctx, e->payload.fc_if.code_then); else execute(ctx, e->payload.fc_if.code_else); free(cond); break; case KW_FOREACH: list = tmpasm_arg2str(ctx, e->payload.fc_foreach.data, 0); for(i = ctx->cb->first(ctx, &state, list); i != NULL; i = ctx->cb->next(ctx, &state)) { ctx->cb->set(ctx, e->payload.fc_foreach.loop_var, i); execute(ctx, e->payload.fc_foreach.code_body); } free(list); break; case KW_SWITCH: cond = tmpasm_arg2str(ctx, e->payload.fc_switch.cond, 0); for(c = e->payload.fc_switch.first; c != NULL; c = c->next) { char *cv = NULL; if (c->data != NULL) cv = tmpasm_arg2str(ctx, c->data, 0); if ((c->data == NULL) || (ctx->cb->match(ctx, cond, cv))) { execute(ctx, c->body); if (cv != NULL) free(cv); break; } if (cv != NULL) free(cv); } free(cond); break; default:; } e = e->next; } } void tmpasm_execute(tmpasm_t *ctx) { if (TOP->next != NULL) { ctx->executing = TOP->next->last_code; tmpasm_runtime_error(ctx, 3, NULL); return; } if ((TOP->state != ST_PRECMD) || (TOP->kw != KW_none)) { ctx->executing = TOP->last_code; tmpasm_runtime_error(ctx, 3, NULL); return; } ctx->halt = 0; ctx->runtime_error = 0; if (ctx->runtime_error_data != NULL) { free(ctx->runtime_error_data); ctx->runtime_error_data = NULL; } if (ctx->cb->preexec != NULL) ctx->cb->preexec(ctx); execute(ctx, ctx->code); if (ctx->cb->postexec != NULL) ctx->cb->postexec(ctx); } fungw-1.2.0/scconfig/src/tmpasm/tester.c0000644000175100017510000000224412220223061016365 0ustar svnsvn#include #include #include "tmpasm.h" #include "tmpasm_scconfig.h" #include "debug.h" #include "db.h" tmpasm_t *ctx; void re_fail(char *s, char c) { fprintf(stderr, "Regex error: %s [opcode %o]\n", s, c); abort(); } static void do_dump() { tmpasm_dump(ctx, stdout); } static void do_exec() { if (ctx->dead) fprintf(stderr, "Can not execute the script due to the above compilation error.\n"); else tmpasm_execute(ctx); } static void scc_init(void) { db_init(); db_mkdir("/local"); db_cd("/local"); } int main(int argc, char *argv[]) { scc_init(); ctx = tmpasm_init(&scc_cb); scc_tmpasm_parse(ctx, NULL, stdin, stdout); if (argc > 1) { char *cmd; cmd = argv[1]; while(*cmd == '-') cmd++; switch(*cmd) { case 'd': do_dump(); break; case 'e': do_exec(); break; } } else do_dump(); if (ctx->runtime_error != 0) { const char *fmt = tmpasm_runtime_error_fmt(ctx); fprintf(stderr, "Runtime error at %d:%d: ", ctx->runtime_error_line, ctx->runtime_error_col); fprintf(stderr, fmt, (ctx->runtime_error_data == NULL ? "" : ctx->runtime_error_data)); fprintf(stderr, "\n"); } tmpasm_uninit(ctx); db_uninit(); return 0; } fungw-1.2.0/scconfig/src/tmpasm/debug.c0000644000175100017510000000456412217255460016172 0ustar svnsvn#include #include "tmpasm.h" static void indent(FILE *f, int depth) { for(;depth > 0; depth--) fputc(' ', f); } static void print_arg(FILE *f, tmpasm_arg_t *a) { if (a == NULL) { fprintf(f, "*NULL - broken AST*"); return; } if (a->next != NULL) { /* block mode */ fprintf(f, "[~"); for(;a != NULL; a = a->next) { if (a->is_addr) fprintf(f, "~%s~", a->data); else fprintf(f, "%s", a->data); } fprintf(f, "~]"); } else { if (a->is_addr) fprintf(f, "%s", a->data); else fprintf(f, "{%s}", a->data); } } static void print_loc(FILE *f, tmpasm_exec_t *c) { if ((c->line != 0) || (c->col != 0)) fprintf(f, " [at %d:%d]\n", c->line, c->col); else fprintf(f, "\n"); } static void dump(FILE *f, int depth, tmpasm_exec_t *c) { tmpasm_case_t *cc; int n; for(; c != NULL; c = c->next) { switch(c->kw) { case KW_NOP: indent(f, depth); fprintf(f, "(NOP)"); print_loc(f, c); break; case KW_none: indent(f, depth); fprintf(f, "%s", c->payload.instr.call_name); print_loc(f, c); for(n = 0; n < c->payload.instr.argc; n++) { indent(f, depth+1); fprintf(f, "arg: "); print_arg(f, c->payload.instr.argv[n]); fprintf(f, "\n"); } break; case KW_IF: indent(f, depth); fprintf(f, "if "); print_arg(f, c->payload.fc_if.cond); print_loc(f, c); indent(f, depth); fprintf(f, "then:\n"); dump(f, depth+1, c->payload.fc_if.code_then); indent(f, depth); fprintf(f, "else:\n"); dump(f, depth+1, c->payload.fc_if.code_else); break; case KW_FOREACH: indent(f, depth); fprintf(f, "foreach %s in ", c->payload.fc_foreach.loop_var); print_arg(f, c->payload.fc_foreach.data); print_loc(f, c); dump(f, depth+1, c->payload.fc_foreach.code_body); break; case KW_SWITCH: indent(f, depth); fprintf(f, "switch "); print_arg(f, c->payload.fc_switch.cond); print_loc(f, c); for(cc = c->payload.fc_switch.first; cc != NULL; cc = cc->next) { indent(f, depth+1); if (cc->data != NULL) { fprintf(f, "case "); print_arg(f, cc->data); fprintf(f, "\n"); } else printf("default\n"); dump(f, depth+2, cc->body); } break; default: indent(f, depth); fprintf(f, "invalid kw "); print_loc(f, c); } } } void tmpasm_dump(tmpasm_t *ctx, FILE *f) { dump(f, 0, ctx->code); } fungw-1.2.0/scconfig/src/tmpasm/Makefile.plugin0000644000175100017510000000131513366471752017676 0ustar svnsvnTMPASM_OBJS = \ $(BIN)/tmpasm/tmpasm.o \ $(BIN)/tmpasm/tmpasm_scconfig.o \ $(BIN)/tmpasm/openfiles.o TMPASM_CFLAGS = -I$(SRC)/tmpasm $(BIN)/tmpasm/tmpasm.o: $(SRC)/tmpasm/tmpasm.c $(SRC)/tmpasm/tmpasm.h $(SRC)/default/dep.h $(SRC)/default/log.h $(SRC)/default/regex.h $(CC) $(CFLAGS) -c $(SRC)/tmpasm/tmpasm.c -o $(BIN)/tmpasm/tmpasm.o $(BIN)/tmpasm/tmpasm_scconfig.o: $(SRC)/tmpasm/tmpasm_scconfig.c $(SRC)/tmpasm/tmpasm.h $(SRC)/default/libs.h $(SRC)/default/log.h $(SRC)/default/regex.h $(CC) $(CFLAGS) -c $(SRC)/tmpasm/tmpasm_scconfig.c -o $(BIN)/tmpasm/tmpasm_scconfig.o $(BIN)/tmpasm/openfiles.o: $(SRC)/tmpasm/openfiles.c $(CC) $(CFLAGS) -c $(SRC)/tmpasm/openfiles.c -o $(BIN)/tmpasm/openfiles.o fungw-1.2.0/scconfig/src/tmpasm/debug.h0000644000175100017510000000005312215001565016155 0ustar svnsvnvoid tmpasm_dump(tmpasm_t *ctx, FILE *f); fungw-1.2.0/scconfig/src/tmpasm/Makefile0000644000175100017510000000071313366471752016402 0ustar svnsvnCFLAGS = -Wall -g \ -I../default -DTMPASM_TESTER \ tester: tester.o tmpasm.o debug.o tmpasm_scconfig.o openfiles.o \ ../default/db.o ../default/ht.o ../default/str.o ../default/log.o \ ../default/regex.o ../default/lib_uniqinc.o tmpasm.o: tmpasm.c tmpasm.h test: regression/Makefile cd regression && make regression/Makefile: regression/Makefile.in tester ./tester -e < regression/Makefile.in > regression/Makefile debug.o: debug.c debug.h tmpasm.h fungw-1.2.0/scconfig/hooks.c0000644000175100017510000000530314032254016014120 0ustar svnsvn#include "../libfungw/scconfig_hooks.h" #include "../libfungwbind/scconfig_hooks.h" static void help1(void) { argtbl_t *a; printf("./configure: configure fungw.\n"); printf("\n"); printf("Usage: ./configure [options]\n"); printf("\n"); printf("options are:\n"); printf(" --prefix=path change installation prefix from /usr to path\n"); printf(" --libdirname=name change the name of LIBDIR from lib to name (e.g. lib64)\n"); printf(" --pupdirname=path change the name of PUPDIR from lib/puplug to path\n"); printf(" --debug build full debug version (-g -O0, extra asserts)\n"); for(a = main_argument_table; a->arg != NULL; a++) { if (a->help != NULL) { char tmp[64]; sprintf(tmp, "%s=str", a->arg); printf(" --%-20s %s\n", tmp, a->help); } } } /* Runs when a custom command line argument is found returns true if no furhter argument processing should be done */ int hook_custom_arg(const char *key, const char *value) { if (strcmp(key, "debug") == 0) { fungw_set_debug(strue); return 1; } if (strcmp(key, "prefix") == 0) { fungw_set_prefix(value); return 1; } if (strcmp(key, "libdirname") == 0) { fungw_set_libdirname(value); return 1; } if (strcmp(key, "pupdirname") == 0) { fungw_set_pupdirname(value); return 1; } else if (strcmp(key, "help") == 0) { help1(); exit(0); } return 0; } /* Runs before anything else */ int hook_preinit() { return 0; } /* Runs after initialization */ int hook_postinit() { return fungw_hook_postinit() | fungwbind_hook_postinit(); } /* Runs after all arguments are read and parsed */ int hook_postarg() { return 0; } /* Runs when things should be detected for the host system */ int hook_detect_host() { return fungw_hook_detect_host(); } /* Runs when things should be detected for the target system */ int hook_detect_target() { return fungw_hook_detect_target() | fungwbind_hook_detect_target(); } /* Runs after detection hooks, should generate the output (Makefiles, etc.) */ int hook_generate() { unsigned generr = 0; generr |= fungw_hook_generate("../doc"); generr |= fungw_hook_generate("../libfungw"); generr |= fungw_hook_generate("../regression"); generr |= fungwbind_hook_generate("../libfungwbind"); fprintf(stderr, "Generating doc/examples/Makefile.conf (%d)\n", generr |= tmpasm("../doc/example", "Makefile.conf.in", "Makefile.conf")); fprintf(stderr, "Generating regression/static_lang.c (%d)\n", generr |= tmpasm("../regression", "static_lang.c.in", "static_lang.c")); fungw_print_configure_summary(); return generr; } /* Runs before everything is uninitialized */ void hook_preuninit() { } /* Runs at the very end, when everything is already uninitialized */ void hook_postuninit() { } fungw-1.2.0/scconfig/Makefile0000644000175100017510000000656713323015201014277 0ustar svnsvn# --- configuration part -- # - generic configuration - # where scconfig source is; this is a path to a partial or full checkout of # svn://repo.hu/scconfig/trunk/src SRC=src/ # where compiled binaries (e.g. objects) should land; should be the same as # $(SRC) the project has its own copy of scconfig embedded BIN=src/ # what cflags to use to compile scconfig USER_CFLAGS = -Wall -g -DGENCALL # what ldflags to use to link scconfig USER_LDFLAGS = # in case hooks.c needs to link to something local USER_OBJS = # what to build - a ./configure all: configure # This line imports scconfig core and default tests include $(SRC)/default/Makefile.plugin # # - PLUGINS - # # Comment this line if you are not interested in c99 features #include $(SRC)/c99/Makefile.plugin # Comment this line if you do not need script libs to be detected include $(SRC)/scripts/Makefile.plugin # Comment this line if you do not need parser libs to be detected #include $(SRC)/parser/Makefile.plugin # Comment this line if you do not need to detect parser generators #include $(SRC)/parsgen/Makefile.plugin # Comment this line if you do not need math related libs #include $(SRC)/math/Makefile.plugin # Comment this line if you do not need socket/networking #include $(SRC)/socket/Makefile.plugin # Comment this line if you do not need user/password API detection #include $(SRC)/userpass/Makefile.plugin # Comment this line if you do not need gui (X11, toolkits) #include $(SRC)/gui/Makefile.plugin # Comment this line if you do not need software utility libs (glib) #include $(SRC)/sul/Makefile.plugin # Uncomment this line if you need menus #include $(SRC)/menulib/Makefile.plugin # Comment this line if you do not need generator (templating); conflicts with tmpasm #include $(SRC)/generator/Makefile.plugin # Comment this line if you do not need tmpasm (templating); conflicts with generator include $(SRC)/tmpasm/Makefile.plugin # --- you shouldn't edit the lines below --- OBJS = $(USER_OBJS) hooks.o $(DEFAULT_NOMAIN_OBJS) $(SCRIPT_OBJS) $(PARSER_OBJS) $(GENERATOR_OBJS) $(TMPASM_OBJS) $(C99_OBJS) $(PARSGEN_OBJS) $(MATH_OBJS) $(SOCKET_OBJS) $(USERPASS_OBJS) $(GUI_OBJS) $(SUL_OBJS) CFLAGS = $(USER_CFLAGS) $(DEFAULT_CFLAGS) $(SCRIPT_CFLAGS) $(PARSER_CFLAGS) $(GENERATOR_CFLAGS) $(TMPASM_CFLAGS) $(C99_CFLAGS) $(PARSGEN_CFLAGS) $(MATH_CFLAGS) $(SOCKET_CFLAGS) $(USERPASS_CFLAGS) $(GUI_CFLAGS) $(SUL_CFLAGS) $(MENULIB_CFLAGS) -I$(SRC)/default LDFLAGS = $(USER_LDFLAGS) $(DEFAULT_LDFLAGS) $(SCRIPT_LDFLAGS) $(PARSER_LDFLAGS) $(GENERATOR_LDFLAGS) $(TMPASM_LDFLAGS) $(C99_LDFLAGS) $(PARSGEN_LDFLAGS) $(MATH_LDFLAGS) $(SOCKET_LDFLAGS) $(USERPASS_LDFLAGS) $(GUI_LDFLAGS) $(SUL_LDFLAGS) $(MENULIB_LDFLAGS) all: configure sccbox cquote configure: $(OBJS) $(DEFAULT_MAIN_OBJS) $(CC) -o configure $(OBJS) $(DEFAULT_MAIN_OBJS) menuconfig: $(OBJS) $(MENULIB_OBJS) $(CC) -o configure $(OBJS) $(MENULIB_OBJS) src/util/sccbox.o: src/util/sccbox.c $(CC) -c $(CFLAGS) -o src/util/sccbox.o src/util/sccbox.c sccbox: src/util/sccbox.o $(CC) $(LDFLAGS) -o sccbox src/util/sccbox.o src/util/cquote.o: src/util/cquote.c $(CC) -c $(CFLAGS) -o src/util/cquote.o src/util/cquote.c cquote: src/util/cquote.o $(CC) $(LDFLAGS) -o cquote src/util/cquote.o hooks.o: ../libfungw/scconfig_hooks.h ../libfungwbind/scconfig_hooks.h clean: rm $(OBJS) $(DEFAULT_MAIN_OBJS) configure sccbox cquote distclean: clean -rm config.cache config.log fungw-1.2.0/Makefile0000644000175100017510000000136614032252107012502 0ustar svnsvnall: cd libfungw && make all cd libfungwbind && make all cd regression && make all cd doc && make all clean: cd libfungw && make clean cd libfungwbind && make clean cd regression && make clean cd doc && make clean -cd src_3rd/genht && make clean distclean: cd regression && make distclean cd libfungw && make distclean cd libfungwbind && make distclean cd doc && make distclean cd scconfig && make distclean -cd src_3rd/genht && make clean install: cd libfungw && make install cd libfungwbind && make install cd doc && make install linstall: cd libfungw && make linstall cd libfungwbind && make linstall cd doc && make linstall uninstall: cd libfungw && make uninstall cd libfungwbind && make uninstall cd doc && make uninstall fungw-1.2.0/Release_notes0000644000175100017510000000034514047742734013571 0ustar svnsvnRelease notes for fungw 1.2.0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This release contains some bug fixes and bindings for two new scripting libs: 1. picol is a minimal tcl implementation 2. mujs is a small JavaScript implementation fungw-1.2.0/configure0000755000175100017510000000006313106762035012751 0ustar svnsvn#!/bin/sh cd scconfig && make && ./configure "$@"