pike.git / src / builtin.cmod

version» Context lines:

pike.git/src/builtin.cmod:1: - /* -*- c -*- + /* -*- mode: c; encoding: utf-8; -*-   || 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 "interpret.h"   #include "svalue.h"   #include "pike_macros.h"   #include "object.h"
pike.git/src/builtin.cmod:19:   #include "pike_types.h"   #include "pike_memory.h"   #include "threads.h"   #include "module_support.h"   #include "cyclic.h"   #include "bignum.h"   #include "main.h"   #include "operators.h"   #include "builtin_functions.h"   #include "fsort.h" - #include "port.h" + #include "pike_cpulib.h"   #include "gc.h"   #include "block_allocator.h"   #include "pikecode.h"   #include "opcodes.h" -  + #include "whitespace.h" + #include "sprintf.h" + #include "pike_search.h"    - #include <ctype.h> +    #include <errno.h>   #include <math.h> -  + #include <fcntl.h>    - DECLARATIONS + #ifdef HAVE_ARPA_INET_H + #include <arpa/inet.h> + #endif /* HAVE_ARPA_INET_H */    -  + #define DEFAULT_CMOD_STORAGE    -  + #ifdef STRUCT_TM_HAS___TM_GMTOFF +  struct tm_extra { }; + #define tm_zone __tm_zone + #define tm_gmtoff __tm_gmtoff + #define GET_GMTOFF(TM) ((TM)->tm_gmtoff) + #define GET_ZONE(this) ((this)->t.tm_zone) + #define SET_GMTOFF(TM, VAL) (((TM)->tm_gmtoff) = (VAL)) + #define SET_ZONE(this, VAL) ((this)->t.tm_zone = (VAL)) + #elif defined(STRUCT_TM_HAS_GMTOFF) +  struct tm_extra { }; + #define GET_GMTOFF(TM) ((TM)->tm_gmtoff) + #define GET_ZONE(this) ((this)->t.tm_zone) + #define SET_GMTOFF(TM, VAL) (((TM)->tm_gmtoff) = (VAL)) + #define SET_ZONE(this, VAL) ((this)->t.tm_zone = (VAL)) + #else +  struct tm_extra { const char *tm_zone; }; + #define GET_GMTOFF(TM) 0 + #define GET_ZONE(this) ((this)->extra.tm_zone) + #define SET_GMTOFF(TM, VAL) (VAL) + #define SET_ZONE(this, VAL) ((this)->extra.tm_zone = (VAL)) + #endif +  + DECLARATIONS +    /*! @module System    */    - #if defined(HAVE_MKTIME) && defined(HAVE_GMTIME) && defined(HAVE_LOCALTIME) +    /*! @class TM    *! A wrapper for the system struct tm time keeping structure.    *! This can be used as a (very) lightweight alternative to Calendar.    */   PIKECLASS TM   {    CVAR struct tm t;    CVAR time_t unix_time;    CVAR int modified; -  CVAR struct pike_string *set_zone; +  CVAR struct tm_extra extra;    - #ifdef STRUCT_TM_HAS___TM_GMTOFF - #define tm_zone __tm_zone - #define tm_gmtoff __tm_gmtoff - #define GET_GMTOFF(TM) ((TM)->tm_gmtoff) - #define GET_ZONE(TM) ((TM)->tm_zone) - #define SET_GMTOFF(TM, VAL) (((TM)->tm_gmtoff) = (VAL)) - #define SET_ZONE(TM, VAL) (((TM)->tm_zone) = (VAL)) - #elif defined(STRUCT_TM_HAS_GMTOFF) - #define GET_GMTOFF(TM) ((TM)->tm_gmtoff) - #define GET_ZONE(TM) ((TM)->tm_zone) - #define SET_GMTOFF(TM, VAL) (((TM)->tm_gmtoff) = (VAL)) - #define SET_ZONE(TM, VAL) (((TM)->tm_zone) = (VAL)) - #else - #define GET_GMTOFF(TM) 0 - #define GET_ZONE(TM) ((char*)NULL) - #define SET_GMTOFF(TM, VAL) (VAL) - #define SET_ZONE(TM, VAL) (VAL) - #endif -  - #if 0 - /* This is supposed to make any timezone work. -  * However: It does not really work. And makes things even slower than -  * the calendar module. -  */ - #ifndef HAVE_EXTERNAL_TIMEZONE - #define timezone 0 - #endif - #define WITH_ZONE(RETURNTYPE, FUNCTION, ARGUMENTS, CALL ) \ -  static RETURNTYPE FUNCTION##_zone ARGUMENTS \ -  { \ -  RETURNTYPE res; \ -  int reset = 0; \ -  char *old_zone = NULL; \ -  if( GET_ZONE(x) ) \ -  { \ -  reset = 1; \ -  old_zone = getenv("TZ"); \ -  setenv("TZ", GET_ZONE(x), 1 ); \ -  tzset(); \ -  SET_GMTOFF(x, timezone); \ -  } \ -  \ -  res = FUNCTION CALL; \ -  \ -  if( reset ) \ -  { \ -  if( old_zone ) \ -  setenv("TZ", old_zone, 1 ); \ -  else \ -  unsetenv( "TZ" ); \ -  tzset(); \ -  } \ -  return res; \ -  } -  -  WITH_ZONE(time_t,mktime,( struct tm *x ),(x)); -  WITH_ZONE(struct tm*,localtime,( time_t *t, struct tm *x ),(t)); -  WITH_ZONE(char *,asctime,( struct tm *x ),(x)); -  WITH_ZONE(int,strftime,( char *buffer, size_t max_len, char *format, struct tm *x ),(buffer,max_len,format,x)); - #ifdef HAVE_STRPTIME -  WITH_ZONE(char *,strptime,( const char *str, const char *format, struct tm *x ),(str,format,x)); - #endif - #else +    #define strftime_zone strftime - #define mktime_zone mktime +    #define strptime_zone strptime - #define asctime_zone asctime +    #define localtime_zone(X,Y) localtime(X) - #endif +    #ifndef HAVE_EXTERNAL_TIMEZONE   #undef timezone   #endif      #define MODIFY(X) do{ THIS->modified = 1;THIS->t.X; }while(0)   #define FIX_THIS() do { \ -  if(THIS->modified){ \ -  THIS->unix_time = mktime_zone( &THIS->t ); \ -  THIS->modified = 0; \ -  } \ +  if(THIS->modified) \ +  fix_tm(THIS); \    } while(0)    -  +  static void fix_tm(struct TM_struct *this) +  { +  const char *tm_zone = GET_ZONE(this); +  int is_utc_zone = tm_zone && !strcmp(tm_zone, "UTC"); +  if (is_utc_zone) +  this->t.tm_isdst = 0; +  this->unix_time = mktime_zone(&this->t, is_utc_zone, 0); +  this->modified = 0; +  } +    #ifdef HAVE_STRPTIME    /*! @decl int(0..1) strptime( string(1..255) format, string(1..255) data )    *! -  *! Parse the given @[data] using the format in @[format] as a date. +  *! @note +  *! The @expr{format@} and @expr{data@} are reversed.    *! -  *! @dl -  *! @item %% -  *! The % character. -  *! -  *! @item %a or %A -  *! The weekday name according to the C locale, in abbreviated -  *! form or the full name. -  *! -  *! @item %b or %B or %h -  *! The month name according to the C locale, in abbreviated form -  *! or the full name. -  *! -  *! @item %c -  *! The date and time representation for the C locale. -  *! -  *! @item %C -  *! The century number (0-99). -  *! -  *! @item %d or %e -  *! The day of month (1-31). -  *! -  *! @item %D -  *! Equivalent to %m/%d/%y. -  *! -  *! @item %H -  *! The hour (0-23). -  *! -  *! @item %I -  *! The hour on a 12-hour clock (1-12). -  *! -  *! @item %j -  *! The day number in the year (1-366). -  *! -  *! @item %m -  *! The month number (1-12). -  *! -  *! @item %M -  *! The minute (0-59). -  *! -  *! @item %n -  *! Arbitrary whitespace. -  *! -  *! @item %p -  *! The C locale's equivalent of AM or PM. -  *! -  *! @item %R -  *! Equivalent to %H:%M. -  *! -  *! @item %S -  *! The second (0-60; 60 may occur for leap seconds; -  *! earlier also 61 was allowed). -  *! -  *! @item %t -  *! Arbitrary whitespace. -  *! -  *! @item %T -  *! Equivalent to %H:%M:%S. -  *! -  *! @item %U -  *! The week number with Sunday the first day of the week (0-53). -  *! -  *! @item %w -  *! The weekday number (0-6) with Sunday = 0. -  *! -  *! @item %W -  *! The week number with Monday the first day of the week (0-53). -  *! -  *! @item %x -  *! The date, using the C locale's date format. -  *! -  *! @item %X -  *! The time, using the C locale's time format. -  *! -  *! @item %y -  *! The year within century (0-99). When a century is not -  *! otherwise specified, values in the range 69-99 refer to years -  *! in the twentieth century (1969-1999); values in the range -  *! 00-68 refer to years in the twenty-first century (2000-2068). -  *! -  *! @item %Y -  *! The year, including century (for example, 1991). -  *! @enddl -  *! +  *! @seealso +  *! @[strptime]    */    PIKEFUN int(0..1) strptime( string(1..255) format, string(1..255) data )    {    if( format->size_shift || data->size_shift )    Pike_error("Only 8bit strings are supported\n");    THIS->modified = 1;    if( strptime_zone( data->str, format->str, &THIS->t ) == NULL )    RETURN 0;    RETURN 1;    } - #endif + #endif /* HAVE_STRPTIME */    /*! @decl string(1..255) strftime( string(1..255) format ) -  *! See also @[Gettext.setlocale] +     *! -  *! Convert the structure to a string. -  *! -  *! @dl -  *! @item %a -  *! The abbreviated weekday name according to the current locale -  *! -  *! @item %A -  *! The full weekday name according to the current locale. -  *! -  *! @item %b -  *! The abbreviated month name according to the current locale. -  *! -  *! @item %B -  *! The full month name according to the current locale. -  *! -  *! @item %c -  *! The preferred date and time representation for the current locale. -  *! -  *! @item %C -  *! The century number (year/100) as a 2-digit integer. -  *! -  *! @item %d -  *! The day of the month as a decimal number (range 01 to 31). -  *! -  *! @item %D -  *! Equivalent to @expr{%m/%d/%y@}. (for Americans only. -  *! Americans should note that in other countries @expr{%d/%m/%y@} -  *! is rather common. This means that in international context -  *! this format is ambiguous and should not be used.) -  *! -  *! @item %e -  *! Like @expr{%d@}, the day of the month as a decimal number, -  *! but a leading zero is replaced by a space. -  *! -  *! @item %E -  *! Modifier: use alternative format, see below. -  *! -  *! @item %F -  *! Equivalent to %Y-%m-%d (the ISO 8601 date format). (C99) -  *! -  *! @item %G -  *! The ISO 8601 week-based year (see NOTES) with century as a -  *! decimal number. The 4-digit year corresponding to the ISO -  *! week number (see @expr{%V@}). This has the same format and -  *! value as @expr{%Y@}, except that if the ISO week number -  *! belongs to the previous or next year, that year is used instead. -  *! -  *! @item %g -  *! Like @expr{%G@}, but without century, that is, -  *! with a 2-digit year (00-99). (TZ) -  *! -  *! @item %h -  *! Equivalent to %b. -  *! -  *! @item %H -  *! The hour as a decimal number using a 24-hour clock (range 00 to 23). -  *! -  *! @item %I -  *! The hour as a decimal number using a 12-hour clock (range 01 to 12). -  *! -  *! @item %j -  *! The day of the year as a decimal number (range 001 to 366). -  *! -  *! @item %k -  *! The hour (24-hour clock) as a decimal number (range 0 to 23); -  *! single digits are preceded by a blank. (See also @expr{%H@}.) -  *! -  *! @item %l -  *! The hour (12-hour clock) as a decimal number (range 1 to 12); -  *! single digits are preceded by a blank. (See also @expr{%I@}.) -  *! -  *! @item %m -  *! The month as a decimal number (range 01 to 12). -  *! -  *! @item %M -  *! The minute as a decimal number (range 00 to 59). -  *! -  *! @item %n -  *! A newline character. (SU) -  *! -  *! @item %O -  *! Modifier: use alternative format, see below. (SU) -  *! -  *! @item %p -  *! Either @expr{"AM"@} or @expr{"PM"@} according to the given time -  *! value, or the corresponding strings for the current locale. -  *! Noon is treated as @expr{"PM"@} and midnight as @expr{"AM"@}. -  *! -  *! @item %P -  *! Like @expr{%p@} but in lowercase: @expr{"am"@} or @expr{"pm"@} -  *! or a corresponding string for the current locale. -  *! -  *! @item %r -  *! The time in a.m. or p.m. notation. In the POSIX locale this is -  *! equivalent to @expr{%I:%M:%S %p@}. -  *! -  *! @item %R -  *! The time in 24-hour notation (@expr{%H:%M@}). (SU) -  *! For a version including the seconds, see @expr{%T@} below. -  *! -  *! @item %s -  *! The number of seconds since the Epoch, -  *! 1970-01-01 00:00:00 +0000 (UTC). (TZ) -  *! -  *! @item %S -  *! The second as a decimal number (range 00 to 60). -  *! (The range is up to 60 to allow for occasional leap seconds.) -  *! -  *! @item %t -  *! A tab character. (SU) -  *! -  *! @item %T -  *! The time in 24-hour notation (@expr{%H:%M:%S@}). (SU) -  *! -  *! @item %u -  *! The day of the week as a decimal, range 1 to 7, Monday being 1. -  *! See also @expr{%w@}. (SU) -  *! -  *! @item %U -  *! The week number of the current year as a decimal number, -  *! range 00 to 53, starting with the first Sunday as the first -  *! day of week 01. See also @expr{%V@} and @expr{%W@}. -  *! -  *! @item %V -  *! The ISO 8601 week number of the current year as a decimal number, -  *! range 01 to 53, where week 1 is the first week that has at least -  *! 4 days in the new year. See also @expr{%U@} and @expr{%W@}. -  *! -  *! @item %w -  *! The day of the week as a decimal, range 0 to 6, Sunday being 0. -  *! See also @expr{%u@}. -  *! @enddl +  *! @seealso +  *! @[strftime], @[Gettext.setlocale]    */ -  PIKEFUN string strftime(string(1..255) format) +  PIKEFUN string(1..255) strftime(string(1..255) format)    { -  char *buffer = xalloc( 8192 ); +  char buffer[8192];    buffer[0] = 0; -  strftime_zone( buffer, 8192, format->str, &THIS->t ); -  push_text( buffer ); +  strftime_zone(buffer, sizeof(buffer), format->str, &THIS->t); +  push_text(buffer);    }       /*! @decl int(0..60) sec;    *! @decl int(0..59) min;    *! @decl int(0..59) hour;    *! @decl int(1..31) mday;    *! @decl int(0..11) mon;    *! @decl int year;    *!    *! The various fields in the structure. Note that setting these
pike.git/src/builtin.cmod:393:    *! When read the fields are always normalized.    *!    *! Unlike the system struct tm the 'year' field is not year-1900,    *! instead it is the actual year.    */    PIKEFUN int(0..60) `sec() { FIX_THIS();RETURN THIS->t.tm_sec; }    PIKEFUN int(0..59) `min() { FIX_THIS();RETURN THIS->t.tm_min; }    PIKEFUN int(0..23) `hour() { FIX_THIS();RETURN THIS->t.tm_hour; }    PIKEFUN int(1..31) `mday() { FIX_THIS();RETURN THIS->t.tm_mday; }    PIKEFUN int(0..11) `mon() { FIX_THIS();RETURN THIS->t.tm_mon; } -  PIKEFUN int `year() { FIX_THIS();RETURN THIS->t.tm_year+1900; } +     -  +  PIKEFUN int `year() { FIX_THIS();RETURN THIS->t.tm_year+1900; }    PIKEFUN int `sec=(int a) { MODIFY(tm_sec=a); }    PIKEFUN int `min=(int a) { MODIFY(tm_min=a); }    PIKEFUN int `hour=(int a){ MODIFY(tm_hour=a); }    PIKEFUN int `mday=(int a){ MODIFY(tm_mday=a); }    PIKEFUN int `year=(int a){ MODIFY(tm_year=a-1900); }    PIKEFUN int `mon=(int a){ MODIFY(tm_mon=a); }       /*! @decl int isdst    *! -  *! True if daylight savings are in effect. If this field is -1 +  *! True if daylight-saving is in effect. If this field is -1    *! (the default) it (and the timezone info) will be updated    *! automatically using the timezone rules.    */    PIKEFUN int(-1..1) `isdst() {    FIX_THIS();    RETURN THIS->t.tm_isdst;    }       /*! @decl int wday    *! The day of the week, sunday is 0, saturday is 6.    *! This is calculated from the other fields and can not be changed directly.    */    PIKEFUN int(0..6) `wday() { FIX_THIS(); RETURN THIS->t.tm_wday; }       /*! @decl int yday    *! The day of the year, from 0 (the first day) to 365    *! This is calculated from the other fields and can not be changed directly.    */ -  PIKEFUN int(0..365) `yday() { FIX_THIS(); RETURN THIS->t.tm_yday; } +  PIKEFUN int(0..365) `yday() { FIX_THIS(); RETURN THIS->t.tm_yday; }       /*! @decl int unix_time()    *! Return the unix time corresponding to this time_t. If no time    *! can be parsed from the structure -1 is returned.    */    PIKEFUN int unix_time()    {    FIX_THIS();    RETURN THIS->unix_time;    }       /*! @decl string asctime()    *! Return a string representing the time. Mostly useful for debug    *! purposes, the exact format is very locale (see    *! @[Gettext.setlocale]) and OS dependent.    */    PIKEFUN string asctime()    {    FIX_THIS();    { -  char *tval = asctime_zone( &THIS->t ); -  if( tval ) -  push_text( tval ); -  else + #define STRFTIME_MAXSIZE 26 +  char s[STRFTIME_MAXSIZE]; +  if( !strftime(s, STRFTIME_MAXSIZE, "%c\n", &THIS->t) )    push_undefined(); -  +  else +  push_text(s);    }    }    -  PIKEFUN void _sprintf( int flag, mapping options ) +  PIKEFUN string _sprintf( int flag, mapping options )    { -  int post_sum = 1; +  int post_sum = 0;    switch( flag )    {    case 'O': -  push_text("System.TM("); +  push_static_text("System.TM(");    post_sum = 1;    /* fallthrough */    case 's':    f_TM_asctime(0); -  push_text("\n"); -  if( GET_ZONE(&(THIS->t)) ) +  push_static_text("\n"); +  if( GET_ZONE(THIS) )    { -  push_text(" "); -  push_text( GET_ZONE(&(THIS->t)) ); +  push_static_text(" "); +  push_text( GET_ZONE(THIS) );    f_add( 2 );    }    else -  push_text(""); +  push_static_text("");    f_replace( 3 );    break;    case 'd':    f_TM_unix_time(0);    break;    default:    Pike_error("Can not format as %c", flag );    }    if( post_sum )    { -  push_text(")"); +  push_static_text(")");    f_add(3);    }       }    -  PIKEFUN mixed cast( string to ) +  /*! @decl int|string cast(string to) +  *! +  *! Casted to an integer @[unix_time] will be returned. +  *! +  *! Casting to a string will call @[asctime]. +  */ +  PIKEFUN int|string cast( string to ) +  flags ID_PROTECTED;    { -  struct pike_string *s_string, *s_int; -  MAKE_CONST_STRING(s_int, "int"); -  MAKE_CONST_STRING(s_string, "string"); -  if( to == s_int ) +  if( to == literal_int_string )    {    f_TM_unix_time(0);    return;    } -  if( to == s_string ) +  if( to == literal_string_string )    {    f_TM_asctime(0);    return;    } -  Pike_error("Does not know how to cast to %s\n", to->str ); +  pop_stack(); +  push_undefined();    }       /*! @decl string zone    *!    *! The timezone of this structure    */    PIKEFUN string `zone() {    FIX_THIS(); -  if( GET_ZONE(&(THIS->t)) ) -  push_text( GET_ZONE(&(THIS->t)) ); +  if( GET_ZONE(THIS) ) +  push_text( GET_ZONE(THIS) );    else    push_undefined();    }       /*! @decl int gmtoff    *! The offset from GMT for the time in this tm-struct    */    PIKEFUN int `gmtoff() {    FIX_THIS();    push_int( GET_GMTOFF(&(THIS->t)) );    }    -  /* Setting the zone does not work, so.. */ -  -  /* PIKEFUN string `zone=(string x) { */ -  /* if( THIS->set_zone ) */ -  /* free_string( THIS->set_zone ); */ -  /* THIS->set_zone = x; */ -  /* MODIFY( tm_zone = x->str ); */ -  /* x->refs++; */ -  /* } */ -  +     /*! @decl int(0..1) localtime( int time )    *! Initialize the struct tm to the local time for the specified    *! unix time_t.    */    PIKEFUN int(0..1) localtime( int _t )    {    time_t t = _t;    struct tm *res = localtime_zone( &t, &THIS->t ); -  -  /* These are supposedly correctly by localtime_zone. */ -  SET_GMTOFF(res, GET_GMTOFF(&(THIS->t))); -  SET_ZONE(res, GET_ZONE(&(THIS->t))); -  +     if( !res )    RETURN 0; -  +     THIS->t = *res;    THIS->modified = 1;    RETURN 1;    }          /*! @decl int(0..1) gmtime( int time )    *! Initialize the struct tm to the UTC time for the specified    *! unix time_t.    */    PIKEFUN int(0..1) gmtime( int _t )    {    time_t t = _t;    struct tm *res = gmtime( &t );       if( !res )    RETURN 0;       THIS->t = *res; -  +  SET_ZONE(THIS, "UTC"); /* Override timezone */    THIS->modified = 1;    RETURN 1;    }    -  /*! @decl void create(int t) +  /*! @decl int(0..1) gmtime( Gmp.mpz time ) +  *! Initialize the struct tm to the UTC time for the specified +  *! unix time_t. +  */ +  PIKEFUN int(0..1) gmtime( object(Gmp.mpz)|object(Gmp.bignum) _t ) +  { +  INT64 t_int64 = 0; +  time_t t; +  struct tm *res; +  +  if (!low_int64_from_bignum(&t_int64, _t)) +  RETURN 0; +  +  t = t_int64; +  res = gmtime( &t ); +  +  if( !res ) +  RETURN 0; +  +  THIS->t = *res; +  SET_ZONE(THIS, "UTC"); /* Override timezone */ +  THIS->modified = 1; +  RETURN 1; +  } +  +  /*! @decl void create(int|Gmp.mpz t)    *! Create a new @[TM] initialized from a unix time_t.    *! The timezone will always be UTC when using this function.    */ -  PIKEFUN void create( int _t ) +  PIKEFUN void create( int|object(Gmp.mpz)|object(Gmp.bignum) _t )    {    f_TM_gmtime( 1 );    if( Pike_sp[-1].u.integer == 0 )    Pike_error("time out of range\n"); -  +  pop_stack();    }       /*! @decl void create()    *! Construct a new TM, all fields will be set to 0.    */    PIKEFUN void create( )    {    memset( &THIS->t, 0, sizeof( struct tm ) );    THIS->t.tm_isdst = -1; -  +  THIS->t.tm_mday = 1;    THIS->unix_time = 0;    THIS->modified = 1;    }       /*! @decl void create( int year, int(0..11) mon, int(1..31) mday, @    *! int(0..24) hour, int(0..59) min, int(0..59) sec, @    *! string|void timezone )    *! Construct a new time using the given values.    *! Slightly faster than setting them individually.    */    PIKEFUN void create( int year, int(0..11) mon, int(1..31) mday,    int(0..24) hour, int(0..59) min, int(0..59) sec,    string|void timezone )    {    struct tm *t = &THIS->t; -  +  int use_utc = 0;    t->tm_isdst = -1;    t->tm_year = year - 1900;    t->tm_mon = mon;    t->tm_mday = mday;    t->tm_hour = hour;    t->tm_min = min;    t->tm_sec = sec; -  if (THIS->set_zone) { -  free_string(THIS->set_zone); -  THIS->set_zone = NULL; +  if (timezone) { +  if (strcmp(timezone->str, "UTC")) +  Pike_error("Timezone must either be UTC or omitted.\n"); +  use_utc = 1;    } -  if( !timezone ) /* gmtime. */ -  SET_ZONE(t, "UTC"); -  else -  { -  add_ref(timezone); -  THIS->set_zone = timezone; -  SET_ZONE(t, timezone->str); +  if (use_utc) +  t->tm_isdst = 0; +  THIS->unix_time = mktime_zone(&THIS->t, use_utc, 0); +  /* Setting it to other timezones than UTC is not supported (yet) */ +  if (use_utc) +  SET_ZONE(THIS, "UTC"); +  pop_n_elems(args);    } -  THIS->unix_time = mktime_zone( t ); -  } +     -  + #ifdef PIKE_NULL_IS_SPECIAL    INIT { -  THIS->set_zone = 0; +     THIS->modified = 0;    } -  -  EXIT { -  if( THIS->set_zone ) -  free_string( THIS->set_zone ); + #endif   } - } +    /*! @endclass    */   #undef FIX_THIS   #ifdef STRUCT_TM_HAS___TM_GMTOFF   #undef tm_zone   #undef tm_gmtoff   #endif - #endif +    /*! @endmodule    */      /*! @decl array(array(int|string|type)) describe_program(program p)    *! @belongs Debug    *!    *! Debug function for showing the symbol table of a program.    *!    *! @returns    *! Returns an array of arrays with the following information
pike.git/src/builtin.cmod:692:    */   PMOD_EXPORT   PIKEFUN array(array(int|string)) _describe_program(mixed x)    efun;   {    struct program *p;    struct array *res;    int i;       if (!(p = program_from_svalue(Pike_sp - args))) -  SIMPLE_BAD_ARG_ERROR("_describe_program", 1, "program"); +  SIMPLE_ARG_TYPE_ERROR("_describe_program", 1, "program");       for (i=0; i < (int)p->num_identifier_references;i++) {    struct reference *ref = p->identifier_references + i;    struct identifier *id = ID_FROM_PTR(p, ref);    struct inherit *inh = INHERIT_FROM_PTR(p, ref);    push_int(ref->id_flags);    ref_push_string(id->name);    ref_push_type_value(id->type);    push_int(id->identifier_flags);    if (IDENTIFIER_IS_C_FUNCTION(id->identifier_flags)) {
pike.git/src/builtin.cmod:749:    f_aggregate_mapping(0);    apply_low(x->u.object,    fun + p->inherits[SUBTYPEOF(*x)].identifier_level, 2);    if(TYPEOF(Pike_sp[-1]) == T_STRING)    {    stack_swap();    pop_stack();    return;    } else if (UNSAFE_IS_ZERO(Pike_sp-1)) {    pop_n_elems(2); -  push_constant_text("object"); +  ref_push_string(literal_object_string);    return;    } else {    Pike_error("Non-string returned from _sprintf()\n");    }    }    }    pop_stack();    switch(t)    { -  case T_ARRAY: push_constant_text("array"); break; -  case T_FLOAT: push_constant_text("float"); break; -  case T_FUNCTION: push_constant_text("function"); break; -  case T_INT: push_constant_text("int"); break; -  case T_MAPPING: push_constant_text("mapping"); break; -  case T_MULTISET: push_constant_text("multiset"); break; -  case T_OBJECT: push_constant_text("object"); break; -  case T_PROGRAM: push_constant_text("program"); break; -  case T_STRING: push_constant_text("string"); break; -  case T_TYPE: push_constant_text("type"); break; +  case T_ARRAY: ref_push_string(literal_array_string); break; +  case T_FLOAT: ref_push_string(literal_float_string); break; +  case T_FUNCTION: ref_push_string(literal_function_string); break; +  case T_INT: ref_push_string(literal_int_string); break; +  case T_MAPPING: ref_push_string(literal_mapping_string); break; +  case T_MULTISET: ref_push_string(literal_multiset_string); break; +  case T_OBJECT: ref_push_string(literal_object_string); break; +  case T_PROGRAM: ref_push_string(literal_program_string); break; +  case T_STRING: ref_push_string(literal_string_string); break; +  case T_TYPE: ref_push_string(literal_type_string); break;    case T_ZERO: push_constant_text("zero"); break;    case T_VOID: push_constant_text("void"); break;    /* The following are internal and shouldn't be applicable in normal use. */ -  case T_SVALUE_PTR: push_constant_text("svalue_ptr"); break; -  case T_OBJ_INDEX: push_constant_text("obj_index"); break; -  case T_MAPPING_DATA: push_constant_text("mapping_data"); break; -  case T_PIKE_FRAME: push_constant_text("pike_frame"); break; -  case T_MULTISET_DATA: push_constant_text("multiset_data"); break; -  default: push_constant_text("unknown"); break; +  case T_SVALUE_PTR: push_static_text("svalue_ptr"); break; +  case T_OBJ_INDEX: push_static_text("obj_index"); break; +  case T_MAPPING_DATA: push_static_text("mapping_data"); break; +  case T_PIKE_FRAME: push_static_text("pike_frame"); break; +  case T_MULTISET_DATA: push_static_text("multiset_data"); break; +  default: push_static_text("unknown"); break;    }   }    -  + /*! @decl int siphash24(string data, void|int key) +  *! @appears Crypto.siphash24 +  *! +  *! Hashes a string, with an optional key, to a 64 bit integer using +  *! the siphash-2-4 algorithm. Currently the 64 bit @[key] parameter +  *! is used both for the high and low part of the 128 bit key. +  *! +  */ + #include "siphash24.h" + PIKEFUN int siphash24(string s, void|int key) + { +  mpz_t ret; +  mpz_init(ret); +  mpz_set_ui(ret, pike_string_siphash24(s, key && key->u.integer) ); +  push_bignum(ret); + }      /*! @decl string int2char(int x)    *! @appears String.int2char    *!    *! Same as sprintf("%c",x);    *!    *! @seealso    *! @[sprintf()]    */   PMOD_EXPORT   PIKEFUN string int2char(int|object x)    efun;    optflags OPT_TRY_OPTIMIZE; -  +  rawtype tFunc(tSetvar(0, tOr(tInt,tObj)), tNStr(tVar(0)));   {    int c;    struct program *p;    if(TYPEOF(*x) == T_OBJECT && (p = x->u.object->prog))    {    ptrdiff_t fun = FIND_LFUN(p->inherits[SUBTYPEOF(*x)].prog, LFUN__SPRINTF);    if(fun != -1)    {    push_int('c');    f_aggregate_mapping(0);
pike.git/src/builtin.cmod:816:    if(TYPEOF(Pike_sp[-1]) == T_STRING)    {    stack_swap();    pop_stack();    return;    }    Pike_error("Non-string returned from _sprintf()\n");    }    }    if(TYPEOF(*x) != T_INT) -  SIMPLE_BAD_ARG_ERROR("int2char", 1, "int"); +  SIMPLE_ARG_TYPE_ERROR("int2char", 1, "int");       c=x->u.integer;       if(c>=0 && c<256)    {    struct pike_string *s;    s=begin_shared_string(1);    s->str[0]=c;    RETURN end_shared_string(s);    }else{
pike.git/src/builtin.cmod:873:    if(TYPEOF(Pike_sp[-1]) == T_STRING)    {    stack_swap();    pop_stack();    return;    }    Pike_error("Non-string returned from _sprintf()\n");    }    }    if(TYPEOF(*x) != T_INT) -  SIMPLE_BAD_ARG_ERROR("int2hex", 1, "int"); +  SIMPLE_ARG_TYPE_ERROR("int2hex", 1, "int");       c=x->u.integer;       len=1;    if(c<0) {    len++;    n=(-c)&((unsigned INT_TYPE)(-1));    }else{    n=c;    }
pike.git/src/builtin.cmod:909:    while(len && n)    {    s->str[--len]="0123456789abcdef"[n&0xf];    n>>=4;    }    }    RETURN end_shared_string(s);   }       - /*! @decl string string2hex(string data) + /*! @decl string string2hex(string data, void|int(0..) flags)    *! @appears String.string2hex    *!    *! Convert a string of binary data to a hexadecimal string.    *! -  +  *! @param flags +  *! The binary or of the following flags: +  *! @int +  *! @value 1 +  *! Use upper case characters. +  *! +  *! @value 2 +  *! The input is in little-endian byte order. +  *! @endint +  *!    *! @seealso    *! @[hex2string()]    */      static const char hexchar[] = {    '0','1','2','3','4','5','6','7','8','9',    'a','b','c','d','e','f'   };    - static const unsigned char hexdecode[256] = - { -  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, -  -  /* '0' - '9' */ -  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -  -  0,0,0,0,0,0,0, -  -  /* 'A' - 'F' */ -  10, 11, 12, 13, 14, 15, -  -  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, -  /* 'a' - 'f' */ -  10, 11, 12, 13, 14, 15, + static const char HEXCHAR[] = { +  '0','1','2','3','4','5','6','7','8','9', +  'A','B','C','D','E','F'   };      PMOD_EXPORT -  PIKEFUN string(0..255) string2hex(string s) -  errname String.string2hex; +  PIKEFUN string(48..102) string2hex(string s, void|int flags) +  efun;    optflags OPT_TRY_OPTIMIZE;   {    struct pike_string *hex;    unsigned char *p,*st = (unsigned char *)s->str; -  int i, l; +  const char *hextab = hexchar; +  int i, l, d = 1;       if (s->size_shift)    Pike_error("Bad argument 1 to string2hex(), expected 8-bit string.\n");    -  +  if (flags && (flags->u.integer & 1)) { +  hextab = HEXCHAR; +  } +     hex = begin_shared_string(2 * s->len);    p = (unsigned char *)hex->str;    l = s->len;    -  +  if (flags && (flags->u.integer & 2)) { +  d = -1; +  st += l-1; +  } +     for (i=0; i<l; i++) { -  *p++ = hexchar[*st>>4]; -  *p++ = hexchar[*st&15]; -  st++; +  *p++ = hextab[*st>>4]; +  *p++ = hextab[*st&15]; +  st += d;    }       RETURN end_shared_string(hex);   }    - /*! @decl string hex2string(string hex) + extern const unsigned char hexdecode[256]; +  + /*! @decl string(8bit) hex2string(string(8bit) hex)    *! @appears String.hex2string    *! -  *! Convert a string of hexadecimal digits to binary data. +  *! Convert a string of hexadecimal digits to binary +  *! data. Non-hexadecimal characters will be ignored when between +  *! tuples. Eg. "00 00" is ok, but "0 000" isn't.    *!    *! @seealso    *! @[string2hex()]    */   PMOD_EXPORT   PIKEFUN string(0..255) hex2string(string hex) -  errname String.hex2string; +     optflags OPT_TRY_OPTIMIZE;   {    struct pike_string *s;    int tmp, i;    unsigned char *p, *q = (unsigned char *)hex->str; -  int l = hex->len>>1; -  if(hex->size_shift) Pike_error("Only hex digits allowed.\n"); -  if(hex->len&1) Pike_error("Can't have odd number of digits.\n"); +  int l = i = hex->len; +  if(hex->size_shift) Pike_error("Wide strings are not allowed.\n");    -  s = begin_shared_string(l); +  s = begin_shared_string(l>>1);    p = (unsigned char *)s->str; -  for (i=0; i<l; i++) +  while( l > 1 )    {    tmp = hexdecode[*q++]; -  *p++ = (tmp<<4) | hexdecode[*q++]; +  if( tmp==16 ) { l--; i--; continue; } +  *p = tmp<<4; +  tmp = hexdecode[*q++]; +  if( tmp==16 ) Pike_error("Illegal hex format.\n"); +  *p++ |= tmp; +  l -= 2;    } -  +  if( l && hexdecode[*q]!=16 ) +  Pike_error("Illegal hex format.\n"); +  +  if( hex->len>>1 == i>>1 )    RETURN end_shared_string(s); -  +  else +  RETURN end_and_resize_shared_string(s, i>>1);   }    -  + /*! @decl array(int) range(string s) +  *! @appears String.range +  *! +  *! Returns the character range of a string in an array of two +  *! elements. The first element contains the lower bound and the +  *! second the upper. The precision is only 8 bits, so for wide +  *! strings only character blocks are known. +  */ + PIKEFUN array(int) string_range(string s) +  errname range; +  optflags OPT_TRY_OPTIMIZE; + { +  int min, max; +  check_string_range(s, 0, &min, &max); +  pop_n_elems(args); +  push_int(min); +  push_int(max); +  f_aggregate(2); + } +    /*! @decl array column(array data, mixed index)    *!    *! Extract a column from a two-dimensional array.    *!    *! This function is exactly equivalent to:    *! @code    *! map(@[data], lambda(mixed x,mixed y) { return x[y]; }, @[index])    *! @endcode    *!    *! Except of course it is a lot shorter and faster.
pike.git/src/builtin.cmod:1106:   {    INT32 old_level;    if (facility) {    struct pike_string *gc_str;    MAKE_CONST_STRING(gc_str, "gc");    if (facility == gc_str) {    old_level = gc_trace;    gc_trace = level;    }    else { -  bad_arg_error("trace", Pike_sp-args, args, 2, +  bad_arg_error("trace", args, 2,    "trace facility identifier", Pike_sp-args+1,    "Bad argument 2 to trace(). Unknown trace facility.");    }    }    else {    old_level = Pike_interpreter.trace_level;   #ifdef PIKE_THREADS    if (!all_threads)    Pike_interpreter.trace_level = level;    else {
pike.git/src/builtin.cmod:1176:    *! @member float "average_slowness"    *! When predicting the next gc interval, use a decaying average    *! with this slowness factor. It should be a value between 0.0 and    *! 1.0 that specifies the weight to give to the old average value.    *! The remaining weight up to 1.0 is given to the last reading.    *! @member function(:void) "pre_cb"    *! This function is called when the gc starts.    *! @member function(:void) "post_cb"    *! This function is called when the mark and sweep pass of the gc    *! is done. -  *! @member function(object:void) "destruct_cb" +  *! @member function(object,int,int:void) "destruct_cb"    *! This function is called once for each object that is part of    *! a cycle just before the gc will destruct it. -  +  *! The arguments are: +  *! @dl +  *! @item +  *! The object to be destructed. +  *! @item +  *! The reason for it being destructed. One of: +  *! @int +  *! @value Object.DESTRUCT_CLEANUP +  *! Destructed during exit. +  *! @value Object.DESTRUCT_GC +  *! Destructed during normal implicit or explicit @[gc()]. +  *! @endint +  *! @item +  *! The number of references it had. +  *! @enddl    *! @member function(int:void) "done_cb"    *! This function is called when the gc is done and about to exit.    *! The argument is the same value as will be returned by gc().    *! @endmapping    *!    *! @seealso    *! @[gc], @[Debug.gc_status]    */   PMOD_EXPORT   PIKEFUN mapping(string:mixed) gc_parameters (void|mapping(string:mixed) params) -  errname Pike.gc_parameters; +     optflags OPT_SIDE_EFFECT;   {    struct pike_string *str;    struct svalue *set;    struct svalue get;       if (!params) {    push_mapping (allocate_mapping (6));    params = Pike_sp[-1].u.mapping;    }
pike.git/src/builtin.cmod:1216:    else { \    GET; \    mapping_string_insert (params, str, &get); \    } \    } while (0)      #define HANDLE_FLOAT_FACTOR(NAME, VAR) \    HANDLE_PARAM (NAME, { \    if (TYPEOF(*set) != T_FLOAT || \    set->u.float_number < 0.0 || set->u.float_number > 1.0) \ -  SIMPLE_BAD_ARG_ERROR ("Pike.gc_parameters", 1, \ +  SIMPLE_ARG_TYPE_ERROR ("gc_parameters", 1, \    "float between 0.0 and 1.0 for " NAME); \ -  VAR = DO_NOT_WARN ((double) set->u.float_number); \ +  VAR = (double) set->u.float_number; \    }, { \ -  SET_SVAL(get, T_FLOAT, 0, float_number, \ -  DO_NOT_WARN ((FLOAT_TYPE) VAR)); \ +  SET_SVAL(get, T_FLOAT, 0, float_number, (FLOAT_TYPE) VAR); \    });       HANDLE_PARAM ("enabled", {    if (TYPEOF(*set) != T_INT || set->u.integer < -1 || set->u.integer > 1) -  SIMPLE_BAD_ARG_ERROR ("Pike.gc_parameters", 1, +  SIMPLE_ARG_TYPE_ERROR ("gc_parameters", 1,    "integer in the range -1..1 for 'enabled'");    if (gc_enabled != set->u.integer) {    if (gc_enabled > 0) {    /* Disabling automatic gc - save the old alloc_threshold and set it to    * the maximum value to avoid getting gc_evaluator_callback added. */    saved_alloc_threshold = alloc_threshold;    alloc_threshold = GC_MAX_ALLOC_THRESHOLD;    }    else if (set->u.integer > 0) {    /* Enabling automatic gc - restore the old alloc_threshold. If the
pike.git/src/builtin.cmod:1290:    *!    *! Convert the output from a previous call to @[time()] into a readable    *! string containing the current year, month, day and time.    *!    *! Like @[localtime], this function might throw an error if the    *! ctime(2) call failed on the system. It's platform dependent what    *! time ranges that function can handle, e.g. Windows doesn't handle    *! a negative @[timestamp].    *!    *! @seealso -  *! @[time()], @[localtime()], @[mktime()], @[gmtime()] +  *! @[strftime()], @[time()], @[localtime()], @[gmtime()], @[mktime()]    */   PMOD_EXPORT   PIKEFUN string ctime(longest timestamp)    efun;    optflags OPT_TRY_OPTIMIZE;   {    time_t i;    char *s;    - #if SIZEOF_TIME_T < SIZEOF_LONGEST + #if SIZEOF_TIME_T < SIZEOF_INT64    if (timestamp > MAX_TIME_T || timestamp < MIN_TIME_T)    SIMPLE_ARG_ERROR ("ctime", 1, "Timestamp outside valid range.");   #endif       i = (time_t) timestamp;    s = ctime (&i);    if (!s) Pike_error ("ctime() on this system cannot handle "    "the timestamp %ld.\n", (long) i);    RETURN make_shared_string(s);   }
pike.git/src/builtin.cmod:1331:    *!    *! @seealso    *! @[indices()], @[values()]    */   PMOD_EXPORT   PIKEFUN mapping(1:2) mkmapping(array(1=mixed) ind, array(2=mixed) val)    efun;    optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND;   {    if(ind->size != val->size) -  bad_arg_error("mkmapping", Pike_sp-args, args, 2, "array", Pike_sp+1-args, +  bad_arg_error("mkmapping", args, 2, "array", Pike_sp+1-args,    "mkmapping called on arrays of different sizes (%d != %d)\n",    ind->size, val->size);       RETURN mkmapping(ind, val);   }    - /*! @decl void secure(string str) + /*! @decl string secure(string str)    *! @belongs String    *!    *! Marks the string as secure, which will clear the memory area    *! before freeing the string. -  +  *! +  *! @seealso +  *! @[Object.secure()]    */   PIKEFUN string string_secure(string str)    optflags OPT_SIDE_EFFECT;    rawtype tFunc(tSetvar(0, tStr), tVar(0));   {    str->flags |= STRING_CLEAR_ON_EXIT;    REF_RETURN str;   }    -  + /*! @decl object secure(object str) +  *! @belongs Object +  *! +  *! Marks the object as secure, which will clear the memory area +  *! before freeing the object. +  *! +  *! @seealso +  *! @[String.secure()] +  */ + PIKEFUN object object_secure(object obj) +  optflags OPT_SIDE_EFFECT; +  rawtype tFunc(tSetvar(0, tObj), tVar(0)); + { +  obj->flags |= OBJECT_CLEAR_ON_EXIT; +  REF_RETURN obj; + } +    /*! @decl int count(string haystack, string needle)    *! @belongs String    *!    *! Count the number of non-overlapping times the string @[needle]    *! occurs in the string @[haystack]. The special cases for the needle    *! @expr{""@} is that it occurs one time in the empty string, zero    *! times in a one character string and between every character    *! (length-1) in any other string.    *!    *! @seealso    *! @[search()], @[`/()]    */   PMOD_EXPORT   PIKEFUN int string_count(string haystack, string needle) -  errname String.count; +  errname count;    optflags OPT_TRY_OPTIMIZE;   {    ptrdiff_t c = 0;    ptrdiff_t i, j;       switch (needle->len)    {    case 0:    switch (haystack->len)    {
pike.git/src/builtin.cmod:1395:    default:    for (i=0; i<haystack->len; i++)    {    j=string_search(haystack,needle,i);    if (j==-1) break;    i=j+needle->len-1;    c++;    }    break;    } -  RETURN DO_NOT_WARN((INT_TYPE)c); +  RETURN (INT_TYPE)c;   }      /*! @decl string trim_whites (string s)    *! @belongs String    *!    *! Trim leading and trailing spaces and tabs from the string @[s].    */   PMOD_EXPORT   PIKEFUN string string_trim_whites (string s) -  errname String.trim_whites; +  errname trim_whites;    optflags OPT_TRY_OPTIMIZE;   {    ptrdiff_t start = 0, end = s->len;    int chr;    switch (s->size_shift) {   #define DO_IT(TYPE) \    { \    for (; start < s->len; start++) { \    chr = ((TYPE *) s->str)[start]; \    if (chr != ' ' && chr != '\t') break; \
pike.git/src/builtin.cmod:1455:    *! @note    *! Trailing and leading whitespace around \r and \n characters    *! is stripped as well (only useful if they're not in the @[whitespace] set).    *!    *! @note    *! This function is a lot faster with just one argument (i.e. the builtin    *! whitespace set has an optimised code path).    */   PMOD_EXPORT   PIKEFUN string string_normalize_space (string s, string|void whitespace) -  errname String.normalize_space; +  errname normalize_space;    optflags OPT_TRY_OPTIMIZE;   {    size_t len = s->len, wlen;    const void *src = s->str;    unsigned shift = s->size_shift, replspace;    const void *ws;    void *wstemp = 0;    struct string_builder sb;    unsigned foundspace = 0;       wlen = replspace = 0; /* useless, but suppresses silly compiler warning */       {    unsigned bshift = shift, wshift; -  if(whitespace) -  if(!(wlen = whitespace->len)) +  if(whitespace) { +  if(!(wlen = whitespace->len)) {    REF_RETURN s; -  else { +  } else {    ws = whitespace->str; wshift = whitespace->size_shift;    replspace = index_shared_string(whitespace, 0);    if(replspace > 0xffff)    bshift = 2;    else if(replspace > 0xff && !bshift)    bshift = 1;    if(wshift!=shift) { /* convert whitespace to shift of input */    PCHARP pcnws;    wstemp = xalloc(wlen<<shift);    pcnws = MKPCHARP(wstemp, shift);
pike.git/src/builtin.cmod:1498:    do {    unsigned chr = INDEX_PCHARP(pcows, i++);    if (chr<=0xff || (chr<=0xffff && shift)) /* shift is 0 or 1 */    SET_INDEX_PCHARP(pcnws, wlen++, chr);    } while(--clen);    } else    pike_string_cpy(pcnws, whitespace);    ws = wstemp;    }    } -  else +  } else    ws = 0;       init_string_builder_alloc (&sb, len, bshift);    if(bshift == shift)    sb.known_shift = bshift;    } - #define SPACECASE8 \ -  case ' ':case '\t':case '\r':case '\n':case '\v':case '\f': \ -  case 0x85:case 0xa0: - #include "whitespace.h" +        switch (shift) {   #define NORMALISE_TIGHT_LOOP(TYPE,CASE) \    { \    const TYPE *start = src, *end = start+len; \    if (!ws) { \    TYPE *dst = (void*)sb.s->str; \    for (; start < end; start++) { \    switch(*start) { \    CASE \
pike.git/src/builtin.cmod:1573:    } \    string_builder_putchar(&sb, chr); foundspace=0; \   skip##TYPE:; \    } \    } \    }    case 0: NORMALISE_TIGHT_LOOP (p_wchar0,SPACECASE8); break;    case 1: NORMALISE_TIGHT_LOOP (p_wchar1,SPACECASE16); break;    case 2: NORMALISE_TIGHT_LOOP (p_wchar2,SPACECASE16); break;   #undef NORMALISE_TIGHT_LOOP - #undef SPACECASE8 - #undef SPACECASE16 +     }    if (wstemp)    free(wstemp);    if (foundspace)    sb.s->len--;    RETURN finish_string_builder (&sb);   }    - /*! @decl string trim_all_whites (string s) + /*! @decl string trim (string s)    *! @belongs String    *!    *! Trim leading and trailing white spaces characters (space, tab,    *! newline, carriage return, form feed, vertical tab and all the    *! white spaces defined in Unicode) from the string @[s].    */   PMOD_EXPORT - PIKEFUN string string_trim_all_whites (string s) -  errname String.trim_all_whites; + PIKEFUN string string_trim (string s) +  errname trim;    optflags OPT_TRY_OPTIMIZE;   {    ptrdiff_t start = 0, end = s->len;    int chr;    switch (s->size_shift) { - #define SPACECASE8 \ -  case ' ':case '\t':case '\r':case '\n':case '\v':case '\f': \ -  case 0x85:case 0xa0: - #include "whitespace.h" +       #define DO_IT(TYPE,CASE) \    { \    for (; start < end; start++) { \    chr = ((TYPE *) s->str)[start]; \    switch(chr) { \    CASE \    continue; \    } \    break; \
pike.git/src/builtin.cmod:1626:    CASE \    continue; \    } \    break; \    } \    }    case 0: DO_IT (p_wchar0,SPACECASE8); break;    case 1: DO_IT (p_wchar1,SPACECASE16); break;    case 2: DO_IT (p_wchar2,SPACECASE16); break;   #undef DO_IT - #undef SPACECASE8 - #undef SPACECASE16 +     }    RETURN string_slice (s, start, end + 1 - start);   }      /*! @decl string status(int verbose)    *! @belongs String    *!    *! Get string table statistics.    *!    *! @returns
pike.git/src/builtin.cmod:1650:    *!    *! @note    *! Currently returns the empty string (@expr{""@})    *! if @[verbose] is zero.    *!    *! @note    *! The formatting and contents of the result    *! may vary between different versions of Pike.    */   PIKEFUN string string_status(int verbose) -  errname String.status; +  errname status;   {    RETURN add_string_status(verbose);   }      /*! @decl int implements(program prog, program api)    *! @belongs Program    *!    *! Returns 1 if @[prog] implements @[api].    */   PMOD_EXPORT   PIKEFUN int program_implements(program prog, program api) -  errname Program.implements; +  errname implements;    optflags OPT_TRY_OPTIMIZE;   {    RETURN implements(prog, api);   }      /*! @decl int inherits(program|object child, program parent)    *! @belongs Program    *!    *! Returns 1 if @[child] has inherited @[parent].    */   PMOD_EXPORT   PIKEFUN int program_inherits(program|object child, program parent) -  errname Program.inherits; +  errname inherits;    optflags OPT_TRY_OPTIMIZE;   {    struct program *p = program_from_svalue(child);       if (!p) -  SIMPLE_ARG_TYPE_ERROR("Program.inherits", 1, "program|object"); +  SIMPLE_ARG_TYPE_ERROR("inherits", 1, "program|object");    RETURN low_get_storage(p, parent) != -1;   }      /*! @decl string defined(program p)    *! @belongs Program    *!    *! Returns a string with filename and linenumber describing where    *! the program @[p] was defined.    *!    *! The returned string is of the format @expr{"filename:linenumber"@}.    *!    *! If it cannot be determined where the program was defined, @expr{0@}    *! (zero) will be returned.    */   PMOD_EXPORT   PIKEFUN string program_defined(program p) -  errname Program.defined; +  errname defined;    optflags OPT_TRY_OPTIMIZE;   {    INT_TYPE line;    struct pike_string *tmp = low_get_program_line(p, &line);       pop_n_elems(args);       if (tmp) {    push_string(tmp);    if(line >= 1)    { -  push_constant_text(":"); +  push_static_text(":");    push_int(line);    f_add(3);    }    }    else    push_int(0);   }      /*! @decl int(8..8)|int(16..16)|int(32..32) width(string s)    *! @belongs String
pike.git/src/builtin.cmod:1742:    *! The string @[s] contains characters >= 65536.    *! @endint    *!    *! @note    *! It is possible that a future version of Pike may return    *! further values. In particular the width @expr{7@} seems    *! like it could be useful.    */   PMOD_EXPORT   PIKEFUN int(8 .. 8)|int(16 .. 16)|int(32 .. 32) string_width(string s) -  errname String.width; +  errname width;    optflags OPT_TRY_OPTIMIZE;   {    RETURN 8 * (1 << s->size_shift);   }      /*! @decl mixed m_delete(object|mapping map, mixed index)    *!    *! If @[map] is an object that implements @[lfun::_m_delete()],    *! that function will be called with @[index] as its single argument.    *!
pike.git/src/builtin.cmod:1788:    pop_n_elems(args);    *Pike_sp=s;    Pike_sp++;    dmalloc_touch_svalue(Pike_sp-1);    }    else if (TYPEOF(*map) == T_OBJECT && (p = map->u.object->prog))    {    int id = FIND_LFUN(p->inherits[SUBTYPEOF(*map)].prog, LFUN__M_DELETE);       if( id == -1 ) -  SIMPLE_BAD_ARG_ERROR("m_delete", 1, "object containing the _m_delete method"); +  SIMPLE_ARG_TYPE_ERROR("m_delete", 1, "object containing the _m_delete method");       apply_low(map->u.object,    id + p->inherits[SUBTYPEOF(*map)].identifier_level, 1);    stack_swap();    pop_stack();    } else { -  SIMPLE_BAD_ARG_ERROR("m_delete", 1, "object|mapping"); +  SIMPLE_ARG_TYPE_ERROR("m_delete", 1, "object|mapping");    }   }    -  + /*! @decl void m_clear(mapping map) +  *! +  *! Clear the contents of a mapping. +  *! +  *! This function clears the content of the mapping @[map] so +  *! that it becomes empty. This is an atomic operation. +  *! +  *! @seealso +  *! @[m_delete()] +  */ + PMOD_EXPORT + PIKEFUN void m_clear(mapping map) +  efun +  optflags OPT_SIDE_EFFECT; + { +  /* FIXME: Add an LFUN__M_CLEAR analogous with LFUN__M_DELETE? */ +  clear_mapping(map); + } +    /*! @decl int get_weak_flag(array|mapping|multiset m)    *!    *! Returns the weak flag settings for @[m]. It's a combination of    *! @[Pike.WEAK_INDICES] and @[Pike.WEAK_VALUES].    */   PMOD_EXPORT   PIKEFUN int get_weak_flag(array m)    efun;    optflags OPT_EXTERNAL_DEPEND;   {
pike.git/src/builtin.cmod:1832:   }      /*! @decl program __empty_program(int|void line, string|void file)    */   PIKEFUN program __empty_program(int|zero|void line, string|void file)    efun;    optflags OPT_EXTERNAL_DEPEND;   {    struct program *prog = low_allocate_program();    if (file) ext_store_program_line (prog, line, file); - #if 0 -  push_program (prog); -  safe_pike_fprintf (stderr, "Creating empty program %O (%x)\n", -  Pike_sp - 1, Pike_sp[-1].u.program); -  Pike_sp--; - #endif +     RETURN prog;   }      /* Cut the string at the first NUL. */   static struct pike_string *delambda(struct pike_string *str)   {    PCHARP pcharp = MKPCHARP_STR(str);    ptrdiff_t len = pcharp_strlen(pcharp);    if (len == str->len) {    /* Common case. */
pike.git/src/builtin.cmod:1875:   PIKEFUN string function_name(program|function func)    efun;    optflags OPT_TRY_OPTIMIZE;   {    int f = -1;    struct program *p = NULL;       switch(TYPEOF(*func))    {    default: -  SIMPLE_BAD_ARG_ERROR("function_name", 1, "function|program"); -  return; /* NOTREACHED */ +  SIMPLE_ARG_TYPE_ERROR("function_name", 1, "function|program"); +  UNREACHABLE(return);       case PIKE_T_PROGRAM:    {    p = func->u.program;       if(p->parent)    {    int e;    p=p->parent;    /* search constants in parent for this
pike.git/src/builtin.cmod:1919:    dump_program_tables(func->u.program->parent, 0);    }   #endif    }    break;    }       case PIKE_T_FUNCTION:    if((f = SUBTYPEOF(*func)) == FUNCTION_BUILTIN) break;    if(!(p = func->u.object->prog)) -  bad_arg_error("function_name", Pike_sp-args, args, 1, +  bad_arg_error("function_name", args, 1,    "function", Pike_sp-args,    "Destructed object.\n");    if(p == pike_trampoline_program)    {    struct pike_trampoline *t;    t=((struct pike_trampoline *)func->u.object->storage);       if(t->frame->current_object->prog) {    p = t->frame->current_object->prog;    f = t->func;
pike.git/src/builtin.cmod:1984:    add_ref(o);    pop_n_elems(args);    push_object(o);    return;    }    SET_SVAL(*func, T_OBJECT, 0, object, func->u.object);    return;          default: -  SIMPLE_BAD_ARG_ERROR("function_object",1,"function"); +  SIMPLE_ARG_TYPE_ERROR("function_object",1,"function");    }    pop_n_elems(args);    push_int(0);   }      /*! @decl program function_program(function|program f)    *!    *! Return the program the function @[f] is in.    *!    *! If @[f] is a global function defined in the runtime @expr{0@}
pike.git/src/builtin.cmod:2038:    }    if (p) {    ref_push_program(p);    stack_pop_n_elems_keep_top(args);    return;    }    }    break;       default: -  SIMPLE_BAD_ARG_ERROR("function_program", 1, "function"); +  SIMPLE_ARG_TYPE_ERROR("function_program", 1, "function");    }    pop_n_elems(args);    push_int(0);   }    -  + /*! @class RandomInterface +  */ + PIKECLASS RandomInterface + { +  CVAR UINT64 int_buffer; +  CVAR int buffer_bits;    - /*! @decl mixed random(object o) -  *! If random is called with an object, @[lfun::random] will be -  *! called in the object. + #ifdef PIKE_NULL_IS_SPECIAL +  INIT +  { +  THIS->int_buffer = 0; +  THIS->buffer_bits = 0; +  } + #endif +  +  /*! @decl string(8bit) random_string(int(0..))    *! -  *! @seealso -  *! @[lfun::_random()] +  *! Prototype for the randomness generating function. +  *! +  *! Override this symbol to implement a usable class.    */ -  +  PIKEFUN string(8bit) random_string(int(0..)) +  prototype; +  {}    - PMOD_EXPORT - PIKEFUN mixed random(object o) -  efun; -  optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND; +  static void fill_int_buffer()    { -  int f = low_find_lfun(o->prog, LFUN__RANDOM); -  if (f < 0) { -  Pike_error("Calling undefined lfun::%s.\n", lfun_names[LFUN__RANDOM]); +  push_int(8); +  apply_current(f_RandomInterface_random_string_fun_num, 1); +  if( TYPEOF(Pike_sp[-1]) != T_STRING || +  Pike_sp[-1].u.string->len != 8 ) +  Pike_error("Illegal return value from random_string.\n"); + #if PIKE_BYTEORDER == 1234 +  /* Little endian. */ +  THIS->int_buffer = ((INT64 *)Pike_sp[-1].u.string->str)[0]; + #else +  /* Big endian. */ +  { +  int i; +  for (i = 0; i < 8; i++) { +  ((p_wchar0 *)&THIS->int_buffer)[i] = STR0(Pike_sp[-1].u.string)[7-i];    } -  apply_low(o, f, 0); -  stack_swap(); +  } + #endif +  THIS->buffer_bits = 64;    pop_stack();    }    -  +  /* Generates a number 0<=c<limit from random bits taken from the +  int_buffer. Follows the NIST SP800-90A method for converting bit +  sequences into bound numbers, described in section B.5.1.1, and +  summarized as "throw away attempts that are too large". */ +  static INT_TYPE read_int(INT_TYPE limit) +  { +  int bits; +  UINT64 mask; +  int i; +  +  if(limit <= 1) return 0; +  +  bits = my_log2(limit-1)+1; +  mask = (((UINT64)1)<<bits)-1; +  for(i=0; i<1000; i++) +  { +  INT_TYPE ret; +  if(THIS->buffer_bits < bits) +  fill_int_buffer(); +  ret = THIS->int_buffer & mask; +  THIS->int_buffer >>= bits; +  THIS->buffer_bits -= bits; +  if( ret < limit ) +  return ret; +  } +  Pike_error("Failed to generate random data.\n"); +  } +     /*! @decl int random(int max) -  *! @decl float random(float max) +     *! -  *! This function returns a random number in the range 0 - @[max]-1. +  *! This function returns a random number in the range +  *! @expr{0 .. @[max]-1@}.    *!    *! @seealso -  *! @[random_seed()] +  *! @[Random]    */ -  - PMOD_EXPORT - PIKEFUN int random(int i) +  PIKEFUN int(0..) random(int limit)    { -  if(i <= 0) RETURN 0; - #if SIZEOF_INT_TYPE > 4 -  if(i >> 31) { -  unsigned INT_TYPE a = my_rand(); -  unsigned INT_TYPE b = my_rand(); -  RETURN (INT_TYPE)(((a<<32)|b) % i); +  RETURN read_int(limit);    } - #endif -  RETURN my_rand() % i; - } +     - PMOD_EXPORT +  /*! @decl float random(float max) +  *! +  *! This function returns a random number in the range +  *! @expr{0 .. @[max]-ɛ@}. +  *! +  *! @seealso +  *! @[Random] +  */    PIKEFUN float random(float f)    { -  +  UINT64 value;    if(f<=0.0) RETURN 0.0; - #define N 1048576 -  RETURN f * (my_rand()%N/((float)N)) + -  f * (my_rand()%N/( ((float)N) * ((float)N) )); +  if (THIS->buffer_bits < 64) +  fill_int_buffer(); +  value = THIS->int_buffer; +  THIS->buffer_bits = 0;    -  +  RETURN (FLOAT_TYPE)ldexp((double)f * value, -64);    }       /*! @decl mixed random(array|multiset x)    *! Returns a random element from @[x]. -  +  *! +  *! @throws +  *! Throws an exception if the array or multiset is empty.    */    - PMOD_EXPORT +     PIKEFUN mixed random(array a)    rawtype tFunc(tArr(tSetvar(0,tMix)),tVar(0));    {    if(!a->size) -  SIMPLE_BAD_ARG_ERROR("random", 1, "array with elements in it"); -  push_svalue(a->item + (my_rand() % a->size)); -  stack_swap(); -  pop_stack(); +  SIMPLE_ARG_TYPE_ERROR("random", 1, "array with elements in it"); +  push_svalue(a->item + (read_int(a->size)));    }    - PMOD_EXPORT +     PIKEFUN mixed random(multiset m)    rawtype tFunc(tSet(tSetvar(1,tMix)),tVar(1));    {    if(multiset_is_empty (m)) -  SIMPLE_BAD_ARG_ERROR("random", 1, "multiset with elements in it"); -  if (multiset_indval (m)) { -  ptrdiff_t nodepos = multiset_get_nth (m, my_rand() % multiset_sizeof (m)); -  push_multiset_index (m, nodepos); -  push_multiset_value (m, nodepos); +  SIMPLE_ARG_TYPE_ERROR("random", 1, "multiset with elements in it"); +  push_multiset_index (m, multiset_get_nth (m, read_int(multiset_sizeof (m))));    sub_msnode_ref (m); -  f_aggregate (2); +     } -  else { -  push_multiset_index (m, multiset_get_nth (m, my_rand() % -  multiset_sizeof (m))); -  sub_msnode_ref (m); -  } -  stack_swap(); -  pop_stack(); - } +        /*! @decl array random(mapping m)    *! Returns a random index-value pair from the mapping. -  +  *! +  *! @array +  *! @elem mixed 0 +  *! The index of the mapping entry. +  *! @elem mixed 1 +  *! The value f the mapping entry. +  *! @endarray +  *! +  *! @throws +  *! Throws an exception if the mapping is empty.    */ -  - PMOD_EXPORT +     PIKEFUN array random(mapping m)    { -  struct mapping_data *md=m->data; -  size_t bucket, count; +     struct keypair *k; -  +  int e, count;       if(!m_sizeof(m)) -  SIMPLE_BAD_ARG_ERROR("random", 1, "mapping with elements in it"); +  SIMPLE_ARG_TYPE_ERROR("random", 1, "mapping with elements in it");    -  /* Find a random, nonempty bucket */ -  bucket=my_rand() % md->hashsize; -  while(! md->hash[bucket] ) -  if(++bucket > (size_t)md->hashsize) -  bucket=0; +  count = read_int( m_sizeof(m) );    -  /* Count entries in bucket */ -  count=0; -  for(k=md->hash[bucket];k;k=k->next) count++; -  -  /* Select a random entry in this bucket */ -  count = my_rand() % count; -  k=md->hash[bucket]; -  while(count-- > 0) k=k->next; -  -  /* Push result and return */ +  NEW_MAPPING_LOOP(m->data) +  { +  if(count-- < 1) +  {    push_svalue(&k->ind);    push_svalue(&k->val);    f_aggregate(2); -  stack_swap(); -  pop_stack(); +  return;    } -  +  }    -  +  UNREACHABLE(return); +  } +  +  /*! @decl mixed random(object o) +  *! If random is called with an object, @[lfun::_random] will be +  *! called in the object. +  *! +  *! @throws +  *! Throws an exception if the object doesn't have a _random method. +  *! +  *! @seealso +  *! @[lfun::_random()] +  */ +  PIKEFUN mixed random(object o) +  { +  int f = low_find_lfun(o->prog, LFUN__RANDOM); +  struct object *co; +  +  if (f < 0) +  Pike_error("Calling undefined lfun::%s.\n", lfun_names[LFUN__RANDOM]); +  +  co = Pike_fp->current_object; +  ref_push_function(co, f_RandomInterface_random_string_fun_num); +  /* FIXME: precompiler doesn't generate usable fun_nums for variant +  symbols. */ +  ref_push_function(co, find_shared_string_identifier(MK_STRING("random"), +  co->prog)); +  apply_low(o, f, 2); +  } + } + /*! @endclass +  */ +  + /** +  * Generates a random string of length len, using the current +  * random_string() function, and pushes it on the stack. +  */ + PMOD_EXPORT void push_random_string(unsigned len) + { +  struct svalue *random = +  simple_mapping_string_lookup(get_builtin_constants(), "random_string"); +  if(!random || (TYPEOF(*random) != T_FUNCTION)) +  Pike_error("Unable to resolve random function.\n"); +  push_int(len); +  apply_svalue(random, 1); +  if(TYPEOF(Pike_sp[-1])!=T_STRING || Pike_sp[-1].u.string->len != (ptrdiff_t)len || +  Pike_sp[-1].u.string->size_shift != 0) +  Pike_error("Couldn't generate random string.\n"); + } +  + #ifdef __NT__ + #include <wincrypt.h> + static HCRYPTPROV crypto_handle; + #else + static int random_fd = -1; + #endif + /*! @class RandomSystem +  */ + PIKECLASS RandomSystem + { +  /*! @decl inherit RandomInterface +  */ +  INHERIT RandomInterface; +  +  /*! @decl string(8bit) random_string(int(0..) len) +  *! +  *! Return a string of random data from the system randomness pool. +  *! +  *! On POSIX platforms this reads random data from @tt{/dev/urandom@} +  *! on other platforms it may use other methods. +  *! +  *! @throws +  *! May throw errors on unexpected state. +  */ +  PIKEFUN string(8bit) random_string(int(0..) len) +  { +  struct pike_string *ret; +  char *str; +  +  if( !len ) +  REF_RETURN empty_pike_string; +  if( len<0 ) +  Pike_error("Bad argument 1 to random_string(). Expected int(0..).\n"); +  + #ifdef __NT__ +  if(!crypto_handle) +  { +  if( !CryptAcquireContext(&crypto_handle, 0, 0, PROV_RSA_FULL, +  CRYPT_VERIFYCONTEXT|CRYPT_SILENT) ) +  Pike_error("Failed to set up Crypto Service.\n"); +  } +  +  ret = begin_shared_string(len); +  str = ret->str; +  if( !CryptGenRandom(crypto_handle, len, (BYTE*)str) ) +  { +  do_free_unlinked_pike_string (ret); +  Pike_error("Failed to create random data.\n"); +  } + #else /* !__NT__ */ +  while( random_fd == -1 ) +  { +  random_fd = open("/dev/urandom", O_RDONLY); +  if( random_fd==-1 ) { +  if (errno == EINTR) continue; /* Retry */ +  Pike_error("Failed to open /dev/urandom.\n"); +  } +  } +  +  ret = begin_shared_string(len); +  str = ret->str; +  while( len ) +  { +  int sz = read(random_fd, str, len); +  if (sz < 0) { +  if (errno == EINTR) continue; /* Retry */ +  +  free_string(ret); +  +  /* Attempt to recover on next call. */ +  close(random_fd); +  random_fd = -1; +  +  Pike_error("Failed to read %d bytes from /dev/urandom.\n", len); +  } +  str += sz; +  len -= sz; +  } + #endif /* !__NT__ */ +  +  RETURN end_shared_string(ret); +  } + } + /*! @endclass +  */ +    #if defined(HAVE_SETENV) && defined(HAVE_UNSETENV)   #define USE_SETENV   #else   /* Used to hold refs to the strings that we feed to putenv. Indexed on    * variable names, values are the "name=value" strings.    *    * This is not needed when using {,un}setenv(), since they maintain    * their own corresponding table. */   static struct mapping *env_allocs = NULL;   #endif
pike.git/src/builtin.cmod:2209:    pop_stack();    push_int (0);    }       else {    char *entry = getenv (var->str);    pop_stack();    if (!entry)    push_int (0);    else { -  char *eq = STRCHR (entry, '='); +  char *eq = strchr (entry, '=');    /* There should always be a '=' in the entry, but you never know.. */    push_string (make_shared_string (eq ? eq + 1 : entry));    }    }    }       else {   #ifdef DECLARE_ENVIRON    extern char **environ;   #endif
pike.git/src/builtin.cmod:2237:    * one in gnu libc). */    for (n = 0; environ[n]; n++) {}       m = allocate_mapping (n);   #ifndef USE_SETENV    if (env_allocs)    new_env_allocs = allocate_mapping (m_sizeof (env_allocs));   #endif /* !USE_SETENV */       while (--n >= 0) { -  char *entry = environ[n], *eq = STRCHR (entry, '='); +  char *entry = environ[n], *eq = strchr (entry, '=');    if (eq) { /* gnu libc getenv ignores variables without '='. */    struct pike_string *var = make_shared_binary_string (entry, eq - entry);    struct pike_string *val = make_shared_string (eq + 1);    mapping_string_insert_string (m, var, val);      #ifndef USE_SETENV    /* Populate new_env_allocs with the env_allocs entries that    * are still in use. */    if (env_allocs) {    struct svalue *ea_val = low_mapping_string_lookup (env_allocs, var);
pike.git/src/builtin.cmod:2280:    * updates the real environment. Everyone should use the version in    * the master instead so that the cache doesn't get stale. */   PIKEFUN void _putenv (string var, void|string val)   {   #ifndef USE_SETENV    struct pike_string *putenv_str, *env_alloc_var;   #endif       if (var->size_shift)    SIMPLE_ARG_TYPE_ERROR ("putenv", 1, "string(0..255)"); -  if (string_has_null (var) || STRCHR (var->str, '=')) +  if (string_has_null (var) || strchr (var->str, '='))    SIMPLE_ARG_ERROR ("putenv", 1, "Variable name cannot contain '=' or NUL.");       if (val) {   #ifndef USE_SETENV    struct string_builder sb;   #endif       if (val->size_shift)    SIMPLE_ARG_TYPE_ERROR ("putenv", 2, "void|string(0..255)");    if (string_has_null (val))
pike.git/src/builtin.cmod:2362:    * assume no other entities are naughty enough to modify it). */    mapping_string_insert_string (env_allocs, env_alloc_var, putenv_str);    else {    struct svalue key;    SET_SVAL(key, T_STRING, 0, string, env_alloc_var);    map_delete (env_allocs, &key);    }   #endif /* !USE_SETENV */   }    - #if defined(PIKE_DEBUG) && defined(PIKE_PORTABLE_BYTECODE) -  - /*! @decl void disassemble(function fun) -  *! @belongs Debug -  *! -  *! Disassemble a Pike function to @[Stdio.stderr]. -  *! -  *! @note -  *! This function is only available if the Pike runtime -  *! has been compiled with debug enabled. -  */ - PIKEFUN void _disassemble(function fun) - { -  if ((TYPEOF(*fun) != T_FUNCTION) || -  (SUBTYPEOF(*fun) == FUNCTION_BUILTIN)) { -  fprintf(stderr, -  "Disassembly only supported for functions implemented in Pike.\n"); -  } else if (!fun->u.object->prog) { -  fprintf(stderr, "Function in destructed object.\n"); -  } else { -  int f = SUBTYPEOF(*fun); -  struct reference *ptr = PTR_FROM_INT(fun->u.object->prog, f); -  struct program *p = PROG_FROM_PTR(fun->u.object->prog, ptr); -  struct identifier *id = p->identifiers + ptr->identifier_offset; -  if (id->func.offset >= 0) { -  struct pike_string *tripples = -  p->strings[read_program_data(p->program + id->func.offset, -1)]; -  switch(tripples->size_shift) { - #define CASE(SHIFT) \ -  case SHIFT: \ -  { \ -  PIKE_CONCAT(p_wchar, SHIFT) *str = \ -  PIKE_CONCAT(STR, SHIFT)(tripples); \ -  int i=0; \ -  while(i < tripples->len) { \ -  fprintf(stderr, "@@@ %d: %s, %d, %d\n", \ -  i/3, \ -  instrs[*str - F_OFFSET]. \ -  name, \ -  str[1], str[2]); \ -  str += 3; \ -  i += 3; \ -  } \ -  } \ -  break -  CASE(0); -  CASE(1); -  CASE(2); - #undef CASE -  } -  } else { -  fprintf(stderr, "Prototype.\n"); -  } -  } -  pop_n_elems(args); -  push_int(0); - } -  - #endif /* PIKE_DEBUG && PIKE_PORTABLE_BYTECODE */ -  +    /*    * Backtrace handling.    */      /*! @module Pike    */      /*! @class BacktraceFrame    */   
pike.git/src/builtin.cmod:2446:    PIKEVAR program oprog flags ID_PROTECTED|ID_PRIVATE;   #endif    PIKEVAR array args;       /* These are cleared when filename and lineno have been initialized    * from them. */    PIKEVAR program prog flags ID_PROTECTED|ID_PRIVATE;    CVAR PIKE_OPCODE_T *pc;       /* These two are considered to be uninitialized from prog, pc and -  * fun as long as lineno == -1. */ +  * fun as long as lineno == 0. */    CVAR struct pike_string *filename;    CVAR INT_TYPE lineno;    -  + #ifdef PIKE_NULL_IS_SPECIAL    INIT    {    THIS->pc = NULL; -  THIS->lineno = -1; +  THIS->lineno = 0;    THIS->filename = NULL;    } -  + #endif       EXIT    gc_trivial;    {    if (THIS->filename) {    free_string(THIS->filename);    THIS->filename = NULL;    }    THIS->pc = NULL; -  THIS->lineno = -1; +  THIS->lineno = 0;    }       /* NOTE: Use old-style getter/setter syntax for compatibility with    * old Parser.Pike.split() used by precompile.pike.    */       PIKEFUN mixed `->fun()    {    push_svalue(&THIS->_fun);    }
pike.git/src/builtin.cmod:2496:    }   #endif    assign_svalue(&THIS->_fun, val);    }       /*! @decl int(0..1) _is_type(string t)    *! This object claims to be an array for backward compatibility.    */    PIKEFUN int(0..1) _is_type(string t)    { -  INT_TYPE res = (t == findstring("array")); -  pop_n_elems(args); -  push_int(res); +  RETURN (t == literal_array_string);    }       static void fill_in_file_and_line()    {    struct pike_string *file = NULL; -  assert (THIS->lineno == -1); +  assert (THIS->lineno == 0);       if (THIS->pc && THIS->prog) {    file = low_get_line(THIS->pc, THIS->prog, &THIS->lineno);    THIS->pc = NULL;    }    else if (TYPEOF(THIS->_fun) == PIKE_T_FUNCTION) {   #ifdef PIKE_DEBUG    if (THIS->_fun.u.object->prog &&    THIS->_fun.u.object->prog != THIS->oprog) {    struct identifier *id = ID_FROM_INT(THIS->oprog, SUBTYPEOF(THIS->_fun));
pike.git/src/builtin.cmod:2541:       if (THIS->prog) {    free_program(THIS->prog);    THIS->prog = NULL;    }    }       /*! @decl string _sprintf(int c, mapping|void opts)    */    PIKEFUN string _sprintf(int c, mapping|void opts) +  flags ID_PROTECTED;    {    pop_n_elems(args);       if (c != 'O') {    push_undefined ();    return;    }    -  push_text("backtrace_frame("); +  push_static_text("backtrace_frame(");    -  if (THIS->lineno == -1) fill_in_file_and_line(); +  if (THIS->lineno == 0) fill_in_file_and_line();       if (THIS->filename) {    ref_push_string(THIS->filename); -  push_text(":"); +  push_static_text(":");    push_int(THIS->lineno); -  push_text(", "); +  push_static_text(", ");    f_add(4);    } else { -  push_text("Unknown file, "); +  push_static_text("Unknown file, ");    }    if (TYPEOF(THIS->_fun) == PIKE_T_FUNCTION) {    if (THIS->_fun.u.object->prog) {   #ifdef PIKE_DEBUG    if (THIS->_fun.u.object->prog != THIS->oprog) {    struct identifier *id =    ID_FROM_INT(THIS->oprog, SUBTYPEOF(THIS->_fun));    /* FIXME: Dump dmalloc info for the object? */    Pike_fatal("Lost track of function pointer! Function name was %s.\n",    id->name?id->name->str:"<no name>");    }   #endif    push_svalue(&THIS->_fun);    f_function_name(1); -  push_text("(), "); +  push_static_text("(), ");    f_add(2);    } else {    free_svalue(&THIS->_fun);    SET_SVAL(THIS->_fun, PIKE_T_INT, NUMBER_DESTRUCTED, integer, 0); -  push_text("destructed_function(), "); +  push_static_text("destructed_function(), ");    }    } else if (TYPEOF(THIS->_fun) == PIKE_T_PROGRAM) {    /* FIXME: Use the master? */ -  push_text("program(), "); +  push_static_text("program(), ");    } else if (TYPEOF(THIS->_fun) == PIKE_T_STRING) {    push_svalue(&THIS->_fun); -  push_text("(), "); +  push_static_text("(), ");    f_add(2);    } else { -  push_text("destructed_function(), "); +  push_static_text("destructed_function(), ");    }       if (THIS->args) { -  push_text("Args: "); +  push_static_text("Args: ");    push_int(THIS->args->size);    f_add(2);    } else { -  push_text("No args"); +  push_static_text("No args");    } -  push_text(")"); +  push_static_text(")");    f_add(5);    }       /*! @decl int(3..) _sizeof()    */    PIKEFUN int(3..) _sizeof() -  +  flags ID_PROTECTED;    {    if (THIS->args) {    push_int(THIS->args->size + 3);    } else {    push_int(3);    }    }       /*! @decl mixed `[](int index, int|void end_or_none)    *! The BacktraceFrame object can be indexed as an array.    */    PIKEFUN mixed `[](int index, int|void end_or_none) -  +  flags ID_PROTECTED;    {    INT_TYPE end = index; -  INT32 numargs = 0; +  INT32 numargs = 3;    INT32 i;    -  if (THIS->args) { -  numargs = THIS->args->size; -  } +  if (THIS->args) +  numargs += THIS->args->size;    -  numargs += 3; -  +     if (!end_or_none) { -  if (index < 0) { -  index_error("pike_frame->`[]", Pike_sp-args, args, NULL, Pike_sp-args, +  if (index < 0) +  index_error("pike_frame->`[]", args, NULL, Pike_sp-args,    "Indexing with negative index (%"PRINTPIKEINT"d)\n", index); -  } else if (index >= numargs) { -  index_error("pike_frame->`[]", Pike_sp-args, args, NULL, Pike_sp-args, +  else if (index >= numargs) +  index_error("pike_frame->`[]", args, NULL, Pike_sp-args,    "Indexing with too large index (%"PRINTPIKEINT"d)\n", index); -  } -  } else { -  if (TYPEOF(*end_or_none) != PIKE_T_INT) { -  SIMPLE_BAD_ARG_ERROR("`[]",2,"int|void"); -  } +  } else    end = end_or_none->u.integer; -  } +        pop_n_elems(args);       if (end_or_none) {    if ((end < 0) || (end < index) || (index >= numargs)) {    f_aggregate(0);    return;    }    -  if (end >= numargs) { +  if (end >= numargs)    end = numargs-1;    } -  } +        for (i = index; i <= end; i++) {    switch(i) {    case 0: /* Filename */ -  if (THIS->lineno == -1) fill_in_file_and_line(); -  if (THIS->filename) { +  if (THIS->lineno == 0) fill_in_file_and_line(); +  if (THIS->filename)    ref_push_string(THIS->filename); -  } else { +  else    push_int(0); -  } +     break;    case 1: /* Linenumber */ -  if (THIS->lineno == -1) fill_in_file_and_line(); +  if (THIS->lineno == 0) fill_in_file_and_line();    push_int(THIS->lineno);    break;    case 2: /* Function */    push_svalue(&THIS->_fun);    break;    default: /* Arguments */    {    if ((i > 2) && (THIS->args) && (i-3 < THIS->args->size)) {    push_svalue(THIS->args->item + (i - 3));    break;    } -  bad_arg_error("backtrace_frame->`[]", Pike_sp-args, args, 1, +  bad_arg_error("`[]", args, 1,    "int(0..)", Pike_sp-args,    "Bad argument 1 to backtrace_frame->`[](): "    "Expected int(0..%d)\n",    numargs + 2);    } -  /* NOT_REACHED */ -  break; +  UNREACHABLE(break);    }    } -  if (end_or_none) { +  if (end_or_none)    f_aggregate(1 + end - index);    } -  } +        /*! @decl mixed `[]=(int index, mixed value)    */    PIKEFUN mixed `[]=(int index, mixed value) -  +  flags ID_PROTECTED;    { -  INT32 numargs = 0; +  INT32 numargs = 3;    -  if (THIS->args) { -  numargs = THIS->args->size; -  } +  if (THIS->args) +  numargs += THIS->args->size;    -  numargs += 3; -  -  if ((index < -numargs) || (index >= numargs)) { -  index_error("pike_frame->`[]=", Pike_sp-args, args, NULL, Pike_sp-args, +  if ((index < -numargs) || (index >= numargs)) +  index_error("pike_frame->`[]=", args, NULL, Pike_sp-args,    "Index %"PRINTPIKEINT"d is out of array range 0..%d,\n",    index, numargs-1); -  } else if (index < 0) { +  else if (index < 0)    index += numargs; -  } +        if (args > 2) {    pop_n_elems(args - 2);    args = 2;    }       switch(index) {    case 0: /* Filename */ -  if (THIS->lineno == -1) fill_in_file_and_line(); +  if (THIS->lineno == 0) fill_in_file_and_line();    if (TYPEOF(*value) != PIKE_T_STRING) {    if ((TYPEOF(*value) != PIKE_T_INT) ||    (value->u.integer)) { -  SIMPLE_BAD_ARG_ERROR("backtrace_frame->`[]=", 2, -  "string|int(0..0)"); +  SIMPLE_ARG_TYPE_ERROR("`[]=", 2, "string|int(0..0)");    }    if (THIS->filename) {    free_string(THIS->filename);    THIS->filename = NULL;    }    } else {    if (THIS->filename) {    free_string(THIS->filename);    THIS->filename = NULL;    }    copy_shared_string(THIS->filename, value->u.string);    }    break;       case 1: /* Linenumber */ -  if (THIS->lineno == -1) fill_in_file_and_line(); -  if (TYPEOF(*value) != PIKE_T_INT) { -  SIMPLE_BAD_ARG_ERROR("backtrace_frame->`[]=", 2, "int(1..)"); -  } +  if (THIS->lineno == 0) fill_in_file_and_line(); +  if (TYPEOF(*value) != PIKE_T_INT) +  SIMPLE_ARG_TYPE_ERROR("`[]=", 2, "int(1..)");    THIS->lineno = value->u.integer;    break;       case 2: /* Function */ -  if (THIS->lineno == -1) fill_in_file_and_line(); +  if (THIS->lineno == 0) fill_in_file_and_line();    assign_svalue(&THIS->_fun, value);    break;    default: /* Arguments */    assign_svalue(THIS->args->item + index - 3, value);    break;    }    stack_swap();    pop_stack();    }   
pike.git/src/builtin.cmod:2787:    *! the Pike interpreter.    *! @member int "abi"    *! The number of bits in the ABI. Usually @expr{32@} or @expr{64@}.    *! @member int "native_byteorder"    *! The byte order used by the native cpu.    *! Usually @expr{1234@} (aka little endian) or    *! @expr{4321@} (aka bigendian).    *! @member int "int_size"    *! The number of bits in the native integer type.    *! Usually @expr{32@} or @expr{64@}. +  *! @member int "time_size" +  *! The number of bits in the native @tt{time_t@} type. +  *! This is typically the same value as @expr{"int_size"@}.    *! @member int "float_size"    *! The number of bits in the native floating point type.    *! Usually @expr{32@} or @expr{64@}. -  *! @member int(0..1) "auto_bignum" -  *! Present if integers larger than the native size are automatically -  *! converted into bignums. +  *! @member int(1..1) "auto_bignum" +  *! Integers larger than the native size are now always +  *! automatically converted into bignums.    *! @endmapping    */   PIKEFUN mapping(string:int|string) get_runtime_info()    optflags OPT_TRY_OPTIMIZE;   {    pop_n_elems(args); -  push_constant_text("bytecode_method"); -  push_constant_text(PIKE_BYTECODE_METHOD_NAME); -  push_constant_text("abi"); +  push_static_text("bytecode_method"); +  push_text(PIKE_BYTECODE_METHOD_NAME); +  push_static_text("abi");    push_int(sizeof(void *) * 8); -  push_constant_text("native_byteorder"); +  push_static_text("native_byteorder");    push_int(PIKE_BYTEORDER); -  push_constant_text("int_size"); +  push_static_text("int_size");    push_int(sizeof(INT_TYPE) * 8); -  push_constant_text("float_size"); +  push_static_text("time_size"); +  push_int(sizeof(time_t) * 8); +  push_static_text("float_size");    push_int(sizeof(FLOAT_TYPE) * 8); -  push_constant_text("auto_bignum"); +  push_static_text("auto_bignum");    push_int(1); -  f_aggregate_mapping(6*2); +  f_aggregate_mapping(7*2);   }      /*! @endmodule    */      void low_backtrace(struct Pike_interpreter_struct *i)   {    struct svalue *stack_top = i->stack_pointer;    struct pike_frame *f, *of = 0;    int size = 0;    struct array *res = NULL;       for (f = i->frame_pointer; f; f = f->next) {    size++;    }       res = allocate_array_no_init(size, 0);    push_array(res);       for (f = i->frame_pointer; f && size; f = (of = f)->next) { -  struct object *o = low_clone(backtrace_frame_program); +  struct object *o = fast_clone_object(backtrace_frame_program);    struct backtrace_frame_struct *bf;    struct identifier *function = NULL;    -  call_c_initializers(o); -  +     size--;       SET_SVAL(res->item[size], PIKE_T_OBJECT, 0, object, o);       bf = OBJ2_BACKTRACE_FRAME(o);    -  +  SET_SVAL(bf->_fun, PIKE_T_INT, NUMBER_DESTRUCTED, integer, 0); +  +  if (!f->context) { +  if (f->pc == (void *)do_gc) { +  SET_SVAL(bf->_fun, PIKE_T_STRING, 0, string, make_shared_string("gc")); +  } +  continue; +  } +     if ((bf->prog = f->context->prog)) {    add_ref(bf->prog);    bf->pc = f->pc;    }    -  SET_SVAL(bf->_fun, PIKE_T_INT, NUMBER_DESTRUCTED, integer, 0); -  +     if (f->current_object && f->current_object->prog) {    if (f->fun == FUNCTION_BUILTIN) {    /* Unusual case. The frame is from call_c_initializers(), gc()    * or similar. cf [bug 6156]. /grubba    *    * Masquerade as the program.    *    * FIXME: Ought to keep parent-pointers.    */    SET_SVAL(bf->_fun, PIKE_T_PROGRAM, 0,
pike.git/src/builtin.cmod:2876:    object, f->current_object);    add_ref(f->current_object);    function = ID_FROM_INT(f->current_object->prog, f->fun);   #ifdef PIKE_DEBUG    add_ref(bf->oprog = bf->_fun.u.object->prog);   #endif    }    }       if (f->locals) { -  INT32 numargs = DO_NOT_WARN((INT32) MINIMUM(f->num_args, -  stack_top - f->locals)); +  INT32 numargs = (INT32) MINIMUM(f->num_args, +  stack_top - f->locals);    INT32 varargs = 0;       if(of && of->locals) {    /* f->num_args can be too large, so this is necessary for some    * reason. I don't know why. /mast    *    * possibly because f->num_args was uninitialized for c_initializers    * /arne    * */    -  numargs = DO_NOT_WARN((INT32)MINIMUM(f->num_args,of->locals - f->locals)); +  numargs = (INT32)MINIMUM(f->num_args,of->locals - f->locals);    }       numargs = MAXIMUM(numargs, 0);       /* Handle varargs... */    if (function && (function->identifier_flags & IDENTIFIER_VARARGS) &&    (f->locals + numargs < stack_top) &&    (TYPEOF(f->locals[numargs]) == T_ARRAY)) {    varargs = f->locals[numargs].u.array->size;    }
pike.git/src/builtin.cmod:2942:    *! @elem function fun    *! The function that was called at this level.    *! @elem mixed|void ... args    *! The arguments that the function was called with.    *! @endarray    *!    *! The current call frame will be last in the array.    *!    *! @note    *! Please note that the frame order may be reversed in a later version -  *! (than 7.1) of Pike to accommodate for deferred backtraces. +  *! of Pike to accommodate for deferred backtraces.    *!    *! Note that the arguments reported in the backtrace are the current    *! values of the variables, and not the ones that were at call-time.    *! This can be used to hide sensitive information from backtraces    *! (eg passwords).    *!    *! @seealso    *! @[catch()], @[throw()]    */   PMOD_EXPORT   PIKEFUN array(mixed) backtrace()    efun;    optflags OPT_EXTERNAL_DEPEND;   {    low_backtrace(& Pike_interpreter);   }    - /*! @module String -  */ -  - /*! @class Buffer -  *! A buffer, used for building strings. It's -  *! conceptually similar to a string, but you can only @[add] -  *! strings to it, and you can only @[get] the value from it once. -  *! -  *! There is a reason for those seemingly rather odd limitations, -  *! it makes it possible to do some optimizations that really speed -  *! things up. -  *! -  *! You do not need to use this class unless you add very many -  *! strings together, or very large strings. -  *! -  *! @example -  *! For the fastest possible operation, write your code like this: -  *! -  *! @code -  *! String.Buffer b = String.Buffer( ); -  *! -  *! function add = b->add; -  *! -  *! .. call add several times in code ... -  *! -  *! string result = b->get(); // also clears the buffer -  *! @endcode -  */ - PIKECLASS Buffer - { -  CVAR struct string_builder str; -  CVAR int initial; -  -  PIKEFUN int _size_object() -  { -  if( THIS->str.s ) -  RETURN THIS->str.malloced; -  RETURN 0; -  } -  -  void f_Buffer_get_copy( INT32 args ); -  void f_Buffer_get( INT32 args ); -  void f_Buffer_add( INT32 args ); -  -  /*! @decl void create(int initial_size) -  *! -  *! Initializes a new buffer. -  *! -  *! If no @[initial_size] is specified, 256 is used. If you -  *! know approximately how big the buffer will be, you can optimize -  *! the operation of @[add()] (slightly) by passing the size to this -  *! function. -  */ -  PIKEFUN void create( int|void size ) -  { -  struct Buffer_struct *str = THIS; -  if( size ) -  str->initial = MAXIMUM( size->u.integer, 512 ); -  else -  str->initial = 256; -  } -  -  /*! @decl string _sprintf( int flag, mapping flags ) -  *! It is possible to @[sprintf] a String.Buffer object -  *! as @tt{%s@} just as if it was a string. -  */ -  PIKEFUN string _sprintf( int flag, mapping flags ) -  { -  switch( flag ) -  { -  case 'O': -  { -  struct pike_string *res; -  struct Buffer_struct *str = THIS; -  push_text( "Buffer(%d /* %d */)" ); -  if( str->str.s ) -  { -  push_int(str->str.s->len); -  push_int(str->str.malloced); -  } -  else -  { -  push_int( 0 ); -  push_int( 0 ); -  } -  f_sprintf( 3 ); -  dmalloc_touch_svalue(Pike_sp-1); -  res = Pike_sp[-1].u.string; -  Pike_sp--; -  RETURN res; -  } -  -  case 's': -  { -  pop_n_elems( args ); -  if( Pike_fp->current_object->refs != 1 ) -  f_Buffer_get_copy( 0 ); -  else -  f_Buffer_get( 0 ); -  } -  return; -  -  case 't': -  RETURN make_shared_binary_string("Buffer",6); -  } -  pop_n_elems( args ); -  push_undefined(); -  } -  -  /*! @decl mixed cast( string type ) -  *! It is possible to cast a String.Buffer object to -  *! a @expr{string@} and an @expr{int@}. -  */ -  PIKEFUN mixed cast( string type ) -  { -  struct pike_string *string_t; -  struct pike_string *int_t; -  MAKE_CONST_STRING( string_t, "string" ); -  MAKE_CONST_STRING( int_t, "int" ); -  -  if( type == string_t ) -  { -  pop_n_elems( args ); -  if( Pike_fp->current_object->refs != 1 ) -  f_Buffer_get_copy( 0 ); -  else -  f_Buffer_get( 0 ); -  return; -  } -  -  if( type == int_t ) -  { -  struct Buffer_struct *str = THIS; -  pop_stack(); -  if( Pike_fp->current_object->refs != 1 ) -  f_Buffer_get_copy( 0 ); -  else -  f_Buffer_get( 0 ); -  o_cast_to_int( ); -  return; -  } -  Pike_error("Cannot cast to %S\n", type); -  } -  -  /*! @decl String.Buffer `+( string|String.Buffer what ) -  */ -  PIKEFUN object `+( string|Buffer what ) -  rawtype tFunc(tOr(tString, tObjIs_BUFFER), tObjIs_BUFFER); -  { -  struct Buffer_struct *str = THIS, *str2; -  struct object *res = fast_clone_object( Buffer_program ); -  str2 = OBJ2_BUFFER( res ); -  str2->initial = str->initial; -  if( str->str.s ) -  init_string_builder_copy (&str2->str, &str->str); -  apply( res, "add", 1 ); -  RETURN res; -  } -  -  /*! @decl String.Buffer `+=( string|String.Buffer what ) -  */ -  PIKEFUN object `+=( string|Buffer what ) -  rawtype tFunc(tOr(tString, tObjIs_BUFFER), tObjIs_BUFFER); -  { -  f_Buffer_add( 1 ); -  REF_RETURN Pike_fp->current_object; -  } -  -  /*! @decl int add(string|String.Buffer ... data) -  *! -  *! Adds @[data] to the buffer. -  *! -  *! @returns -  *! Returns the size of the buffer. -  *! -  *! @note -  *! Pike 7.8 and earlier did not support adding @[String.Buffer]s -  *! directly. -  *! -  *! @seealso -  *! @[addat()] -  */ -  PIKEFUN int add( string|Buffer ... arg1 ) -  rawtype tFuncV(tNone, tOr(tString, tObjIs_BUFFER), tIntPos); -  { -  struct Buffer_struct *str = THIS; -  int init_from_arg0 = 0, j; -  -  for (j=0; j < args; j++) { -  if (TYPEOF(Pike_sp[j-args]) == PIKE_T_STRING) { -  } else if ((TYPEOF(Pike_sp[j-args]) != PIKE_T_OBJECT) || -  (Pike_sp[j-args].u.object->prog != Buffer_program)) { -  SIMPLE_BAD_ARG_ERROR("add", j+1, "string|String.Buffer"); -  } -  } -  -  if (!str->str.s && args) { -  ptrdiff_t sum = 0; -  int shift = 0; -  for (j=0; j < args; j++) { -  struct pike_string *a; -  if (TYPEOF(Pike_sp[j-args]) == PIKE_T_STRING) { -  a = Pike_sp[j-args].u.string; -  } else { -  a = OBJ2_BUFFER(Pike_sp[j-args].u.object)->str.s; -  if (!a) continue; -  } -  sum += a->len; -  shift |= a->size_shift; -  } -  if (sum < str->initial) -  sum = str->initial; -  else if (sum > str->initial) -  sum <<= 1; -  shift = shift & ~(shift >> 1); -  -  if ((TYPEOF(Pike_sp[-args]) == PIKE_T_STRING) && -  (shift == Pike_sp[-args].u.string->size_shift) && -  init_string_builder_with_string (&str->str, Pike_sp[-args].u.string)) { -  mark_free_svalue (Pike_sp - args); -  if (sum > str->str.s->len) -  string_build_mkspace (&str->str, sum - str->str.s->len, shift); -  init_from_arg0 = 1; -  } -  else -  init_string_builder_alloc(&str->str, sum, shift); -  -  /* We know it will be a string that really is this wide. */ -  str->str.known_shift = shift; -  } -  -  for( j = init_from_arg0; j<args; j++ ) -  { -  struct pike_string *a; -  if (TYPEOF(Pike_sp[j-args]) == PIKE_T_STRING) { -  a = Pike_sp[j-args].u.string; -  } else { -  a = OBJ2_BUFFER(Pike_sp[j-args].u.object)->str.s; -  if (!a) continue; -  } -  string_builder_shared_strcat( &str->str, a ); -  } -  -  if (str->str.s) { -  RETURN str->str.s->len; -  } else { -  RETURN 0; -  } -  } -  -  /*! @decl int addat(int(0..) pos, string|String.Buffer ... data) -  *! -  *! Adds @[data] to the buffer, starting at position @[pos]. -  *! -  *! @returns -  *! Returns the size of the buffer. -  *! -  *! @note -  *! If the buffer isn't of the required size, it will be padded -  *! with NUL-characters. -  *! -  *! @note -  *! Pike 7.8 and earlier did not support adding @[String.Buffer]s -  *! directly. -  *! -  *! @seealso -  *! @[add()] -  */ -  PIKEFUN int addat(int(0..) pos, string ... arg1 ) -  rawtype tFuncV(tNone, tOr(tString, tObjIs_BUFFER), tIntPos); -  { -  struct Buffer_struct *str = THIS; -  -  if (pos < 0) -  SIMPLE_BAD_ARG_ERROR("addat", 1, "int(0..)"); -  -  if (args) { -  int init_from_arg0 = 0, j; -  ptrdiff_t sum = 0; -  int shift = 0; -  for (j=1; j < args; j++) { -  struct pike_string *a; -  if (TYPEOF(Pike_sp[j-args]) == PIKE_T_STRING) { -  a = Pike_sp[j-args].u.string; -  } else if ((TYPEOF(Pike_sp[j-args]) != PIKE_T_OBJECT) || -  (Pike_sp[j-args].u.object->prog != Buffer_program)) { -  SIMPLE_BAD_ARG_ERROR("addat", j+1, "string|String.Buffer"); -  } else { -  a = OBJ2_BUFFER(Pike_sp[j-args].u.object)->str.s; -  if (!a) continue; -  } -  sum += a->len; -  shift |= a->size_shift; -  } -  -  if (!str->str.s) { -  if ((sum + pos) <= str->initial) { -  sum = str->initial; -  } else { -  sum <<= 1; -  sum += pos; -  } -  shift = shift & ~(shift >> 1); -  -  init_string_builder_alloc(&str->str, sum, shift); -  } else { -  sum += pos; -  shift |= str->str.known_shift; -  shift = shift & ~(shift >> 1); -  if (sum > str->str.s->len) { -  string_build_mkspace(&str->str, sum - str->str.s->len, shift); -  } else if (shift != str->str.known_shift) { -  string_build_mkspace(&str->str, 0, shift); -  } -  } -  /* We know it will be a string that really is this wide. */ -  str->str.known_shift = shift; -  -  if (str->str.s->len < pos) { -  /* Clear the padding. */ -  MEMSET(str->str.s->str + (str->str.s->len << str->str.s->size_shift), -  0, (pos - str->str.s->len) << str->str.s->size_shift); -  } -  -  for(j = 1; j<args; j++) { -  struct pike_string *a; -  if (TYPEOF(Pike_sp[j-args]) == PIKE_T_STRING) { -  a = Pike_sp[j-args].u.string; -  } else { -  a = OBJ2_BUFFER(Pike_sp[j-args].u.object)->str.s; -  if (!a) continue; -  } -  pike_string_cpy(MKPCHARP_STR_OFF(str->str.s, pos), a); -  pos += a->len; -  } -  -  if (str->str.s->len < pos) { -  str->str.s->len = pos; -  /* Ensure NUL-termination */ -  str->str.s->str[str->str.s->len << str->str.s->size_shift] = 0; -  } -  } -  -  if (str->str.s) { -  RETURN str->str.s->len; -  } else { -  RETURN 0; -  } -  } -  -  /*! @decl void putchar(int c) -  *! Appends the character @[c] at the end of the string. -  */ -  PIKEFUN void putchar(int c) { -  struct Buffer_struct *str = THIS; -  if(!str->str.s) -  init_string_builder_alloc(&str->str, str->initial, 0); -  string_builder_putchar(&str->str, c); -  } -  -  /*! @decl int sprintf(strict_sprintf_format format, sprintf_args ... args) -  *! Appends the output from @[sprintf] at the end of the string. -  *! Returns the resulting size of the String.Buffer. -  */ -  PIKEFUN int sprintf(mixed ... arguments) -  rawtype tFuncV(tAttr("strict_sprintf_format", tOr(tStr, tObj)), -  tAttr("sprintf_args", tMix), tStr); -  -  { -  // FIXME: Reset length on exception? -  struct Buffer_struct *str = THIS; -  if(!str->str.s) -  init_string_builder_alloc(&str->str, str->initial, 0); -  low_f_sprintf(args, 0, &str->str); -  RETURN str->str.s->len; -  } -  -  /*! @decl string get_copy() -  *! -  *! Get the data from the buffer. Significantly slower than @[get], -  *! but does not clear the buffer. -  *! -  *! @seealso -  *! @[get()] -  */ -  PIKEFUN string get_copy() -  { -  struct pike_string *str = THIS->str.s; -  if( str ) -  { -  ptrdiff_t len = str->len; -  if( len > 0 ) -  { -  char *d = (char *)str->str; -  switch( str->size_shift ) -  { -  case 0: -  RETURN make_shared_binary_string0((p_wchar0 *)d,len); -  break; -  case 1: -  RETURN make_shared_binary_string1((p_wchar1 *)d,len); -  break; -  case 2: -  RETURN make_shared_binary_string2((p_wchar2 *)d,len); -  break; -  } -  } -  } -  push_empty_string(); -  return; -  } -  -  /*! @decl string get() -  *! -  *! Get the data from the buffer. -  *! -  *! @note -  *! This will clear the data in the buffer -  *! -  *! @seealso -  *! @[get_copy()], @[clear()] -  */ -  PIKEFUN string get( ) -  { -  struct Buffer_struct *str = THIS; -  if( str->str.s ) -  { -  struct pike_string *s = finish_string_builder( &str->str ); -  str->str.malloced = 0; -  str->str.s = NULL; -  RETURN s; -  } -  pop_n_elems(args); -  push_empty_string(); -  return; -  } -  -  /*! @decl void clear() -  *! -  *! Empty the buffer, and don't care about the old content. -  *! -  *! @note -  *! This function was not available in Pike 7.8 and earlier. -  *! -  *! @seealso -  *! @[get()] -  */ -  PIKEFUN void clear() -  { -  /* FIXME: Support resetting the initial size? */ -  struct Buffer_struct *str = THIS; -  if (str->str.s) { -  /* FIXME: There's also the alternative of using -  * reset_string_builder() here. -  */ -  free_string_builder(&str->str); -  str->str.s = NULL; -  } -  } -  -  /*! @decl int _sizeof() -  *! -  *! Returns the size of the buffer. -  */ -  PIKEFUN int _sizeof() -  { -  struct Buffer_struct *str = THIS; -  RETURN str->str.s ? str->str.s->len : 0; -  } -  -  INIT -  { -  struct Buffer_struct *str = THIS; -  MEMSET( str, 0, sizeof( *str ) ); -  } -  -  EXIT -  gc_trivial; -  { -  struct Buffer_struct *str = THIS; -  if( str->str.s ) -  free_string_builder( &str->str ); -  } -  -  GC_RECURSE -  { -  if (mc_count_bytes (Pike_fp->current_object) && THIS->str.s) -  mc_counted_bytes += THIS->str.malloced; -  } - } -  - /*! @endclass -  */ -  +    /*! @class Replace    *!    *! This is a "compiled" version of the @[replace] function applied on    *! a string, with more than one replace string. The replace strings    *! are given to the create method as a @i{from@} and @i{to@} array    *! and are then analyzed. The @expr{`()@} is then called with a    *! string and the replace rules in the Replace object will be    *! applied. The Replace object is used internally by the Pike    *! optimizer and need not be used manually.    */
pike.git/src/builtin.cmod:3477:    /* NOTE: from and to are only kept for _encode()'s use. */    PIKEVAR array from flags ID_PROTECTED;    PIKEVAR array to flags ID_PROTECTED;       PIKEFUN int _size_object()    {    int res = 0, i;    if( THIS->ctx.v )    {    struct svalue tmp; -  tmp.type = PIKE_T_STRING; +  SET_SVAL_TYPE(tmp, PIKE_T_STRING);    for( i=0; i<THIS->ctx.num; i++ )    {    res += sizeof(struct replace_many_tupel);    tmp.u.string = THIS->ctx.v[i].ind;    res += rec_size_svalue( &tmp, NULL );    tmp.u.string = THIS->ctx.v[i].val;    res += rec_size_svalue( &tmp, NULL );    }    }       RETURN res;    }    -  /*! @decl void create(array(string)|mapping(string:string)|void from, @ -  *! array(string)|string|void to) +  /*! @decl void create() +  *! @decl void create(mapping(string:string)) +  *! @decl void create(array(string) from, array(string)|string to)    */    PIKEFUN void create(array(string)|mapping(string:string)|void from_arg,    array(string)|string|void to_arg)    {    if (THIS->from) {    free_array(THIS->from);    THIS->from = NULL;    }    if (THIS->to) {    free_array(THIS->to);    THIS->to = NULL;    }    if (THIS->ctx.v)    free_replace_many_context(&THIS->ctx);    -  if (!args) { -  push_int(0); -  return; -  } -  if (from_arg && TYPEOF(*from_arg) == T_MAPPING) { +     if (to_arg) { -  Pike_error("Bad number of arguments to create().\n"); -  } -  THIS->from = mapping_indices(from_arg->u.mapping); -  THIS->to = mapping_values(from_arg->u.mapping); -  pop_n_elems(args); -  args = 0; -  } else { -  /* FIXME: Why is from declared |void, when it isn't allowed -  * to be void? -  * /grubba 2004-09-02 -  * -  * It probably has to do with the "if (!args)" above: It should -  * be possible to create an empty instance. /mast -  */ -  if (!from_arg || !to_arg) { -  Pike_error("Bad number of arguments to create().\n"); -  } -  pop_n_elems(args-2); -  args = 2; -  if (TYPEOF(*from_arg) != T_ARRAY) { -  SIMPLE_BAD_ARG_ERROR("Replace", 1, +  if (!from_arg || (TYPEOF(*from_arg) != T_ARRAY)) { +  SIMPLE_ARG_TYPE_ERROR("replace", 1,    "array(string)|mapping(string:string)");    }    if (TYPEOF(*to_arg) == T_STRING) {    push_int(from_arg->u.array->size);    stack_swap();    f_allocate(2); -  +  to_arg = Pike_sp - 1;    } -  if (TYPEOF(*to_arg) != T_ARRAY) { -  SIMPLE_BAD_ARG_ERROR("Replace", 2, "array(string)|string"); +  else if (TYPEOF(*to_arg) != T_ARRAY) { +  SIMPLE_ARG_TYPE_ERROR("replace", 2, "array(string)|string");    } -  if (from_arg->u.array->size != to_arg->u.array->size) { +  else if (from_arg->u.array->size != to_arg->u.array->size) {    Pike_error("Replace must have equal-sized from and to arrays.\n");    }    add_ref(THIS->from = from_arg->u.array);    add_ref(THIS->to = to_arg->u.array); -  +  } else if (from_arg) { +  if (TYPEOF(*from_arg) != T_MAPPING) +  Pike_error("Illegal arguments to create().\n"); +  THIS->from = mapping_indices(from_arg->u.mapping); +  THIS->to = mapping_values(from_arg->u.mapping); +  } else { +  pop_n_elems(args); +  return;    }       if (!THIS->from->size) {    /* Enter no-op mode. */    pop_n_elems(args); -  push_int(0); +     return;    }       if( (THIS->from->type_field & ~BIT_STRING) &&    (array_fix_type_field(THIS->from) & ~BIT_STRING) ) -  SIMPLE_BAD_ARG_ERROR("Replace", 1, +  SIMPLE_ARG_TYPE_ERROR("replace", 1,    "array(string)|mapping(string:string)");       if( (THIS->to->type_field & ~BIT_STRING) &&    (array_fix_type_field(THIS->to) & ~BIT_STRING) ) -  SIMPLE_BAD_ARG_ERROR("Replace", 2, "array(string)|string"); +  SIMPLE_ARG_TYPE_ERROR("replace", 2, "array(string)|string");       compile_replace_many(&THIS->ctx, THIS->from, THIS->to, 1); -  +     pop_n_elems(args); -  push_int(0); +     }       /*! @decl string `()(string str)    */    PIKEFUN string `()(string str)    {    if (!THIS->ctx.v) {    /* The result is already on the stack in the correct place... */    return;    }
pike.git/src/builtin.cmod:3608:    push_undefined();    }    f_aggregate(2);    }       /*! @decl void _decode(array(array(string)) encoded)    */    PIKEFUN void _decode(array(array(string)) encoded)    {    INT32 i; -  for (i=0; i < encoded->size; i++) { +  for (i=0; i < encoded->size; i++)    push_svalue(encoded->item + i); -  stack_swap(); -  } -  pop_stack(); +        f_multi_string_replace_create(i);    }    -  + #ifdef PIKE_NULL_IS_SPECIAL    INIT    { -  MEMSET(&THIS->ctx, 0, sizeof(struct replace_many_context)); +  memset(&THIS->ctx, 0, sizeof(struct replace_many_context));    } -  + #endif       EXIT    gc_trivial;    {    free_replace_many_context(&THIS->ctx);    }   }      /*! @endclass    */
pike.git/src/builtin.cmod:3650:    *! not be used manually.    */   PIKECLASS single_string_replace   {    CVAR SearchMojt mojt;    PIKEVAR string del flags ID_PROTECTED|ID_PRIVATE;    PIKEVAR string to flags ID_PROTECTED|ID_PRIVATE;       EXTRA    { -  MAP_VARIABLE ("o", tObj, ID_PROTECTED|ID_PRIVATE, +  PIKE_MAP_VARIABLE ("o",    single_string_replace_storage_offset +    OFFSETOF (single_string_replace_struct, mojt.container), -  T_OBJECT); +  tObj, T_OBJECT, ID_PROTECTED|ID_PRIVATE);    }       /*! @decl void create(string|void from, string|void to)    *!    *! @note    *! May be called with either zero or two arguments.    */    PIKEFUN void create(string|void del, string|void to)    {    if (THIS->del) {    free_string(THIS->del);    THIS->del = NULL;    }    if (THIS->to) {    free_string(THIS->to);    THIS->to = NULL;    }    -  if (!del) return; +  if (!del) { +  pop_n_elems(args); +  return; +  }       if (!to) { -  SIMPLE_BAD_ARG_ERROR("String.SingleReplace->create", 2, "string"); +  SIMPLE_ARG_TYPE_ERROR("replace", 2, "string");    }       if (del == to) {    /* No-op... */ -  +  pop_n_elems(args);    return;    }       copy_shared_string(THIS->del, del);    copy_shared_string(THIS->to, to);       if (del->len) {    THIS->mojt = simple_compile_memsearcher(del);    } -  +  pop_n_elems(args);    }       /*** replace function ***/    typedef char *(* replace_searchfunc)(void *,void *,size_t);       /*! @decl string `()(string str)    */    PIKEFUN string `()(string str)    {    int shift;
pike.git/src/builtin.cmod:3731:    } else {    char *s, *end, *tmp;    replace_searchfunc f = (replace_searchfunc)0;    void *mojt_data = THIS->mojt.data;    PCHARP r;       end = str->str+(str->len<<str->size_shift);       switch(str->size_shift)    { -  case 0: f = (replace_searchfunc)THIS->mojt.vtab->func0; break; -  case 1: f = (replace_searchfunc)THIS->mojt.vtab->func1; break; -  case 2: f = (replace_searchfunc)THIS->mojt.vtab->func2; break; - #ifdef PIKE_DEBUG -  default: Pike_fatal("Illegal shift.\n"); - #endif +  case eightbit: f = (replace_searchfunc)THIS->mojt.vtab->func0; break; +  case sixteenbit: f = (replace_searchfunc)THIS->mojt.vtab->func1; break; +  case thirtytwobit: f = (replace_searchfunc)THIS->mojt.vtab->func2; break; +  default: UNREACHABLE(break);    }       if(del->len == to->len)    {    ret = begin_wide_shared_string(str->len, shift);    } else {    INT32 delimiters = 0;       s = str->str;   
pike.git/src/builtin.cmod:3803:    }       /*! @decl void _decode(array(string)|int(0..0) encoded)    */    PIKEFUN void _decode(array(string)|int(0..0) encoded_)    {    INT32 i = 0;    if (TYPEOF(*encoded_) == PIKE_T_ARRAY) {    struct array *encoded = encoded_->u.array;    -  for (i=0; i < encoded->size; i++) { +  for (i=0; i < encoded->size; i++)    push_svalue(encoded->item + i); -  stack_swap(); +     } -  } -  pop_stack(); +        f_single_string_replace_create(i);    }   }      /*! @endclass    */      /*! @class Bootstring    *!
pike.git/src/builtin.cmod:3920:    i %= output.s->len+1;    string_builder_putchar( &output, n );    if (i != output.s->len-1)    switch (output.s->size_shift) {    case 0:    {    p_wchar0 *s = STR0(output.s);    INT_TYPE p = output.s->len;    while (--p>i)    s[p] = s[p-1]; -  s[p] = DO_NOT_WARN ((p_wchar0) n); +  s[p] = (p_wchar0) n;    }    break;    case 1:    {    p_wchar1 *s = STR1(output.s);    INT_TYPE p = output.s->len;    while (--p>i)    s[p] = s[p-1]; -  s[p] = DO_NOT_WARN ((p_wchar1) n); +  s[p] = (p_wchar1) n;    }    break;    case 2:    {    p_wchar2 *s = STR2(output.s);    INT_TYPE p = output.s->len;    while (--p>i)    s[p] = s[p-1]; -  s[p] = DO_NOT_WARN ((p_wchar2) n); +  s[p] = (p_wchar2) n;    }    break; - #ifdef PIKE_DEBUG -  default: -  Pike_fatal("Illegal shift size!\n"); - #endif +     }    i++;    }       RETURN finish_string_builder( &output );    }       /*! @decl string encode(string s)    *!    *! Encodes a string using Bootstring encoding into a string constisting
pike.git/src/builtin.cmod:4272:    *! Array that @[__automap__()] is to loop over.    *!    *! @param depth    *! Recursion depth of @[arr] where the loop will be.    */    PIKEFUN void create(array a, int d)    {    if(THIS->arg) free_array(THIS->arg);    add_ref(THIS->arg=a);    THIS->depth=d; +  pop_n_elems(2);    }       PIKEFUN string _sprintf(int mode, mapping flags)    {    pop_n_elems(args);    if (mode != 'O') {    push_undefined ();    return;    } -  push_text("%O%*'[*]'n"); +  push_static_text("%O%*'[*]'n");    if(THIS->arg)    ref_push_array(THIS->arg);    else    push_int(0);    push_int(THIS->depth*3);    f_sprintf(3);    }   }      /*! @endclass
pike.git/src/builtin.cmod:4316:    TYPE_FIELD types;       for(e=0;e<args;e++)    {    if(TYPEOF(real_args[e]) == T_OBJECT &&    real_args[e].u.object->prog == automap_marker_program &&    OBJ2_AUTOMAP_MARKER(real_args[e].u.object)->depth >= d)    {    if(TYPEOF(tmpargs[e]) != T_ARRAY)    index_error("__automap__", -  Pike_sp-args, +     args,    tmpargs,    NULL,    "Automap on non-array.\n");    tmp=tmpargs[e].u.array->size;    if(tmp < size)    size=tmp;    }    }   
pike.git/src/builtin.cmod:4449:    *!    *! This is the function returned by @[_get_setter()].    */    PIKEFUN void `()(mixed val)    flags ID_PROTECTED;    {    if (!THIS->o) {    Pike_error("Uninitialized Setter!\n");    }    object_low_set_index(THIS->o, THIS->f, Pike_sp-1); -  pop_n_elems(args); -  push_int(0); +     } -  +     PIKEFUN string _sprintf(int c, mapping|void opts)    flags ID_PROTECTED;    {    struct program *prog;    if (!THIS->o) { -  push_constant_text("Setter()"); +  push_static_text("Setter()");    } else if ((prog = THIS->o->prog)) { -  push_constant_text("%O->`%s="); +  push_static_text("%O->`%s=");    ref_push_object(THIS->o);    ref_push_string(ID_FROM_INT(prog, THIS->f)->name);    f_sprintf(3);    } else { -  push_constant_text("Setter(destructed object)"); +  push_static_text("Setter(destructed object)");    }    stack_pop_n_elems_keep_top(args);    }   }      /*! @endclass    */      PMOD_EXPORT struct object *get_setter(struct object *o, int f)   {
pike.git/src/builtin.cmod:4560:    flags ID_PROTECTED;    {    RETURN 1;    }       PIKEFUN string _sprintf(int fmt, mixed ... extras)    flags ID_PROTECTED;    {    pop_n_elems(args);    if (fmt == 'O') { -  push_constant_text("Val.null"); +  push_static_text("Val.null");    } else {    push_undefined();    }    }       PIKEFUN int __hash()    flags ID_PROTECTED;    { -  pop_n_elems(args); -  push_int(17); +  RETURN 17;    }       PIKEFUN int `==(mixed other)    flags ID_PROTECTED;    { -  if (TYPEOF(*other) != T_OBJECT) { -  pop_stack(); -  push_int(0); -  return; -  } +  if (TYPEOF(*other) != T_OBJECT) +  RETURN 0;       /* Look for the is_val_null constant directly in the program of    * other, without going through its `[]. When this is called in a    * codec, other can be a completely arbitrary object which may not    * have a `[] that works in that context. */    push_int (0);    ref_push_program (other->u.object->prog);    push_constant_text("is_val_null");    if (program_index_no_free (Pike_sp - 3, Pike_sp - 2, Pike_sp - 1) &&    TYPEOF(Pike_sp[-3]) == T_INT && Pike_sp[-3].u.integer) {
pike.git/src/builtin.cmod:4607:    }    }       /*! @decl string encode_json()    *!    *! Defined for use with @[Standards.JSON.encode], so that it    *! formats NULL as @expr{null@}.    */    PIKEFUN string encode_json(...)    { +  pop_n_elems(args);    push_constant_text ("null");    }   }      /*! @endclass    */    -  + /*! @class DestructImmediate +  *! An empty class that can be intherited to get the PROGRAM_DESTRUCT_IMMEDIATE +  *! flag set. +  */ +  + PIKECLASS DestructImmediate +  program_flags PROGRAM_DESTRUCT_IMMEDIATE; + { + } +  + /*! @endclass +  */ +    PMOD_EXPORT   PIKEFUN int levenshtein_distance(string a, string b)   {    int i, j, n, *lev_i, *lev_p;       /* Simple cases: strings are equal or one of them is empty: */    if (a == b) RETURN 0;    if (a->len == 0) RETURN b->len;    if (b->len == 0) RETURN a->len;   
pike.git/src/builtin.cmod:4670:    *!    *! The base class for serializable objects.    *!    *! Inherit this class in classes that need to be serializable.    *!    *! @seealso    *! @[Serializer.serialize()], @[Serializer.deserialize()]    */   PIKECLASS Serializable   { -  /* Loop over all variables, and call fun_num in the current object. */ +  /* Loop over all true variables, and call fun_num in the current object. */    static void low_serialize(int i, struct svalue *fun,    int use_setter, int fun_num)    {    struct inherit *inh;    struct program *p = Pike_fp->current_object->prog;    struct svalue *save_sp = Pike_sp;       inh = p->inherits + i;    p = inh->prog;       for (i = 0; i < p->num_identifier_references; i++) {    struct reference *ref = PTR_FROM_INT(p, i);    struct identifier *id;    if ((ref->id_flags & ID_HIDDEN) ||    ((ref->id_flags & (ID_PRIVATE|ID_INHERITED)) ==    (ID_PRIVATE|ID_INHERITED))) {    continue;    }    id = ID_FROM_PTR(p, ref); -  if (!IDENTIFIER_IS_VARIABLE(id->identifier_flags)) { +  if (!IDENTIFIER_IS_VARIABLE(id->identifier_flags) || +  (id->run_time_type == PIKE_T_GET_SET)) {    continue;    }    push_svalue(fun);    if (use_setter) {    push_function(get_setter(Pike_fp->current_object,    i + inh->identifier_level),    f_Setter_cq__backtick_28_29_fun_num);    } else {    low_object_index_no_free(Pike_sp, Pike_fp->current_object,    i + inh->identifier_level);
pike.git/src/builtin.cmod:4752:    *! @[_serialize()], @[_deserialize_variable()]    */    PIKEFUN void _serialize_variable(function(mixed, string, type:void)    serializer, mixed value,    string symbol, type symbol_type)    flags ID_PROTECTED;    rawtype tFunc(tFunc(tMix tStr tType(tMix), tVoid)    tMix tStr tType(tMix), tVoid);    {    f_call_function(args); -  pop_stack(); -  push_int(0); +     }       /*! @decl protected void _serialize(object o, @    *! function(mixed, string, type:void) serializer)    *!    *! Dispatch function for serialization.    *!    *! @param o    *! Object to serialize. Always a context of the current object.    *!
pike.git/src/builtin.cmod:4797:    *! @seealso    *! @[Serializer.serialize()], @[_serialize_variable()],    *! @[_deserialize()]    */    PIKEFUN void _serialize(object o,    function(mixed, string, type:void) serializer)    flags ID_PROTECTED;    rawtype tFunc(tObj tFunc(tMix tStr tType(tMix), tVoid), tVoid);    {    if (o != Pike_fp->current_object) { -  SIMPLE_BAD_ARG_ERROR("_serialize", 1, "this"); +  SIMPLE_ARG_TYPE_ERROR("_serialize", 1, "this");    }    low_serialize(SUBTYPEOF(Pike_sp[-args]), serializer, 0,    f_Serializable_cq__serialize_variable_fun_num); -  pop_n_elems(args); -  push_int(0); +     }       static void *find_program_from_object_type_cb(struct pike_type *t)    {    struct program *p;    if ((t->type != PIKE_T_OBJECT) || !t->cdr) return NULL;    p = id_to_program(CDR_TO_INT(t));    if (!p || (p->flags & PROGRAM_NEEDS_PARENT) ||    (low_find_lfun(p, LFUN__DESERIALIZE) == -1)) return NULL;    return p;
pike.git/src/builtin.cmod:4897:    struct object *o = clone_object(p, 0);    push_object(o); /* Protection against errors and arg to deserialize. */    ref_push_object(o);    apply_svalue(setter, 1);    pop_stack();    push_svalue(deserializer);    f_deserialize(2);    return;    }    f_call_function(args); -  pop_stack(); -  push_int(0); +     }       /*! @decl protected void _deserialize(object o, @    *! function(function(mixed:void), @    *! string, type: void) deserializer)    *!    *! Dispatch function for deserialization.    *!    *! @param o    *! Object to serialize. Always a context of the current object.
pike.git/src/builtin.cmod:4944:    *! @[Serializer.deserialize()], @[_deserialize_variable()],    *! @[_serialize()], @[Builtin.Setter]    */    PIKEFUN void _deserialize(object o,    function(function(mixed:void),    string, type: void) deserializer)    flags ID_PROTECTED;    rawtype tFunc(tObj tFunc(tFunc(tMix, tVoid) tStr tType(tMix), tVoid), tVoid);    {    if (o != Pike_fp->current_object) { -  SIMPLE_BAD_ARG_ERROR("_serialize", 1, "this"); +  SIMPLE_ARG_TYPE_ERROR("_serialize", 1, "this");    }    low_serialize(SUBTYPEOF(Pike_sp[-args]), deserializer, 1,    f_Serializable_cq__deserialize_variable_fun_num); -  pop_n_elems(args); -  push_int(0); +     }   }   /*! @endclass    */      /*! @decl void serialize(object o, @    *! function(mixed, string, type:void) serializer)    *!    *! Call @[lfun::_serialize()] in @[o].    *!
pike.git/src/builtin.cmod:5034:    node->next = node->prev = NULL;    node->refs = 1;    SET_SVAL(node->val, T_INT, NUMBER_UNDEFINED, integer, 0);    return node;   }      void count_memory_in_pike_list_nodes(size_t * n, size_t * s) {    ba_count_all(&pike_list_node_allocator, n, s);   }    - void free_all_pike_list_node_blocks() { + void free_all_pike_list_node_blocks(void) {    ba_destroy(&pike_list_node_allocator);   }      PMOD_EXPORT void free_list_node(struct pike_list_node *node)   {    if (!sub_ref(node)) {    if (node->prev) {    free_list_node(node->prev);    }    if (node->next) {
pike.git/src/builtin.cmod:5328:    *!    *! Describe the list.    *!    *! @seealso    *! @[sprintf()], @[lfun::_sprintf()]    */    PIKEFUN string _sprintf(int c, mapping(string:mixed)|void attr)    flags ID_PROTECTED;    {    if (!THIS->num_elems) { -  push_constant_text("ADT.List(/* empty */)"); +  push_static_text("ADT.List(/* empty */)");    } else if (c == 'O') {    struct pike_list_node *node = THIS->head;    if (THIS->num_elems == 1) { -  push_constant_text("ADT.List(/* 1 element */\n"); +  push_static_text("ADT.List(/* 1 element */\n");    } else { -  push_constant_text("ADT.List(/* %d elements */\n"); +  push_static_text("ADT.List(/* %d elements */\n");    push_int(THIS->num_elems);    f_sprintf(2);    }    while (node->next) {    if (node->next->next) { -  push_constant_text(" %O,\n"); +  push_static_text(" %O,\n");    } else { -  push_constant_text(" %O\n"); +  push_static_text(" %O\n");    }    push_svalue(&node->val);    f_sprintf(2);    node = node->next;    } -  push_constant_text(")"); +  push_static_text(")");    f_add(THIS->num_elems + 2);    } else {    if (THIS->num_elems == 1) { -  push_constant_text("ADT.List(/* 1 element */)"); +  push_static_text("ADT.List(/* 1 element */)");    } else { -  push_constant_text("ADT.List(/* %d elements */)"); +  push_static_text("ADT.List(/* %d elements */)");    push_int(THIS->num_elems);    f_sprintf(2);    }    }    stack_pop_n_elems_keep_top(args);    }       /*! @decl mixed head()    *!    *! Get the element at the head of the list.
pike.git/src/builtin.cmod:5472:    if (THIS->num_elems) {    struct pike_list_node *node = THIS->head;    int i;    for (i = 0; i < THIS->num_elems; i++) {    assign_svalue_no_free(ITEM(a) + i, &node->val);    node = node->next;    }    }    }    -  /*! @decl mixed cast(string type) +  /*! @decl array cast(string type)    *! -  *! Cast the lists. @expr{array@} and @expr{object@} are the only -  *! supported types. +  *! Cast the lists. @expr{array@} is the only +  *! supported type.    */ -  PIKEFUN mixed cast(string type) +  PIKEFUN array cast(string type)    flags ID_PROTECTED;    { -  if (type == MK_STRING("array")) { -  pop_n_elems(args); +  pop_stack(); /* type as at least one more reference. */ +  if (type == literal_array_string)    apply_current(f_List_cq__values_fun_num, 0); -  } else if (type == MK_STRING("object")) { -  pop_n_elems(args); -  ref_push_object(Pike_fp->current_object); -  } else { -  Pike_error("Cannot cast to %o.\n", Pike_sp-1); +  else +  push_undefined();    } -  } +           /*! @decl mixed `[](mixed key) */    PIKEFUN mixed `[](mixed key)    flags ID_PROTECTED;    {    struct pike_list_node *node;    INT_TYPE n; -  if (TYPEOF(*key) != PIKE_T_INT) SIMPLE_BAD_ARG_ERROR("`[]", 1, "int"); +  if (TYPEOF(*key) != PIKE_T_INT) SIMPLE_ARG_TYPE_ERROR("`[]", 1, "int");       n = key->u.integer;    if (n < 0) n = -(n+1);       if (n >= THIS->num_elems) Pike_error("out of bounds");       if (n >= THIS->num_elems >> 1) { /* use shorter direction */    n = THIS->num_elems - n - 1;    key->u.integer = - key->u.integer - 1;    }
pike.git/src/builtin.cmod:5539:    PIKEFUN void append(mixed ... values)    {    struct pike_list_node *node = TAIL_SENTINEL(THIS);    while (args--) {    struct pike_list_node *new_node = alloc_pike_list_node();    new_node->val = *(--Pike_sp);    prepend_list_node(node, new_node);    free_list_node(node = new_node);    THIS->num_elems++;    } -  push_int(0); +     }       /*! @decl void insert(mixed ... values)    *!    *! Insert @[values] at the front of the list.    *!    *! @seealso    *! @[append()]    */    PIKEFUN void insert(mixed ... values)    {    struct pike_list_node *node = THIS->head;    while (args--) {    struct pike_list_node *new_node = alloc_pike_list_node();    new_node->val = *(--Pike_sp);    prepend_list_node(node, new_node);    free_list_node(node = new_node);    THIS->num_elems++;    } -  push_int(0); +     }       /*! @decl void flush()    *!    *! Empties the List.    */    PIKEFUN void flush() {    if (THIS->num_elems) {    while (THIS->head->next) {    if (THIS->head->refs == 2)
pike.git/src/builtin.cmod:5751:   #endif /* PIKE_DEBUG */    /* Detached node. */    gc_recurse_svalues(&THIS->cur->val, 1);    gc_recurse_list_node_tree_forward(THIS->cur->next, THIS->cur->prev);    gc_recurse_list_node_tree_backward(THIS->cur->next, THIS->cur->prev);    }       PIKEFUN int(0..1) `!()    flags ID_PROTECTED;    { -  pop_n_elems(args); -  push_int(!THIS->cur->next || !THIS->cur->prev); +  RETURN (!THIS->cur->next || !THIS->cur->prev);    }       PIKEFUN int(0..) index()    {    pop_n_elems(args);    if (THIS->cur->next && THIS->cur->prev) {    push_int(THIS->ind);    } else {    push_undefined();    }
pike.git/src/builtin.cmod:5831:    *! @seealso    *! @[prev()]    */    PIKEFUN int(0..1) next()    {    struct pike_list_node *next;    if ((next = THIS->cur->next)) {    free_list_node(THIS->cur);    add_ref(THIS->cur = next);    THIS->ind++; -  if (next->next) { -  pop_n_elems(args); -  push_int(1); -  return; +  if (next->next) +  RETURN 1;    } -  +  RETURN 0;    } -  pop_n_elems(args); -  push_int(0); -  } +        /*! @decl int(0..1) prev()    *!    *! Retrace to the previous element in the list.    *!    *! @returns    *! Returns @expr{1@} on success, and @expr{0@} (zero)    *! at the beginning of the list.    *!    *! @seealso    *! @[next()]    */    PIKEFUN int(0..1) prev()    {    struct pike_list_node *prev;    if ((prev = THIS->cur->prev)) {    free_list_node(THIS->cur);    add_ref(THIS->cur = prev);    THIS->ind--; -  if (prev->prev) { -  pop_n_elems(args); -  push_int(1); -  return; +  if (prev->prev) +  RETURN 1;    } -  +  RETURN 0;    } -  pop_n_elems(args); -  push_int(0); -  } +        /*! @decl Iterator `+=(int steps)    *!    *! Advance or retrace the specified number of @[steps].    *!    *! @seealso    *! @[next()], @[prev]    */    PIKEFUN Iterator `+=(int steps)    {
pike.git/src/builtin.cmod:5913:    struct pike_list_node *new_node;    if (!THIS->cur->prev) {    Pike_error("Attempt to insert before the start sentinel.\n");    }    new_node = alloc_pike_list_node();    assign_svalue_no_free(&new_node->val, val);    prepend_list_node(THIS->cur, new_node);    free_list_node(THIS->cur);    THIS->cur = new_node;    List__get_iterator_find_parent()->num_elems++; -  pop_n_elems(args); -  push_int(0); +     }       /*! @decl void append(mixed val)    *!    *! Append @[val] after the current position.    *!    *! @seealso    *! @[insert()], @[delete()], @[set()]    */    PIKEFUN void append(mixed val)    {    struct pike_list_node *new_node;    if (!THIS->cur->next) {    Pike_error("Attempt to append after the end sentinel.\n");    }    new_node = alloc_pike_list_node();    assign_svalue_no_free(&new_node->val, val);    append_list_node(THIS->cur, new_node);    free_list_node(new_node);    List__get_iterator_find_parent()->num_elems++; -  pop_n_elems(args); -  push_int(0); +     }       /*! @decl void delete()    *!    *! Delete the current node.    *!    *! The current position will advance to the next node.    *! This function thus performes the reverse operation    *! of @[insert()].    *!
pike.git/src/builtin.cmod:5968:    if (THIS->cur->refs == 3) {    unlink_list_node(THIS->cur);    } else {    /* There's some other iterator holding references to this node. */    detach_list_node(THIS->cur);    }    List__get_iterator_find_parent()->num_elems--;    }    free_list_node(THIS->cur);    THIS->cur = next; -  pop_n_elems(args); -  push_int(0); +     }       /*! @decl void set(mixed val)    *!    *! Set the value of the current position to @[val].    *!    *! @seealso    *! @[insert()], @[append()], @[delete()]    */    PIKEFUN void set(mixed val)    {    if (!THIS->cur->next || !THIS->cur->prev) {    Pike_error("Attempt to set a sentinel.\n");    }    assign_svalue(&THIS->cur->val, val); -  pop_n_elems(args); -  push_int(0); +     }    }    /*! @endclass    */   }   /*! @endclass    */      /*! @endmodule    */    -  + /*! @module __builtin +  */ +  + /*! @class Stack +  *! This class implements a simple stack. Instead of adding and removing +  *! elements to an array, and thus making it vary in size for every push +  *! and pop operation, this stack tries to keep the stack size constant. +  *! If however the stack risks to overflow, it will allocate double its +  *! current size, i.e. pushing an element on an full 32 slot stack will +  *! result in a 64 slot stack with 33 elements. +  *! +  *! @note +  *! This class is usually accessed as @[ADT.LowLevelStack]. +  */ + PIKECLASS Stack + { +  PIKEVAR array arr; +  +  /*! @decl void push(mixed val) +  *! Push an element on the top of the stack. +  */ +  PIKEFUN void push(mixed val) +  { +  if (!THIS->arr) { +  THIS->arr = real_allocate_array(1, 31); +  array_set_index_no_free(THIS->arr, 0, val); +  } else { +  THIS->arr = array_insert(THIS->arr, val, THIS->arr->size); +  } +  push_int(0); +  } +  +  /*! @decl mixed top() +  *! Returns the top element from the stack, without +  *! popping it. +  *! @throws +  *! Throws an error if called on an empty stack. +  */ +  PIKEFUN mixed top() +  { +  if (THIS->arr && THIS->arr->size) { +  push_svalue(ITEM(THIS->arr) + THIS->arr->size - 1); +  return; +  } +  Pike_error("Stack underflow\n"); +  } +  +  /*! @decl mixed pop(void|int val) +  *! Pops and returns entry @[val] from the stack, counting +  *! from the top. If no value is given the top element is +  *! popped and returned. All popped entries are freed from +  *! the stack. +  */ +  PIKEFUN mixed pop(void|int val) +  { +  if (!THIS->arr || !THIS->arr->size) { +  Pike_error("Stack underflow\n"); +  } +  +  if (val && (val->u.integer > 0)) { +  ptrdiff_t new_size; +  ptrdiff_t old_size; +  +  new_size = THIS->arr->size - val->u.integer; +  if (new_size < 0) new_size = 0; +  +  /* NB: Steal reference from the array element. */ +  *Pike_sp = ITEM(THIS->arr)[new_size]; +  old_size = THIS->arr->size; +  THIS->arr->size = new_size; +  Pike_sp++; +  +  free_svalues(ITEM(THIS->arr) + new_size + 1, old_size - (new_size + 1), +  THIS->arr->type_field); +  } else { +  /* NB: Steal reference from the array element. */ +  THIS->arr->size--; +  *Pike_sp = ITEM(THIS->arr)[THIS->arr->size]; +  Pike_sp++; +  } +  } +  +  /*! @decl void quick_pop(void|int val) +  *! Pops @[val] entries from the stack, or one entry +  *! if no value is given. The popped entries are not +  *! returned. +  */ +  PIKEFUN void quick_pop(void|int val) +  { +  apply_current(f_Stack_pop_fun_num, args); +  push_int(0); +  } +  +  /*! @decl void reset(int|void initial_size) +  *! Empties the stack, resets the stack pointer +  *! and shrinks the stack size to the given value +  *! or 32 if none is given. +  *! @seealso +  *! @[create] +  */ +  PIKEFUN void reset(int|void initial_size) +  { +  ptrdiff_t size = 32; +  if (initial_size && (initial_size->u.integer > 0)) { +  size = initial_size->u.integer; +  } +  +  if (THIS->arr) { +  free_array(THIS->arr); +  THIS->arr = NULL; +  } +  +  /* NB: The initial size MUST != 0 else the empty array will be returned. */ +  THIS->arr = real_allocate_array(1, size-1); +  THIS->arr->size = 0; +  } +  +  /*! @decl void create(int|void initial_size) +  *! An initial stack size can be given when +  *! a stack is cloned. The default value is +  *! 32. +  */ +  PIKEFUN void create(int|void initial_size) +  { +  apply_current(f_Stack_reset_fun_num, args); +  } +  +  /*! @decl void set_stack(array stack) +  *! Sets the stacks content to the provided array. +  */ +  PIKEFUN void set_stack(array stack) +  { +  if (THIS->arr) { +  free_array(THIS->arr); +  } +  add_ref(THIS->arr = stack); +  } +  +  /*! @decl int _sizeof() +  *! @[sizeof] on a stack returns the number of entries +  *! in the stack. +  */ +  PIKEFUN int _sizeof() +  flags ID_PROTECTED; +  { +  push_int(THIS->arr ? THIS->arr->size : 0); +  } +  +  /*! @decl array _values() +  *! @[values] on a stack returns all the entries in +  *! the stack, in order. +  */ +  PIKEFUN array _values() +  flags ID_PROTECTED; +  { +  if (THIS->arr) { +  push_array(copy_array(THIS->arr)); +  } else { +  ref_push_array(&empty_array); +  } +  } +  +  /*! @decl int _search(mixed item) +  *! Return the stack-depth to @[item]. +  *! +  *! This function makes it possible to use +  *! eg @[search()] and @[has_value()] on the stack. +  */ +  PIKEFUN int _search(mixed item) +  flags ID_PROTECTED; +  { +  if (!THIS->arr || !THIS->arr->size) { +  push_int(-1); +  } else { +  push_int(array_search(THIS->arr, item, 0)); +  } +  } +  +  /*! @decl this_program `+(this_program s) +  *! A stack added with another stack yields a new +  *! stack with all the elements from both stacks, +  *! and the elements from the second stack at the +  *! top of the new stack. +  */ +  PIKEFUN Stack `+(Stack s) +  flags ID_PROTECTED; +  { +  struct Stack_struct *other_st = get_storage(s, Stack_program); +  int cnt = 0; +  if (!other_st) { +  SIMPLE_BAD_ARG_ERROR("`+", 1, "Stack"); +  } +  if (THIS->arr) { +  ref_push_array(THIS->arr); +  cnt++; +  } +  if (other_st->arr) { +  ref_push_array(other_st->arr); +  cnt++; +  } +  if (cnt == 1) { +  ref_push_array(&empty_array); +  } +  if (cnt) { +  f_add(2); + #ifdef PIKE_DEBUG +  if (TYPEOF(Pike_sp[-1]) != PIKE_T_ARRAY) { +  Pike_fatal("Stack addition failure.\n"); +  } + #endif +  push_object(s = fast_clone_object(Stack_program)); +  other_st = get_storage(s, Stack_program); + #ifdef PIKE_DEBUG +  if (!other_st) { +  Pike_fatal("Stack lost storage.\n"); +  } + #endif +  add_ref(other_st->arr = Pike_sp[-2].u.array); +  return; +  } +  push_object(clone_object(Stack_program, 0)); +  } +  +  PIKEFUN mixed cast(string to) +  flags ID_PROTECTED; +  { +  if( to == MK_STRING("array") ) { +  apply_current(f_Stack_cq__values_fun_num, 0); +  } else { +  push_undefined(); +  } +  } +  +  PIKEFUN string _sprintf(int t) +  flags ID_PROTECTED; +  { +  if (t != '0') { +  push_undefined(); +  } +  +  push_constant_text("%O%O"); +  ref_push_program(Pike_fp->current_program); +  apply_current(f_Stack_cq__values_fun_num, 0); +  f_sprintf(3); +  } + } + /*! @endclass +  */ +  + /*! @endmodule +  */ +    /*! @module Pike    */      /*! @class MasterCodec    *!    *! This is a bare-bones codec that is used when loading a dumped master.    *!    *! @seealso    *! @[Codec]    */
pike.git/src/builtin.cmod:6056:   }      /*! @endclass    */      /*! @endmodule    */      static struct object *val_module;    - static void get_val_module() + static void get_val_module(void)   {    assert (!val_module); -  push_constant_text ("Val"); +  push_static_text ("Val");    APPLY_MASTER ("resolv", 1);    if (TYPEOF(Pike_sp[-1]) != T_OBJECT)    Pike_error ("\"Val\" didn't resolve to a module object.\n");    val_module = (--Pike_sp)->u.object;   }      /* Always do the lookup in the Val module dynamically to allow the    * values to be replaced. */   #define GET_VAL(NAME) \    PMOD_EXPORT struct object *PIKE_CONCAT (get_val_, NAME) (void) \
pike.git/src/builtin.cmod:6099:    return Null_program;   }      PIKECLASS __Backtrace_Tester__   {    INIT {    Pike_error("__Backtrace_Tester__\n");    }   }    + #undef DEFAULT_CMOD_STORAGE + #define DEFAULT_CMOD_STORAGE static +  + PIKECLASS pike_trampoline + { +  CVAR struct pike_trampoline trampoline; +  +  DECLARE_STORAGE; +  + #ifdef PIKE_NULL_IS_SPECIAL +  INIT +  { +  THIS->trampoline.frame=0; +  } + #endif +  +  EXIT +  { +  if(THIS->trampoline.frame) +  { +  free_pike_scope(THIS->trampoline.frame); +  THIS->trampoline.frame=0; +  } +  } +  +  static void gc_check_frame(struct pike_frame *f) +  { +  if(f->flags & PIKE_FRAME_MALLOCED_LOCALS) +  { +  if(f->current_object) +  debug_gc_check (f->current_object, " as current_object in trampoline frame"); +  if(f->current_program) +  debug_gc_check (f->current_program, " as current_program in trampoline frame"); +  debug_gc_check_svalues (f->locals, f->num_locals, " in locals of trampoline frame"); +  if(f->scope && !debug_gc_check (f->scope, " as scope frame of trampoline frame")) +  gc_check_frame(f->scope); +  } +  } +  +  GC_CHECK +  { +  if (THIS->trampoline.frame && +  !debug_gc_check(THIS->trampoline.frame, " as trampoline frame")) +  gc_check_frame(THIS->trampoline.frame); +  } +  +  static void gc_recurse_frame(struct pike_frame *f) +  { +  if(f->current_object) gc_recurse_object(f->current_object); +  if(f->current_program) gc_recurse_program(f->current_program); +  if(f->flags & PIKE_FRAME_MALLOCED_LOCALS) +  gc_recurse_svalues(f->locals,f->num_locals); +  if(f->scope) gc_recurse_frame(f->scope); +  } +  +  GC_RECURSE +  { +  if (THIS->trampoline.frame) gc_recurse_frame(THIS->trampoline.frame); +  } +  +  PIKEFUN void `()(mixed ... ignored) +  { +  Pike_error("Internal error: Trampoline magic failed!\n"); +  } +  +  PIKEFUN int(0..1) `!() +  { +  if (!THIS->trampoline.frame || !THIS->trampoline.frame->current_object || +  !THIS->trampoline.frame->current_object->prog) { +  push_int(1); +  } else { +  push_int(0); +  } +  } +  +  PIKEFUN string _sprintf(int c, mapping|void opts) +  { +  struct byte_buffer buf = BUFFER_INIT(); +  +  if (!args || TYPEOF(Pike_sp[-args]) != T_INT || +  Pike_sp[-args].u.integer != 'O' || +  !THIS->trampoline.frame || !THIS->trampoline.frame->current_object) { +  pop_n_elems (args); +  push_int (0); +  return; +  } +  pop_n_elems (args); +  +  ref_push_function (THIS->trampoline.frame->current_object, +  THIS->trampoline.func); +  describe_svalue (&buf, Pike_sp - 1, 0, 0); +  pop_stack(); +  push_string(buffer_finish_pike_string(&buf)); +  } + } +    void init_builtin(void)   {    SET_SVAL(gc_pre_cb, PIKE_T_INT, NUMBER_NUMBER, integer, 0);    SET_SVAL(gc_post_cb, PIKE_T_INT, NUMBER_NUMBER, integer, 0);    SET_SVAL(gc_destruct_cb, PIKE_T_INT, NUMBER_NUMBER, integer, 0);    SET_SVAL(gc_done_cb, PIKE_T_INT, NUMBER_NUMBER, integer, 0); -  INIT +  INIT;   }      void exit_builtin(void)   {    struct svalue zero;    if (val_module) free_object (val_module); -  EXIT +  EXIT;    SET_SVAL(zero, PIKE_T_INT, NUMBER_NUMBER, integer, 0);    assign_svalue(&gc_pre_cb, &zero);    assign_svalue(&gc_post_cb, &zero);    assign_svalue(&gc_destruct_cb, &zero);    assign_svalue(&gc_done_cb, &zero);   #ifndef DO_PIKE_CLEANUP    /* This is performed by exit_builtin_modules() at a later point    * in this case, so that the pike_list_node's are valid at cleanup    * time, thus avoiding "got invalid pointer" fatals at exit.    */    ba_destroy(&pike_list_node_allocator);   #endif   #ifndef USE_SETENV    if (env_allocs) free_mapping (env_allocs);   #endif -  + #ifdef __NT__ +  if (crypto_handle) CloseHandle(crypto_handle); + #else +  if (random_fd!=-1) close(random_fd); + #endif   }