pike.git / src / builtin_functions.c

version» Context lines:

pike.git/src/builtin_functions.c:1640:      #define NT_COMBINE_PATH   #include "combine_path.h"      #define UNIX_COMBINE_PATH   #include "combine_path.h"      #define AMIGAOS_COMBINE_PATH   #include "combine_path.h"    + static ptrdiff_t find_last_path_separator(struct pike_string *path, +  p_wchar2 sep, +  ptrdiff_t pos) + { +  switch(path->size_shift) { +  case eightbit: +  { +  p_wchar0 *str = STR0(path); +  while (pos--) { +  if (str[pos] == sep) return pos; +  } +  } +  break; +  case sixteenbit: +  { +  p_wchar1 *str = STR1(path); +  while (pos--) { +  if (str[pos] == sep) return pos; +  } +  } +  break; +  case thirtytwobit: +  { +  p_wchar2 *str = STR2(path); +  while (pos--) { +  if (str[pos] == sep) return pos; +  } +  } +  break; +  default: +  Pike_fatal("Unsupported string width.\n"); +  break; +  } +  return -1; + }    -  + /*! @decl string dirname(string path) +  *! +  *! Returns all but the last segment of a path. Some example inputs and +  *! outputs: +  *! +  *! @xml{<matrix> +  *! <r><c><b>Expression</b></c><c><b>Value</b></c></r> +  *! <r><c>dirname("/a/b")</c><c>"/a"</c></r> +  *! <r><c>dirname("/a/")</c><c>"/a"</c></r> +  *! <r><c>dirname("/a")</c><c>"/"</c></r> +  *! <r><c>dirname("/")</c><c>"/"</c></r> +  *! <r><c>dirname("")</c><c>""</c></r> +  *! </matrix>@} +  *! +  *! @seealso +  *! @[basename()], @[explode_path()] +  */ + PMOD_EXPORT void f_dirname(INT32 args) + { +  struct pike_string *s = NULL; +  ptrdiff_t pos, pos2 = -1;    -  + #ifdef __NT__ +  check_all_args("dirname", args, BIT_STRING, 0); +  push_text("\\"); +  push_text("/"); +  f_replace(3); + #endif +  +  get_all_args("dirname", args, "%t", &s); +  +  pos = find_last_path_separator(s, '/', s->len); +  if (pos < 0) { +  ref_push_string(empty_pike_string); +  } else if (pos) { +  push_string(string_slice(s, 0, pos)); +  } else { +  push_text("/"); +  } +  stack_pop_n_elems_keep_top(args); + } +  + /*! @decl string basename(string path) +  *! +  *! Returns the last segment of a path. +  *! +  *! @seealso +  *! @[dirname()], @[explode_path()] +  */ + PMOD_EXPORT void f_basename(INT32 args) + { +  struct pike_string *s = NULL; +  ptrdiff_t pos, pos2 = -1; +  +  get_all_args("basename", args, "%t", &s); +  +  pos = find_last_path_separator(s, '/', s->len); + #ifdef __NT__ +  pos2 = find_last_path_separator(s, '\\', s->len); +  if (pos2 > pos) pos = pos2; + #endif /* __NT__ */ +  if (pos < 0) { +  return; +  } +  push_string(string_slice(s, pos+1, s->len - (pos+1))); +  stack_pop_n_elems_keep_top(args); + } +  +    /*! @decl int zero_type(mixed a)    *!    *! Return the type of zero.    *!    *! There are many types of zeros out there, or at least there are two.    *! One is returned by normal functions, and one returned by mapping    *! lookups and @[find_call_out()] when what you looked for wasn't there.    *! The only way to separate these two kinds of zeros is @[zero_type()].    *!    *! @returns
pike.git/src/builtin_functions.c:2666:       if (compiler_frame && compiler_frame->current_object->prog) {    ref_push_object(compiler_frame->current_object);    return;    }    }       push_undefined();   }    - /*! @decl CompilationHandler get_active_compilation_handler() -  *! -  *! Returns the currently active compilation compatibility handler, or -  *! @tt{0@} (zero) if none is active. -  *! -  *! @note -  *! This function should only be used during a call of @[compile()]. -  *! -  *! @seealso -  *! @[get_active_error_handler()], @[compile()], -  *! @[master()->get_compilation_handler()], @[CompilationHandler] -  */ - PMOD_EXPORT void f_get_active_compilation_handler(INT32 args) - { -  struct compilation *c = NULL; -  -  if (compilation_program) { -  struct pike_frame *compiler_frame = Pike_fp; -  -  while (compiler_frame && -  (compiler_frame->context->prog != compilation_program)) { -  compiler_frame = compiler_frame->next; -  } -  -  if (compiler_frame) { -  c = (struct compilation *)compiler_frame->current_storage; -  } -  } -  -  pop_n_elems(args); -  if (c && c->compat_handler) { -  ref_push_object(c->compat_handler); -  } else { -  push_int(0); -  } - } -  - /*! @decl CompilationHandler get_active_error_handler() -  *! -  *! Returns the currently active compilation error handler -  *! (second argument to @[compile()]), or @tt{0@} (zero) if none -  *! is active. -  *! -  *! @note -  *! This function should only be used during a call of @[compile()]. -  *! -  *! @seealso -  *! @[get_active_compilation_handler()], @[compile()], @[CompilationHandler] -  */ - PMOD_EXPORT void f_get_active_error_handler(INT32 args) - { -  struct compilation *c = NULL; -  -  if (compilation_program) { -  struct pike_frame *compiler_frame = Pike_fp; -  -  while (compiler_frame && -  (compiler_frame->context->prog != compilation_program)) { -  compiler_frame = compiler_frame->next; -  } -  -  if (compiler_frame) { -  c = (struct compilation *)compiler_frame->current_storage; -  } -  } -  -  pop_n_elems(args); -  if (c && c->handler) { -  ref_push_object(c->handler); -  } else { -  push_int(0); -  } - } -  +    /*! @decl array allocate(int size)    *! @decl array allocate(int size, mixed init)    *!    *! Allocate an array of @[size] elements. If @[init] is specified    *! then each element is initialized by copying that value    *! recursively.    *!    *! @seealso    *! @[sizeof()], @[aggregate()], @[arrayp()]    */
pike.git/src/builtin_functions.c:3096:    *! @note    *! Note that strings containing null characters will only be    *! processed up until the null character.    */   PMOD_EXPORT void f_crypt(INT32 args)   {    char salt[2];    char *ret, *pwd = NULL, *saltp = NULL;    char *alphabet =    "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + #ifdef HAVE_CRYPT_R +  struct crypt_data crypt_data; +  crypt_data.initialized = 0; + #endif       get_all_args("crypt", args, ".%s%s", &pwd, &saltp);       if( !pwd )    {    do {    push_random_string(16);    push_constant_text("\0");    f_minus(2);    } while(Pike_sp[-1].u.string->len<8);
pike.git/src/builtin_functions.c:3138:    salt[0] = alphabet[ Pike_sp[-1].u.integer & 0x3f ];    salt[1] = alphabet[ (Pike_sp[-1].u.integer>>6) & 0x3f ];    pop_stack();       saltp=salt;    if (args > 1) {    pop_n_elems(args-1);    args = 1;    }    } - #ifdef HAVE_CRYPT +  +  /* NB: crypt(3C) with modern algorithms may be quite slow, +  * so release the interpreter lock if possible. +  */ + #ifdef HAVE_CRYPT_R +  /* Glibc reentrant version of crypt(3C). */ +  THREADS_ALLOW(); +  { +  ret = crypt_r(pwd, saltp, &crypt_data); +  } +  THREADS_DISALLOW(); + #elif defined(HAVE_CRYPT) + #ifdef SOLARIS +  /* NB: crypt(3C) on Solaris (since at least Solaris 8) is +  * documented to be thread-safe (due to returning a +  * thread-local buffer). +  * +  * On Linux (glibc 2.27) and MacOS X (Darwin 13.4.0) it +  * is documented NOT to be thread-safe. +  */ +  THREADS_ALLOW(); + #endif    ret = (char *)crypt(pwd, saltp); - #else - #ifdef HAVE__CRYPT + #ifdef SOLARIS +  THREADS_DISALLOW(); + #endif + #elif defined(HAVE__CRYPT)    ret = (char *)_crypt(pwd, saltp);   #else   #error No crypt function found and fallback failed.   #endif - #endif +     if (!ret) {    switch(errno) {   #ifdef ELIBACC    case ELIBACC:    Pike_error("Failed to load a required shared library. "    "Unsupported salt.\n");    break;   #endif    case ENOMEM:    Pike_error("Out of memory.\n");
pike.git/src/builtin_functions.c:3176:    pop_n_elems(args);    push_text(ret);    }else{    int i;    i=!strcmp(ret,saltp);    pop_n_elems(args);    push_int(i);    }   }    - /*! @decl void destruct(void|object o) + /*! @decl int(1bit) destruct(void|object o)    *!    *! Mark an object as destructed.    *!    *! Calls @expr{o->_destruct()@}, and then clears all variables in the    *! object. If no argument is given, the current object is destructed.    *!    *! All pointers and function pointers to this object will become zero.    *! The destructed object will be freed from memory as soon as possible. -  +  *! +  *! @returns +  *! Returns @expr{1@} if @[o] has an @[lfun::_destruct()] that +  *! returned @expr{1@} and inhibited destruction.    */   PMOD_EXPORT void f_destruct(INT32 args)   {    struct object *o; -  +  int ret;    if(args)    {    if(TYPEOF(Pike_sp[-args]) != T_OBJECT) {    if ((TYPEOF(Pike_sp[-args]) == T_INT) &&    (!Pike_sp[-args].u.integer)) { -  pop_n_elems(args); +  pop_n_elems(args-1);    return;    }    SIMPLE_ARG_TYPE_ERROR("destruct", 1, "object");    }       o=Pike_sp[-args].u.object;    }else{    if(!Pike_fp) {    PIKE_ERROR("destruct",    "Destruct called without argument from callback function.\n",    Pike_sp, args);    }    o=Pike_fp->current_object;    }    if (o->prog && o->prog->flags & PROGRAM_NO_EXPLICIT_DESTRUCT)    PIKE_ERROR("destruct", "Object can't be destructed explicitly.\n",    Pike_sp, args); -  +  if (o->inhibit_destruct) { +  /* Destruct the object as soon as the inhibit_destruct +  * counter is back down to zero. +  */ +  o->flags |= OBJECT_PENDING_DESTRUCT; +  pop_n_elems(args); +  push_int(1); +  return; +  }    debug_malloc_touch(o); -  destruct_object (o, DESTRUCT_EXPLICIT); +  ret = destruct_object (o, DESTRUCT_EXPLICIT);    pop_n_elems(args); -  +  push_int(ret);    destruct_objects_to_destruct();   }      /*! @decl array indices(string|array|mapping|multiset|object x)    *!    *! Return an array of all valid indices for the value @[x].    *!    *! @param x    *! @mixed    *! @type string
pike.git/src/builtin_functions.c:3705:       default:    SIMPLE_ARG_TYPE_ERROR("types", 1,    "string|array|mapping|"    "multiset|object|program|function");    }    pop_n_elems(args);    push_array(a);   }    + /*! @decl array(multiset(Pike.Annotation)) annotations(object|program|function x, @ +  *! int(0..1)|void recurse) +  *! +  *! Return an array with the annotations for all symbols in @[x]. +  *! +  *! @param x +  *! @mixed +  *! @type object +  *! For objects which define @[lfun::_annotations()] that return value +  *! is used. +  *! +  *! For other objects an array with annotations for all non-protected +  *! symbols is returned. +  *! @type program +  *! Returns an array with annotations for all non-protected +  *! constant symbols. +  *! @endmixed +  *! +  *! @param recurse +  *! Include annotations recursively added via inherits. +  *! +  *! @returns +  *! The order of the resulting array is the same as that of @[indices()] +  *! for the same @[x]. +  *! +  *! @note +  *! This function was added in Pike 8.1. +  *! +  *! @seealso +  *! @[indices()], @[values()], @[types()], @[lfun::_annotations()], +  *! @[::_annotations()] +  */ + PMOD_EXPORT void f_annotations(INT32 args) + { +  ptrdiff_t size; +  struct array *a = NULL; +  struct pike_type *default_type = mixed_type_string; +  struct svalue *arg = NULL; +  ptrdiff_t flags = 0; +  +  get_all_args("annotations", args, "%*.%i", &arg, &flags); +  +  if (flags & ~(ptrdiff_t)1) { +  SIMPLE_ARG_TYPE_ERROR("annotations", 2, "int(0..1)|void"); +  } +  +  switch(TYPEOF(*arg)) +  { +  case T_OBJECT: +  a = object_annotations(arg->u.object, SUBTYPEOF(Pike_sp[-args]), flags); +  break; +  +  case T_PROGRAM: +  a = program_annotations(arg->u.program, flags); +  break; +  +  case T_FUNCTION: +  { +  struct program *p = program_from_svalue(arg); +  if (p) { +  a = program_annotations(p, flags); +  break; +  } +  } +  /* FALLTHRU */ +  +  default: +  SIMPLE_ARG_TYPE_ERROR("annotations", 1, "object|program|function"); +  } +  pop_n_elems(args); +  push_array(a); + } +    /*! @decl program|function object_program(mixed o)    *!    *! Return the program from which @[o] was instantiated. If the    *! object was instantiated from a class using parent references    *! the generating function will be returned.    *!    *! If @[o] is not an object or has been destructed @expr{0@} (zero)    *! will be returned.    */   PMOD_EXPORT void f_object_program(INT32 args)
pike.git/src/builtin_functions.c:4720:       array_fix_type_field(s->u.array);    if( !s->u.array->type_field) {    ret = 1;    }    else if( !(s->u.array->type_field & ~(BIT_CALLABLE|BIT_INT)) ) {    struct array *a = s->u.array;    int i;    ret = 1;    for(i=0; i<a->size; i++) -  if( TYPEOF(ITEM(a)[i])!=T_INT && !callablep(&ITEM(a)[i]) ) +  if(!IS_UNDEFINED(ITEM(a) + i) && !callablep(&ITEM(a)[i]))    {    ret = 0;    break;    }    }    END_CYCLIC();    return ret;    break;    }    }
pike.git/src/builtin_functions.c:4873:    case T_FLOAT:    delay=(double)Pike_sp[-args].u.float_number;    break;    }       do_abort_on_signal = delay!=0.0 && args > 1    && !UNSAFE_IS_ZERO(Pike_sp + 1-args);    pop_n_elems(args);       delaysleep(delay, do_abort_on_signal, 0); +  low_check_threads_etc();   }      #undef FIX_LEFT   #undef TIME_ELAPSED      /*! @decl void delay(int|float s)    *!    *! This function makes the thread stop for @[s] seconds.    *!    *! Only signal handlers can interrupt the sleep. Other callbacks are
pike.git/src/builtin_functions.c:4958:    case PIKE_T_MAPPING:    res = do_gc_weak_mapping(Pike_sp[-args].u.mapping);    pop_n_elems(args);    break;    case PIKE_T_ARRAY:    res = do_gc_weak_array(Pike_sp[-args].u.array);    pop_n_elems(args);    break;    default:    pop_n_elems(args); -  res = do_gc(NULL, 1); +  res = do_gc(1);    break;    }    push_int(res);   }      #ifdef TYPEP   #undef TYPEP   #endif      
pike.git/src/builtin_functions.c:5296:   {    INT32 tmp=d_flag;       /* Keep below calls to low_thorough_check_short_svalue, or else we    * get O(n!) or so, where n is the number of allocated things. */    d_flag = 49;      #ifdef PIKE_DEBUG    do_debug(); /* Calls do_gc() since d_flag > 3. */   #else -  do_gc(NULL, 1); +  do_gc(1);   #endif    d_flag=tmp;    pop_n_elems(args);   }      static void encode_struct_tm(const struct tm *tm, int gmtoffset)   {    push_static_text("sec");    push_int(tm->tm_sec);    push_static_text("min");
pike.git/src/builtin_functions.c:5389:    if (!gmtime_s (&tm_s, &t)) tm = &tm_s; else tm = NULL;   #else    tm = gmtime(&t);   #endif    if (!tm) Pike_error ("gmtime() on this system cannot handle "    "the timestamp %"PRINTINT64"d.\n", (INT64) t);    pop_n_elems(args);    encode_struct_tm(tm, 0);   }    + static void my_putenv(void *s) + { +  putenv(s); + } +    /*! @decl mapping(string:int) localtime(int timestamp)    *!    *! Convert seconds since 00:00:00 UTC, 1 Jan 1970 into components.    *!    *! @returns    *! This function returns a mapping with the following components:    *! @mapping    *! @member int(0..60) "sec"    *! Seconds over the minute.    *! @member int(0..59) "min"
pike.git/src/builtin_functions.c:5500:    (tz/60)%60,    tz % 60);    putenv(tzbuf);    if (!orig_tz) {   #ifdef PUTENV_ALWAYS_REQUIRES_EQUAL    orig_tz = "TZ=";   #else    orig_tz = "TZ";   #endif    } -  SET_ONERROR(uwp, putenv, orig_tz); +  SET_ONERROR(uwp, my_putenv, orig_tz);    /* NB: No need to call tzset(); mktime() will call it. */    retval = mktime_zone(date, 0, 0);    CALL_AND_UNSET_ONERROR(uwp);    return retval;    }    Pike_error("Time conversion unsuccessful.\n");    }       if(other_timezone)    {
pike.git/src/builtin_functions.c:9149:    else    push_int(0);    return;    }       id = ID_FROM_INT(p, offset);    id_prog = PROG_FROM_INT (p, offset);       if(IDENTIFIER_IS_PIKE_FUNCTION( id->identifier_flags ) &&    id->func.offset != -1) -  file = low_get_line(id_prog->program + id->func.offset, id_prog, &line); +  file = low_get_line(id_prog->program + id->func.offset, id_prog, +  &line, NULL);    else if (IDENTIFIER_IS_CONSTANT (id->identifier_flags) &&    id->func.const_info.offset >= 0 &&    (p2 = program_from_svalue (&id_prog->constants[id->func.const_info.offset].sval)))    file = low_get_program_line (p2, &line);    else    /* The program line is better than nothing for C functions. */    file = low_get_program_line (p, &line);       if (file)    {
pike.git/src/builtin_functions.c:9296:    p = t->frame->current_object->prog;    func = t->func;    }    }       id=ID_FROM_INT(p, func);    id_prog = PROG_FROM_INT (p, func);       if(IDENTIFIER_IS_PIKE_FUNCTION( id->identifier_flags ) &&    id->func.offset != -1) -  file = low_get_line(id_prog->program + id->func.offset, id_prog, &line); +  file = low_get_line(id_prog->program + id->func.offset, id_prog, +  &line, NULL);    else if (IDENTIFIER_IS_CONSTANT (id->identifier_flags) &&    id->func.const_info.offset >= 0 &&    (p2 = program_from_svalue (&id_prog->constants[id->func.const_info.offset].sval)))    file = low_get_program_line (p2, &line);    else    /* The program line is better than nothing for C functions. */    file = low_get_program_line (p, &line);       if (file)    {
pike.git/src/builtin_functions.c:9407:    OPT_TRY_OPTIMIZE, fix_aggregate_mapping_type, 0);       /* function(:mapping(string:mixed)) */    ADD_EFUN("all_constants",f_all_constants,    tFunc(tNone,tMap(tStr,tMix)),OPT_EXTERNAL_DEPEND);       /* function(:object) */    ADD_EFUN("get_active_compiler", f_get_active_compiler,    tFunc(tNone, tObj), OPT_EXTERNAL_DEPEND);    -  /* function(:object) */ -  ADD_EFUN("get_active_compilation_handler", -  f_get_active_compilation_handler, -  tFunc(tNone, tObj), OPT_EXTERNAL_DEPEND); -  -  /* function(:object) */ -  ADD_EFUN("get_active_error_handler", -  f_get_active_error_handler, -  tFunc(tNone, tObj), OPT_EXTERNAL_DEPEND); -  +     /* function(int,void|0=mixed:array(0)) */    ADD_EFUN("allocate", f_allocate,    tFunc(tInt tOr(tVoid,tSetvar(0,tMix)),tArr(tVar(0))), 0);       /* function(mixed:int) */    ADD_EFUN("arrayp", f_arrayp,tFunc(tMix,tInt01),0);    -  +  ADD_EFUN("basename", f_basename, tFunc(tSetvar(0, tStr), tVar(0)), 0); +     /* function(string...:string) */ -  ADD_EFUN("combine_path_nt",f_combine_path_nt,tFuncV(tNone,tStr,tStr),0); -  ADD_EFUN("combine_path_unix",f_combine_path_unix,tFuncV(tNone,tStr,tStr),0); -  ADD_EFUN("combine_path_amigaos",f_combine_path_amigaos,tFuncV(tNone,tStr,tStr),0); +  ADD_EFUN("combine_path_nt", f_combine_path_nt, +  tFuncV(tNone, tSetvar(0, tStr), tVar(0)), 0); +  ADD_EFUN("combine_path_unix", f_combine_path_unix, +  tFuncV(tNone, tSetvar(0, tStr), tVar(0)), 0); +  ADD_EFUN("combine_path_amigaos", f_combine_path_amigaos, +  tFuncV(tNone, tSetvar(0, tStr), tVar(0)), 0);   #if defined(__NT__) -  ADD_EFUN("combine_path",f_combine_path_nt,tFuncV(tNone,tStr,tStr),0); +  ADD_EFUN("combine_path", f_combine_path_nt, +  tFuncV(tNone, tSetvar(0, tStr), tVar(0)), 0);   #else   #ifdef __amigaos__ -  ADD_EFUN("combine_path",f_combine_path_amigaos,tFuncV(tNone,tStr,tStr),0); +  ADD_EFUN("combine_path", f_combine_path_amigaos, +  tFuncV(tNone, tSetvar(0, tStr), tVar(0)), 0);   #else -  ADD_EFUN("combine_path",f_combine_path_unix,tFuncV(tNone,tStr,tStr),0); +  ADD_EFUN("combine_path", f_combine_path_unix, +  tFuncV(tNone, tSetvar(0, tStr), tVar(0)), 0);   #endif   #endif       ADD_EFUN("compile", f_compile,    tFunc(tStr tOr(tObj, tVoid) tOr(tInt, tVoid) tOr(tInt, tVoid) tOr(tPrg(tObj), tVoid) tOr(tObj, tVoid) ,tPrg(tObj)),    OPT_EXTERNAL_DEPEND);       /* function(1=mixed:1) */    ADD_EFUN("copy_value",f_copy_value,tFunc(tSetvar(1,tMix),tVar(1)),0);       /* function(string:string)|function(string,string:int) */    ADD_EFUN("crypt",f_crypt,    tOr(tFunc(tOr(tStr,tVoid),tStr7),tFunc(tStr tStr,tInt01)),OPT_EXTERNAL_DEPEND);    -  /* function(object|void:void) */ -  ADD_EFUN("destruct",f_destruct,tFunc(tOr(tObj,tVoid),tVoid),OPT_SIDE_EFFECT); +  /* function(object|void:int(0..1)) */ +  ADD_EFUN("destruct",f_destruct,tFunc(tOr(tObj,tVoid),tInt01),OPT_SIDE_EFFECT);    -  +  ADD_EFUN("dirname", f_dirname, tFunc(tSetvar(0, tStr), tVar(0)), 0); +     /* function(mixed,mixed:int) */    ADD_EFUN("equal",f_equal,tFunc(tMix tMix,tInt01),OPT_TRY_OPTIMIZE);       /* function(array(0=mixed),int|void,int|void:array(0)) */    ADD_FUNCTION2("everynth",f_everynth,    tFunc(tArr(tSetvar(0,tMix)) tOr(tInt1Plus,tVoid) tOr(tIntPos,tVoid),    tArr(tVar(0))), 0, OPT_TRY_OPTIMIZE);       /* function(int:void) */    ADD_EFUN("exit",f_exit,tFuncV(tInt tOr(tVoid,tStr),tOr(tVoid,tMix),tVoid),
pike.git/src/builtin_functions.c:9673:       /* function(string|multiset:array(int))|function(array(0=mixed)|mapping(mixed:0=mixed)|object|program:array(0)) */    ADD_EFUN2("types", f_types,    tOr3(tFunc(tOr3(tNStr(tSetvar(0,tInt)),    tArr(tSetvar(0,tMix)),    tMap(tMix,tSetvar(0,tMix))),    tArr(tType(tVar(0)))),    tFunc(tMultiset, tArr(tType(tInt1))),    tFunc(tOr(tObj,tPrg(tObj)), tArr(tType(tMix)))),0,NULL,0);    +  /* function(object|program, int(0..1)|void:array(multiset)) */ +  ADD_EFUN2("annotations", f_annotations, +  tFunc(tOr(tObj,tPrg(tObj) tOr(tInt01,tVoid)), tArr(tSet(tMix))),0,NULL,0); +     /* function(mixed:int) */    ADD_EFUN2("zero_type",f_zero_type,tFunc(tMix,tInt01),0,0,generate_zero_type);       /* function(string,string:array) */    ADD_EFUN("array_sscanf", f_sscanf,    tFunc(tStr tAttr("sscanf_format", tStr),    tArr(tAttr("sscanf_args", tMix))), OPT_TRY_OPTIMIZE);       ADD_EFUN("__handle_sscanf_format", f___handle_sscanf_format,    tFunc(tStr tStr tType(tMix) tType(tMix), tType(tMix)),