/* || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. */ #include "global.h" #include "module_support.h" #include "interpret.h" #include "svalue.h" #include "stralloc.h" #include "pike_types.h" #include "pike_error.h" #include "mapping.h" #include "object.h" #include "operators.h" #include "bignum.h" #define sp Pike_sp /* Checks that args_to_check arguments are OK. * Returns 1 if everything worked ok, zero otherwise. * If something went wrong, 'exepect_result' tells you what went wrong. * Make sure to finish the argument list with a zero. */ static int va_check_args(struct svalue *s, int args_to_check, struct expect_result *res, va_list arglist) { res->error_type = ERR_NONE; res->expected = 0; for (res->argno=0; res->argno < args_to_check; res->argno++) { if(!(res->expected & BIT_MANY)) { res->expected = va_arg(arglist, unsigned int); if(!res->expected) { res->error_type = ERR_TOO_MANY; return 0; } } if (!((1UL << TYPEOF(s[res->argno])) & res->expected) && !(res->expected & BIT_ZERO && TYPEOF(s[res->argno]) == T_INT && s[res->argno].u.integer == 0)) { res->got = DO_NOT_WARN((unsigned char)TYPEOF(s[res->argno])); res->error_type = ERR_BAD_ARG; return 0; } } if(!(res->expected & BIT_MANY)) res->expected = va_arg(arglist, unsigned int); if(!res->expected || (res->expected & BIT_VOID)) return 1; res->error_type = ERR_TOO_FEW; return 0; } /* Returns the number of the first bad argument, * -X if there were too few arguments * or 0 if all arguments were OK. */ PMOD_EXPORT int check_args(int args, ...) { va_list arglist; struct expect_result tmp; va_start(arglist, args); va_check_args(sp - args, args, &tmp, arglist); va_end(arglist); if(tmp.error_type == ERR_NONE) return 0; return tmp.argno+1; } /* This function generates errors if any of the args first arguments * is not OK. */ PMOD_EXPORT void check_all_args(const char *fnname, int args, ... ) { va_list arglist; struct expect_result tmp; va_start(arglist, args); va_check_args(sp - args, args, &tmp, arglist); va_end(arglist); switch(tmp.error_type) { case ERR_NONE: return; case ERR_TOO_FEW: new_error(fnname, "Too few arguments.\n", sp, args, NULL, 0); case ERR_TOO_MANY: new_error(fnname, "Too many arguments.\n", sp, args, NULL, 0); case ERR_BAD_ARG: { char buf[1000]; int e; buf[0]=0; for(e=0;e<16;e++) { if(tmp.expected & (1<<e)) { if(buf[0]) { if(tmp.expected & 0xffff & (0xffff << e)) strcat(buf,", "); else strcat(buf," or "); } strcat(buf, get_name_of_type(e)); } } Pike_error("Bad argument %d to %s(), (expecting %s, got %s)\n", tmp.argno+1, fnname, buf, get_name_of_type(tmp.got)); } } } /* get_args and get_all_args type specifiers: * * %i: INT_TYPE * %I: int or float -> INT_TYPE * %d: int (the c type "int" which may vary from INT_TYPE) * %D: int of float -> int * %+: positive int -> INT_TYPE * %l: int or bignum -> LONGEST * %c: char * Only narrow (8 bit) strings without NUL. * %C: char * or NULL Only narrow (8 bit) strings without NUL. * %n: struct pike_string * Only narrow (8 bit) strings. * %N: struct pike_string * or NULL Only narrow (8 bit) strings. * %t: struct pike_string * Any string width. (*) * %T: struct pike_string * or NULL Any string width. (*) * %a: struct array * * %A: struct array * or NULL * %f: float -> FLOAT_TYPE * %F: float or int -> FLOAT_TYPE * %m: struct mapping * * %G: struct mapping * or NULL (*) * %u: struct multiset * * %U: struct multiset * or NULL * %o: struct object * * %O: struct object * or NULL * %p: struct program * * %P: struct program * or NULL * %*: struct svalue * * * For compatibility: * * %s: char * Only 8 bit strings without NUL. * %S: struct pike_string * Only 8 bit strings * %W: struct pike_string * Allow wide strings * %M: struct multiset * * * A period can be specified between type specifiers to mark the start * of optional arguments. If the real arguments run out in the list of * optional arguments, or if a real argument is UNDEFINED in an * optional argument position, the corresponding pointer won't be * assigned at all. * * WARNING: If you use a period to parse optional arguments, then you * should _always_ initialize the corresponding variables before * calling get_args/get_all_args. Just looking at num_args afterwards * is not safe since the user might have passed UNDEFINED, in which * case the variable won't be assigned anyway. * * Note: If there are more arguments than there are type specifiers * the excessive arguments will be silently ignored. This may change * in the future, in which case an extra marker must be added to get * this behaviour. * * Note: A lowercase type specifier (i.e. one that doesn't accept * NULL) in the optional args list leads to behavior that breaks * common coding conventions. Try to avoid it. * * *) Contrived letter since the logical one is taken for historical * reasons. :\ */ /* Values for the info flag: */ #define ARGS_OK 0 /* At end of args and fmt. */ #define ARGS_OPT -1 /* At end of args but not fmt and has passed a period. */ #define ARGS_SHORT -2 /* At end of args but not fmt and has not passed a period. */ #define ARGS_LONG -3 /* At end of fmt but not args. */ #define ARGS_NUL_IN_STRING -4 /* 8 bit string got embedded NUL. */ #define ARGS_SUBTYPED_OBJECT -5 /* An object with a subtype. */ /* Positive values: Stopped at arg with invalid type. The value is the * format letter for the expected type. */ static int va_get_args_2(struct svalue *s, INT32 num_args, const char *fmt, va_list ap, int *info) { int ret=0; int optional = 0; while(*fmt) { void *ptr; if (*fmt == '.' && !optional) { fmt++; optional = 1; continue; } if(*fmt != '%') Pike_fatal("Error in format for get_args or get_all_args.\n"); if(ret == num_args) { if (optional) *info = ARGS_OPT; else *info = ARGS_SHORT; return ret; } ptr = va_arg(ap, void *); #define cast_arg(PTR, TYPE) ((TYPE)(PTR)) #ifdef PIKE_DEBUG /* Try to check that the caller has given the variable for every * optional argument a value, because otherwise the caller is * almost certainly doing something stupid. See the warning in the * blurb above. */ if (optional && PIKE_MEM_NOT_DEF (*cast_arg (ptr, char *))) Pike_fatal ("Detected undefined default value for optional argument.\n"); #endif if (optional && IS_UNDEFINED (s)) { /* An optional argument with an undefined value should be * treated as if it isn't given at all, i.e. don't assign this * argument. */ fmt++; } else switch(*++fmt) { case 'd': if(TYPEOF(*s) != T_INT) goto type_err; /* FIXME: Range checks, including bignum objects. */ *cast_arg(ptr, int *)=s->u.integer; break; case 'i': if(TYPEOF(*s) != T_INT) goto type_err; /* FIXME: Error reporting for bignum objects. */ *cast_arg(ptr, INT_TYPE *)=s->u.integer; break; case '+': if(TYPEOF(*s) != T_INT) goto type_err; if(s->u.integer<0) goto type_err; /* FIXME: Error reporting for bignum objects. */ *cast_arg(ptr, INT_TYPE *)=s->u.integer; break; case 'D': if(TYPEOF(*s) == T_INT) /* FIXME: Range checks. */ *cast_arg(ptr, int *)=s->u.integer; else if(TYPEOF(*s) == T_FLOAT) /* FIXME: Range checks. */ *cast_arg(ptr, int *)= DO_NOT_WARN((int)s->u.float_number); else { ref_push_type_value(int_type_string); push_svalue( s ); f_cast( ); if(TYPEOF(sp[-1]) == T_INT) /* FIXME: Range checks. */ *cast_arg(ptr, int *)=sp[-1].u.integer; else if(TYPEOF(*s) == T_FLOAT) /* FIXME: Range checks. Btw, does this case occur? */ *cast_arg(ptr, int *)= DO_NOT_WARN((int)sp[-1].u.float_number); else Pike_error("Cast to int failed.\n"); pop_stack(); } break; case 'I': if(TYPEOF(*s) == T_INT) *cast_arg(ptr, INT_TYPE *)=s->u.integer; else if(TYPEOF(*s) == T_FLOAT) /* FIXME: Range checks. */ *cast_arg(ptr, INT_TYPE *) = DO_NOT_WARN((INT_TYPE)s->u.float_number); else { /* FIXME: Error reporting for bignum objects. */ ref_push_type_value(int_type_string); push_svalue( s ); f_cast( ); if(TYPEOF(sp[-1]) == T_INT) *cast_arg(ptr, INT_TYPE *)=sp[-1].u.integer; else if(TYPEOF(*s) == T_FLOAT) /* FIXME: Range checks. Btw, does this case occur? */ *cast_arg(ptr, INT_TYPE *)= DO_NOT_WARN((INT_TYPE)sp[-1].u.float_number); else Pike_error("Cast to int failed.\n"); pop_stack(); } break; case 'l': if (TYPEOF(*s) == T_INT) { *cast_arg(ptr, LONGEST *)=s->u.integer; break; } else if (is_bignum_object_in_svalue(s) && int64_from_bignum(cast_arg(ptr, LONGEST *), s->u.object) == 1) { break; } /* FIXME: Error reporting for bignum objects. */ goto type_err; case 'C': if(TYPEOF(*s) != T_STRING && UNSAFE_IS_ZERO (s)) { *cast_arg(ptr, char **) = NULL; break; } /* FALL THROUGH */ case 'c': case 's': if(TYPEOF(*s) != T_STRING) goto type_err; if(s->u.string->size_shift) goto type_err; if(string_has_null(s->u.string)) { *info = ARGS_NUL_IN_STRING; return ret; } *cast_arg(ptr, char **)=s->u.string->str; break; case 'N': if(TYPEOF(*s) != T_STRING && UNSAFE_IS_ZERO (s)) { *cast_arg(ptr, struct pike_string **) = NULL; break; } /* FALL THROUGH */ case 'n': case 'S': if(TYPEOF(*s) != T_STRING) goto type_err; if(s->u.string->size_shift) goto type_err; *cast_arg(ptr, struct pike_string **)=s->u.string; break; case 'T': if(TYPEOF(*s) != T_STRING && UNSAFE_IS_ZERO (s)) { *cast_arg(ptr, struct pike_string **) = NULL; break; } /* FALL THROUGH */ case 't': case 'W': if(TYPEOF(*s) != T_STRING) goto type_err; *cast_arg(ptr, struct pike_string **)=s->u.string; break; case 'A': if(TYPEOF(*s) != T_ARRAY && UNSAFE_IS_ZERO (s)) { *cast_arg(ptr, struct array **) = NULL; break; } /* FALL THROUGH */ case 'a': if(TYPEOF(*s) != T_ARRAY) goto type_err; *cast_arg(ptr, struct array **)=s->u.array; break; case 'f': if(TYPEOF(*s) != T_FLOAT) goto type_err; *cast_arg(ptr, FLOAT_TYPE *)=s->u.float_number; break; case 'F': if(TYPEOF(*s) == T_FLOAT) *cast_arg(ptr, FLOAT_TYPE *)=s->u.float_number; else if(TYPEOF(*s) == T_INT) *cast_arg(ptr, FLOAT_TYPE *)=(FLOAT_TYPE)s->u.integer; else { ref_push_type_value(float_type_string); push_svalue( s ); f_cast( ); *cast_arg(ptr, FLOAT_TYPE *)=sp[-1].u.float_number; pop_stack(); } break; case 'G': if(TYPEOF(*s) != T_MAPPING && UNSAFE_IS_ZERO (s)) { *cast_arg(ptr, struct mapping **) = NULL; break; } /* FALL THROUGH */ case 'm': if(TYPEOF(*s) != T_MAPPING) goto type_err; *cast_arg(ptr, struct mapping **)=s->u.mapping; break; case 'U': if(TYPEOF(*s) != T_MULTISET && UNSAFE_IS_ZERO (s)) { *cast_arg(ptr, struct multiset **) = NULL; break; } /* FALL THROUGH */ case 'u': case 'M': if(TYPEOF(*s) != T_MULTISET) goto type_err; *cast_arg(ptr, struct multiset **)=s->u.multiset; break; case 'O': if(TYPEOF(*s) != T_OBJECT && UNSAFE_IS_ZERO (s)) { *cast_arg(ptr, struct object **) = NULL; break; } /* FALL THROUGH */ case 'o': if(TYPEOF(*s) != T_OBJECT) goto type_err; if (SUBTYPEOF(*s)) { *info = ARGS_SUBTYPED_OBJECT; return ret; } *cast_arg(ptr, struct object **)=s->u.object; break; case 'P': case 'p': switch(TYPEOF(*s)) { case T_PROGRAM: *cast_arg(ptr, struct program **)=s->u.program; break; case T_FUNCTION: if((*cast_arg(ptr, struct program **)=program_from_svalue(s))) break; default: if (*fmt == 'P' && UNSAFE_IS_ZERO(s)) *cast_arg(ptr, struct program **) = NULL; goto type_err; } break; case '*': *cast_arg(ptr, struct svalue **)=s; break; default: Pike_fatal("Unknown format character %d.\n", *fmt); } ret++; s++; fmt++; } if (ret == num_args) *info = ARGS_OK; else *info = ARGS_LONG; return ret; type_err: *info = *fmt; return ret; } /* Compat wrapper, just in case. */ int va_get_args(struct svalue *s, INT32 num_args, const char *fmt, va_list ap) { int info; return va_get_args_2 (s, num_args, fmt, ap, &info); } #ifdef NOT_USED /* get_args does NOT generate errors, it simply returns how * many arguments were actually matched. * usage: get_args(sp-args, args, "%i",&an_int) */ PMOD_EXPORT int get_args(struct svalue *s, INT32 num_args, const char *fmt, ...) { va_list ptr; int ret, info; va_start(ptr, fmt); ret=va_get_args_2(s, num_args, fmt, ptr, &info); va_end(ptr); return ret; } #endif PMOD_EXPORT void get_all_args(const char *fname, INT32 args, const char *format, ... ) { va_list ptr; int ret, info; va_start(ptr, format); ret=va_get_args_2(sp-args, args, format, ptr, &info); va_end(ptr); switch (info) { case ARGS_OK: case ARGS_OPT: case ARGS_LONG: break; case ARGS_NUL_IN_STRING: bad_arg_error( fname, sp-args, args, ret+1, "string (8bit)", sp+ret-args, "Bad argument %d to %s(). NUL char in string not allowed.\n", ret+1, fname); /* NOT REACHED */ case ARGS_SUBTYPED_OBJECT: bad_arg_error(fname, sp-args, args, ret+1, "object", sp+ret-args, "Subtyped objects are not supported.\n"); /* NOT_REACHED */ case ARGS_SHORT: default: { char *expected_type; /* NB: For ARGS_SHORT we know there's no period in format that * might offset the format specs. */ switch(info == ARGS_SHORT ? format[ret*2+1] : info) { case 'd': case 'i': case 'l': expected_type = "int"; break; case 'D': case 'I': expected_type = "int|float"; break; case '+': expected_type = "int(0..)"; break; case 'c': case 's': case 'n': case 'S': expected_type = "string (8bit)"; break; case 'C': case 'N': expected_type = "string (8bit) or zero"; break; case 't': case 'W': expected_type = "string"; break; case 'T': expected_type = "string or zero"; break; case 'a': expected_type = "array"; break; case 'A': expected_type = "array or zero"; break; case 'f': expected_type = "float"; break; case 'F': expected_type = "float|int"; break; case 'm': expected_type = "mapping"; break; case 'G': expected_type = "mapping or zero"; break; case 'M': case 'u': expected_type = "multiset"; break; case 'U': expected_type = "multiset or zero"; break; case 'o': expected_type = "object"; break; case 'O': expected_type = "object or zero"; break; case 'p': expected_type = "program"; break; case 'P': expected_type = "program or zero"; break; case '*': expected_type = "mixed"; break; default: #ifdef PIKE_DEBUG Pike_fatal ("get_all_args not in sync with low_va_get_args.\n"); #else expected_type = NULL; /* To avoid warnings. */ #endif } if (info != ARGS_SHORT) { bad_arg_error( fname, sp-args, args, ret+1, expected_type, sp+ret-args, "Bad argument %d to %s(). Expected %s.\n", ret+1, fname, expected_type); } else { const char *req_args_end = strchr (format, '.'); if (!req_args_end) req_args_end = strchr (format, 0); bad_arg_error( fname, sp-args, args, ret+1, expected_type, 0, "Too few arguments to %s(). Expected %"PRINTPTRDIFFT"d arguments, got %d.\n" "The type of the next argument is expected to be %s.\n", fname, (req_args_end - format) / 2, args, expected_type); } /* NOT REACHED */ } } } /* NOTA BENE: * The code below assumes that dynamic modules are not * unloaded from memory... */ static struct mapping *exported_symbols; PMOD_EXPORT void pike_module_export_symbol(const char *name, int len, void *ptr) { struct pike_string *str=make_shared_binary_string(name,len); struct svalue s; if(!exported_symbols) exported_symbols=allocate_mapping(10); SET_SVAL(s, T_INT, NUMBER_NUMBER, ptr, ptr); mapping_string_insert(exported_symbols, str, &s); free_string(str); } PMOD_EXPORT void *pike_module_import_symbol(const char *name, int len, const char *module, int module_len) { struct svalue *s; struct pike_string *str=make_shared_binary_string(name,len); if(exported_symbols) { s=low_mapping_string_lookup(exported_symbols, str); if(s) { #ifdef PIKE_DEBUG if (TYPEOF(*s) != T_INT || SUBTYPEOF(*s) != NUMBER_NUMBER) Pike_fatal("Unexpected value in exported_symbols.\n"); #endif free_string(str); return s->u.ptr; } } /* Load the module */ push_string(make_shared_binary_string(module,module_len)); SAFE_APPLY_MASTER("resolv",1); pop_stack(); if(exported_symbols) { s=low_mapping_string_lookup(exported_symbols, str); if(s) { #ifdef PIKE_DEBUG if (TYPEOF(*s) != T_INT || SUBTYPEOF(*s) != NUMBER_NUMBER) Pike_fatal("Unexpected value in exported_symbols.\n"); #endif free_string(str); return s->u.ptr; } } free_string(str); return 0; } #ifdef DO_PIKE_CLEANUP void cleanup_module_support (void) { if (exported_symbols) { free_mapping (exported_symbols); exported_symbols = NULL; } } #endif