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. - || $Id$ +    */      #include "global.h"   #include "interpret.h"   #include "svalue.h"   #include "pike_macros.h"   #include "object.h"   #include "program.h"   #include "array.h"   #include "pike_error.h"
pike.git/src/builtin.cmod:20:   #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 "stuff.h"   #include "gc.h" - #include "block_alloc.h" + #include "block_allocator.h"   #include "pikecode.h" -  + #include "opcodes.h" + #include "whitespace.h"      #include <ctype.h>   #include <errno.h>   #include <math.h>    -  + #ifdef HAVE_ARPA_INET_H + #include <arpa/inet.h> + #endif /* HAVE_ARPA_INET_H */ +  + #define DEFAULT_CMOD_STORAGE +    DECLARATIONS    -  +  + /*! @module System +  */ +  + /*! @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; +  + #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 +  + #define strftime_zone strftime + #define mktime_zone mktime + #define strptime_zone strptime + #define asctime_zone asctime + #define localtime_zone(X,Y) localtime(X) + #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; \ +  } \ +  } while(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. +  *! +  *! @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 +  *! +  */ +  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 /* 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 +  */ +  PIKEFUN string strftime(string(1..255) format) +  { +  char *buffer = xalloc( 8192 ); +  buffer[0] = 0; +  strftime_zone( buffer, 8192, 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 +  *! might cause other fields to be recalculated, as an example, +  *! adding 1000 to the hour field would advance the 'mday', 'mon' +  *! and possibly 'year' fields. +  *! +  *! 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 `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 +  *! (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; } +  +  /*! @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 +  push_undefined(); +  } +  } +  +  PIKEFUN string _sprintf( int flag, mapping options ) +  { +  int post_sum = 1; +  switch( flag ) +  { +  case 'O': +  push_static_text("System.TM("); +  post_sum = 1; +  /* fallthrough */ +  case 's': +  f_TM_asctime(0); +  push_static_text("\n"); +  if( GET_ZONE(&(THIS->t)) ) +  { +  push_static_text(" "); +  push_text( GET_ZONE(&(THIS->t)) ); +  f_add( 2 ); +  } +  else +  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_static_text(")"); +  f_add(3); +  } +  +  } +  +  /*! @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; +  { +  if( to == literal_int_string ) +  { +  f_TM_unix_time(0); +  return; +  } +  if( to == literal_string_string ) +  { +  f_TM_asctime(0); +  return; +  } +  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)) ); +  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 ); +  if( !res ) +  RETURN 0; +  +  /* These are supposedly correctly by localtime_zone. */ +  SET_GMTOFF(res, GET_GMTOFF(&(THIS->t))); +  SET_ZONE(res, GET_ZONE(&(THIS->t))); +  +  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; +  THIS->modified = 1; +  RETURN 1; +  } +  +  /*! @decl void create(int 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 ) +  { +  f_TM_gmtime( 1 ); +  if( Pike_sp[-1].u.integer == 0 ) +  Pike_error("time out of range\n"); +  } +  +  /*! @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->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; +  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 ) /* gmtime. */ +  SET_ZONE(t, "UTC"); +  else +  { +  add_ref(timezone); +  THIS->set_zone = timezone; +  SET_ZONE(t, timezone->str); +  } +  THIS->unix_time = mktime_zone( t ); +  } +  +  INIT { +  THIS->set_zone = 0; +  THIS->modified = 0; +  } +  +  EXIT { +  if( THIS->set_zone ) +  free_string( THIS->set_zone ); +  } + } + /*! @endclass +  */ + #undef FIX_THIS + #ifdef STRUCT_TM_HAS___TM_GMTOFF + #undef tm_zone + #undef tm_gmtoff + #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    *! for each symbol in @[p]:    *! @array    *! @elem int modifiers
pike.git/src/builtin.cmod:130:    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 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:290:    while(len && n)    {    s->str[--len]="0123456789abcdef"[n&0xf];    n>>=4;    }    }    RETURN end_shared_string(s);   }       - static INLINE int hexchar( int v ) - { -  return v<10 ? v+'0' : (v-10)+'a'; - } -  +    /*! @decl string string2hex(string data)    *! @appears String.string2hex    *!    *! Convert a string of binary data to a hexadecimal string.    *!    *! @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] = + { +  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +  +  /* '0' - '9' */ +  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +  +  16,16,16,16,16,16,16, +  +  /* 'A' - 'F' */ +  10, 11, 12, 13, 14, 15, +  +  16,16,16,16,16,16,16,16,16,16,16,16,16, +  16,16,16,16,16,16,16,16,16,16,16,16,16, +  +  /* 'a' - 'f' */ +  10, 11, 12, 13, 14, 15, +  +  16,16,16,16,16,16,16,16,16, +  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + }; +    PMOD_EXPORT - PIKEFUN string string2hex(string s) -  errname String.string2hex; +  PIKEFUN string(0..255) string2hex(string s)    optflags OPT_TRY_OPTIMIZE;   {    struct pike_string *hex; -  unsigned char *st = (unsigned char *)s->str; -  int i; +  unsigned char *p,*st = (unsigned char *)s->str; +  int i, l;       if (s->size_shift)    Pike_error("Bad argument 1 to string2hex(), expected 8-bit string.\n");       hex = begin_shared_string(2 * s->len); -  +  p = (unsigned char *)hex->str; +  l = s->len;    -  for (i=0; i<s->len; i++) { -  hex->str[i<<1] = hexchar(st[i]>>4); -  hex->str[i<<1|1] = hexchar(st[i]&15); +  for (i=0; i<l; i++) { +  *p++ = hexchar[*st>>4]; +  *p++ = hexchar[*st&15]; +  st++;    }       RETURN end_shared_string(hex);   }    - /*! @decl string hex2string(string hex) + /*! @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 hex2string(string hex) -  errname String.hex2string; + PIKEFUN string(0..255) hex2string(string hex)    optflags OPT_TRY_OPTIMIZE;   {    struct pike_string *s; -  int i, o=0; -  unsigned char *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 tmp, i; +  unsigned char *p, *q = (unsigned char *)hex->str; +  int l = i = hex->len; +  if(hex->size_shift) Pike_error("Wide strings are not allowed.\n");    -  s = begin_shared_string(l); -  for (i=0; i<l; i++) +  s = begin_shared_string(l>>1); +  p = (unsigned char *)s->str; +  while( l > 1 )    { -  s->str[i] = (q[o]<='9' ? q[o]-'0' :((q[o]+9)&15))<<4; o++; -  s->str[i] |= (q[o]<='9' ? q[o]-'0': ((q[o]+9)&15)); o++; +  tmp = 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:529:    *! this limit is that the current amount of garbage can only be    *! measured in a gc run, and if the gc starts to run very seldom    *! due to very little garbage, it might get too slow to react to an    *! increase in garbage generation. Set to 0.0 to turn this limit    *! off.    *! @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,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:563:    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_BAD_ARG_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_BAD_ARG_ERROR ("gc_parameters", 1,    "integer in the range -1..1 for 'enabled'");    if (gc_enabled != set->u.integer) { -  if (gc_enabled > 0) -  gc_enabled = set->u.integer; -  else { -  gc_enabled = 1; -  if (alloc_threshold == GC_MAX_ALLOC_THRESHOLD) -  alloc_threshold = GC_MIN_ALLOC_THRESHOLD; +  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 +  * gc interval has gotten longer than it should be then the +  * multiplier calculation in do_gc should compensate. */ +  alloc_threshold = saved_alloc_threshold;    } -  +  gc_enabled = set->u.integer; +  }    }, {    SET_SVAL(get, T_INT, NUMBER_NUMBER, integer, gc_enabled);    });    HANDLE_FLOAT_FACTOR ("garbage_ratio_low", gc_garbage_ratio_low);    HANDLE_FLOAT_FACTOR ("time_ratio", gc_time_ratio);    HANDLE_FLOAT_FACTOR ("garbage_ratio_high", gc_garbage_ratio_high);    HANDLE_FLOAT_FACTOR ("min_gc_time_ratio", gc_min_time_ratio);    HANDLE_FLOAT_FACTOR ("average_slowness", gc_average_slowness);    -  +  HANDLE_PARAM("pre_cb", { +  assign_svalue(&gc_pre_cb, set); +  }, { +  assign_svalue(&get, &gc_pre_cb); +  }); +  HANDLE_PARAM("post_cb", { +  assign_svalue(&gc_post_cb, set); +  }, { +  assign_svalue(&get, &gc_post_cb); +  }); +  HANDLE_PARAM("destruct_cb", { +  assign_svalue(&gc_destruct_cb, set); +  }, { +  assign_svalue(&get, &gc_destruct_cb); +  }); +  HANDLE_PARAM("done_cb", { +  assign_svalue(&gc_done_cb, set); +  }, { +  assign_svalue(&get, &gc_done_cb); +  }); +    #undef HANDLE_PARAM   #undef HANDLE_FLOAT_FACTOR       REF_RETURN params;   }      /*! @decl string ctime(int timestamp)    *!    *! Convert the output from a previous call to @[time()] into a readable    *! string containing the current year, month, day and time.
pike.git/src/builtin.cmod:658:    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,    "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:715:    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:775:    *! @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;   
pike.git/src/builtin.cmod:825:    ws = wstemp;    }    }    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:893:    } \    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)    *! @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; +  errname trim_all_whites;    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:946:    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 +  *! Returns a string with an ASCII table containing +  *! the current string table statistics. +  *! +  *! @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 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 child, program parent) + /*! @decl int inherits(program|object child, program parent)    *! @belongs Program    *!    *! Returns 1 if @[child] has inherited @[parent].    */   PMOD_EXPORT - PIKEFUN int program_inherits(program parent, program child) -  errname Program.inherits; + PIKEFUN int program_inherits(program|object child, program parent) +  errname inherits;    optflags OPT_TRY_OPTIMIZE;   { -  RETURN low_get_storage(parent, child) != -1; +  struct program *p = program_from_svalue(child); +  +  if (!p) +  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;   { -  INT32 line; +  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    *!    *! Returns the width of a string.    *! -  *! Three return values are possible: -  *! @int -  *! @value 8 -  *! The string @[s] only contains characters <= 255. -  *! @value 16 -  *! The string @[s] only contains characters <= 65535. -  *! @value 32 -  *! The string @[s] contains characters >= 65536. -  *! @endint +  *! @returns +  *! Three return values are currently possible: +  *! @int +  *! @value 8 +  *! The string @[s] only contains characters <= 255. +  *! @value 16 +  *! The string @[s] only contains characters <= 65535. +  *! @value 32 +  *! 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:1119:   }      /*! @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. */    add_ref(str);    return str;    }    return make_shared_binary_pcharp(pcharp, len);   }    - /*! @decl string function_name(function f) + /*! @decl string function_name(function|program f)    *! -  *! Return the name of the function @[f]. +  *! Return the name of the function or program @[f].    *!    *! If @[f] is a global function defined in the runtime @expr{0@}    *! (zero) will be returned.    *!    *! @seealso    *! @[function_object()]    */   PMOD_EXPORT   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 */ +  UNREACHABLE(return);       case PIKE_T_PROGRAM:    { -  struct program *p=func->u.program; +  p = func->u.program;       if(p->parent)    {    int e;    p=p->parent;    /* search constants in parent for this    * program...    */       for(e = p->num_identifier_references; e--; )
pike.git/src/builtin.cmod:1201:    "Parent program info:\n",    func->u.program);    dump_program_tables(func->u.program->parent, 0);    }   #endif    }    break;    }       case PIKE_T_FUNCTION: -  if(SUBTYPEOF(*func) == FUNCTION_BUILTIN) break; -  if(!func->u.object->prog) +  if((f = SUBTYPEOF(*func)) == FUNCTION_BUILTIN) break; +  if(!(p = func->u.object->prog))    bad_arg_error("function_name", Pike_sp-args, args, 1,    "function", Pike_sp-args,    "Destructed object.\n"); -  if(func->u.object->prog == pike_trampoline_program) +  if(p == pike_trampoline_program)    {    struct pike_trampoline *t;    t=((struct pike_trampoline *)func->u.object->storage);    -  if(t->frame->current_object->prog) -  RETURN delambda(ID_FROM_INT(t->frame->current_object->prog, -  t->func)->name); +  if(t->frame->current_object->prog) { +  p = t->frame->current_object->prog; +  f = t->func;    } -  +  }    -  RETURN delambda(ID_FROM_INT(func->u.object->prog, -  SUBTYPEOF(*func))->name); + #ifdef PIKE_DEBUG +  if(f >= p->num_identifier_references) +  Pike_fatal("Function without reference.\n"); + #endif +  RETURN delambda(ID_FROM_INT(p, f)->name);    }    pop_n_elems(args);    push_int(0);   }      /*! @decl object function_object(function f)    *!    *! Return the object the function @[f] is in.    *!    *! If @[f] is a global function defined in the runtime @expr{0@}
pike.git/src/builtin.cmod:1328:    SIMPLE_BAD_ARG_ERROR("function_program", 1, "function");    }    pop_n_elems(args);    push_int(0);   }         /*! @decl mixed random(object o)    *! If random is called with an object, @[lfun::random] will be    *! called in the object. +  *!    *! @seealso -  *! @[lfun::_random] +  *! @[lfun::_random()]    */    - /*! @decl mixed lfun::_random() -  *! Called by @[random]. Typical uses is when the object implements -  *! a ADT, then a call to this lfun should return a random member of -  *! the ADT or range implied by the ADT. -  *! @seealso -  *! @[predef::random()] -  */ -  +    PMOD_EXPORT   PIKEFUN mixed random(object o)    efun;    optflags OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND;   { -  apply(o,"_random",0); +  int f = low_find_lfun(o->prog, LFUN__RANDOM); +  if (f < 0) { +  Pike_error("Calling undefined lfun::%s.\n", lfun_names[LFUN__RANDOM]); +  } +  apply_low(o, f, 0);    stack_swap();    pop_stack();   }      /*! @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()]    */      PMOD_EXPORT   PIKEFUN int random(int i)   {    if(i <= 0) RETURN 0; - #if defined (AUTO_BIGNUM) && 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); -  } + #if SIZEOF_INT_TYPE > 4 +  if(i >> 31) +  RETURN my_rand64(i);   #endif -  RETURN my_rand() % i; +  RETURN my_rand(i);   }    -  + /*! @decl float random(float max) +  *! +  *! This function returns a random number in the range @expr{0 .. @[max]-ɛ@}. +  *! +  *! @seealso +  *! @[random_seed()] +  */ +    PMOD_EXPORT   PIKEFUN float random(float f)   {    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) )); +     -  +  RETURN (FLOAT_TYPE)ldexp((double)f * my_rand64(0xffffffffffffffff), -64);   }      /*! @decl mixed random(array|multiset x)    *! Returns a random element from @[x].    */      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)); +  push_svalue(a->item + (my_rand(a->size)));    stack_swap();    pop_stack();   }      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); +  push_multiset_index (m, multiset_get_nth (m, my_rand(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.    */      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");    -  /* Find a random, nonempty bucket */ -  bucket=my_rand() % md->hashsize; -  while(! md->hash[bucket] ) -  if(++bucket > (size_t)md->hashsize) -  bucket=0; +  count = my_rand( m_sizeof(m) );    -  /* Count entries in bucket */ -  count=0; -  for(k=md->hash[bucket];k;k=k->next) count++; +  /* We could optimize this by not iterating over hash buckets we will +  not pick a member from. */    -  /* 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;    } -  +  } + }      #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;
pike.git/src/builtin.cmod:1492:    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:1520:    * 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:1563:    * 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:1671:    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. */    CVAR struct pike_string *filename; -  CVAR INT32 lineno; +  CVAR INT_TYPE lineno;       INIT    {    THIS->pc = NULL;    THIS->lineno = -1;    THIS->filename = NULL;    }       EXIT    gc_trivial;
pike.git/src/builtin.cmod:1719:    }   #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 == findstring("array"));    }       static void fill_in_file_and_line()    {    struct pike_string *file = NULL;    assert (THIS->lineno == -1);       if (THIS->pc && THIS->prog) {    file = low_get_line(THIS->pc, THIS->prog, &THIS->lineno);    THIS->pc = NULL;
pike.git/src/builtin.cmod:1772:    */    PIKEFUN string _sprintf(int c, mapping|void opts)    {    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->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_static_text("program(), "); +  } else if (TYPEOF(THIS->_fun) == PIKE_T_STRING) { +  push_svalue(&THIS->_fun); +  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()    {    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)    {    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) { +  if (index < 0)    index_error("pike_frame->`[]", Pike_sp-args, args, NULL, Pike_sp-args,    "Indexing with negative index (%"PRINTPIKEINT"d)\n", index); -  } else if (index >= numargs) { +  else if (index >= numargs)    index_error("pike_frame->`[]", Pike_sp-args, 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->filename)    ref_push_string(THIS->filename); -  } else { +  else    push_int(0); -  } +     break;    case 1: /* Linenumber */    if (THIS->lineno == -1) 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("`[]", Pike_sp-args, 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)    { -  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)) { +  if ((index < -numargs) || (index >= numargs))    index_error("pike_frame->`[]=", Pike_sp-args, 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 (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_BAD_ARG_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 (TYPEOF(*value) != PIKE_T_INT) +  SIMPLE_BAD_ARG_ERROR("`[]=", 2, "int(1..)");    THIS->lineno = value->u.integer;    break;       case 2: /* Function */    if (THIS->lineno == -1) fill_in_file_and_line();    assign_svalue(&THIS->_fun, value);    break;    default: /* Arguments */    assign_svalue(THIS->args->item + index - 3, value);    break;
pike.git/src/builtin.cmod:2015:    *! 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.    *! @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("float_size");    push_int(sizeof(FLOAT_TYPE) * 8); - #ifdef AUTO_BIGNUM -  push_constant_text("auto_bignum"); +  push_static_text("auto_bignum");    push_int(1);    f_aggregate_mapping(6*2); - #else -  f_aggregate_mapping(5*2); - #endif +    }      /*! @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;
pike.git/src/builtin.cmod:2069:       SET_SVAL(res->item[size], PIKE_T_OBJECT, 0, object, o);       bf = OBJ2_BACKTRACE_FRAME(o);       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, +  program, f->current_object->prog); +  add_ref(f->current_object->prog); +  } else {    SET_SVAL(bf->_fun, PIKE_T_FUNCTION,    CHECK_IDREF_RANGE(f->fun, f->current_object->prog),    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 -  function = ID_FROM_INT(f->current_object->prog, f->fun); -  } else { -  SET_SVAL(bf->_fun, PIKE_T_INT, NUMBER_DESTRUCTED, integer, 0); +     } -  +  }       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 */ -  numargs = DO_NOT_WARN((INT32)MINIMUM(f->num_args,of->locals - f->locals)); +  * reason. I don't know why. /mast +  * +  * possibly because f->num_args was uninitialized for c_initializers +  * /arne +  * */ +  +  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:2144:    *! @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
pike.git/src/builtin.cmod:2195:    *! .. 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( args ) +  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 */)" ); +  push_static_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 );    }
pike.git/src/builtin.cmod:2270:    }    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 ) +  flags ID_PROTECTED;    { -  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 ) +  if( type == literal_string_string )    { -  pop_n_elems( args ); +  pop_stack();    if( Pike_fp->current_object->refs != 1 )    f_Buffer_get_copy( 0 );    else    f_Buffer_get( 0 );    return;    }    -  if( type == int_t ) +  if( type == literal_int_string )    {    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); +  +  pop_stack(); +  push_undefined();    }    -  /*! @decl String.Buffer `+( string what ) +  /*! @decl String.Buffer `+( string|String.Buffer what )    */ -  PIKEFUN object `+( string 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); -  +  if( (Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT) ) +  res->flags |= OBJECT_CLEAR_ON_EXIT;    apply( res, "add", 1 );    RETURN res;    }    -  /*! @decl String.Buffer `+=( string what ) +  /*! @decl String.Buffer `+=( string|String.Buffer what )    */ -  PIKEFUN object `+=( string 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 ... data) +  /*! @decl int add(string|String.Buffer ... data)    *!    *! Adds @[data] to the buffer.    *!    *! @returns    *! Returns the size of the buffer.    *! -  *! @seealso -  *! @[addat()] +  *! @note +  *! Pike 7.8 and earlier did not support adding @[String.Buffer]s +  *! directly.    */ -  PIKEFUN int add( string ... arg1 ) +  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; +     -  if (!str->str.s && args) { +  if (args) { +  int init_from_arg0 = 0, j;    ptrdiff_t sum = 0;    int shift = 0;    for (j=0; j < args; j++) { -  struct pike_string *a = Pike_sp[j-args].u.string; +  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("add", 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 (sum < str->initial) -  sum = str->initial; -  else if (sum > str->initial) -  sum <<= 1; +  shift |= str->str.known_shift;    shift = shift & ~(shift >> 1); -  -  if (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 = Pike_sp[j-args].u.string; -  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 ... 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 is padded -  *! with NUL-characters. -  *! -  *! @seealso -  *! @[add()] -  */ -  PIKEFUN int addat(int(0..) pos, string ... arg1 ) -  { -  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 = Pike_sp[j-args].u.string; -  sum += a->len; -  shift |= a->size_shift; -  } -  +     if (!str->str.s) { -  if ((sum + pos) <= str->initial) { +  if (sum <= str->initial)    sum = str->initial; -  } else { +  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; +  } else +  string_build_mkspace(&str->str, sum, 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(sum = str->str.s->len, 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;    } -  -  for(j = 1; j<args; j++) { -  struct pike_string *a = Pike_sp[j-args].u.string; -  pike_string_cpy(MKPCHARP_STR_OFF(str->str.s, pos), a); -  pos += a->len; +  pike_string_cpy(MKPCHARP_STR_OFF(str->str.s, sum), a); +  sum += 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; +  str->str.s->len = sum;    } -  } +     -  if (str->str.s) { -  RETURN str->str.s->len; -  } else { -  RETURN 0; +  RETURN str->str.s ? str->str.s->len : 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, &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; +  if( str && (len = str->len) > 0 )    { -  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); +  str=make_shared_binary_string0((p_wchar0 *)d,len);    break;    case 1: -  RETURN make_shared_binary_string1((p_wchar1 *)d,len); +  str=make_shared_binary_string1((p_wchar1 *)d,len);    break;    case 2: -  RETURN make_shared_binary_string2((p_wchar2 *)d,len); +  str=make_shared_binary_string2((p_wchar2 *)d,len);    break;    } -  +  if( Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT ) +  str->flags |= STRING_CLEAR_ON_EXIT; +  RETURN str;    } -  } +     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; -  +  pop_n_elems(args);    if( str->str.s )    {    struct pike_string *s = finish_string_builder( &str->str );    str->str.malloced = 0;    str->str.s = NULL; -  RETURN s; +  if( Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT ) +  s->flags |= STRING_CLEAR_ON_EXIT; +  push_string(s);    } -  pop_n_elems(args); +  else    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;    }   
pike.git/src/builtin.cmod:2543:    {    struct Buffer_struct *str = THIS;    MEMSET( str, 0, sizeof( *str ) );    }       EXIT    gc_trivial;    {    struct Buffer_struct *str = THIS;    if( str->str.s ) +  { +  if( Pike_fp->flags & OBJECT_CLEAR_ON_EXIT ) +  guaranteed_memset( str->str.s->str, 0, str->str.s->len );    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    */
pike.git/src/builtin.cmod:2573:    *! applied. The Replace object is used internally by the Pike    *! optimizer and need not be used manually.    */   PIKECLASS multi_string_replace   {    CVAR struct replace_many_context ctx;    /* NOTE: from and to are only kept for _encode()'s use. */    PIKEVAR array from flags ID_PROTECTED;    PIKEVAR array to flags ID_PROTECTED;    -  /*! @decl void create(array(string)|mapping(string:string)|void from, @ -  *! array(string)|string|void to) +  PIKEFUN int _size_object() +  { +  int res = 0, i; +  if( THIS->ctx.v ) +  { +  struct svalue tmp; +  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() +  *! @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); +  switch(args) +  { +  case 0:    return; -  } -  if (from_arg && TYPEOF(*from_arg) == T_MAPPING) { -  if (to_arg) { -  Pike_error("Bad number of arguments to create().\n"); -  } +  +  case 1: +  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); -  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; +  break; +  +  case 2:    if (TYPEOF(*from_arg) != T_ARRAY) { -  SIMPLE_BAD_ARG_ERROR("Replace", 1, +  SIMPLE_BAD_ARG_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);    }    if (TYPEOF(*to_arg) != T_ARRAY) { -  SIMPLE_BAD_ARG_ERROR("Replace", 2, "array(string)|string"); +  SIMPLE_BAD_ARG_ERROR("replace", 2, "array(string)|string");    }    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);    }       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_BAD_ARG_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_BAD_ARG_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:2690:    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);    }       INIT    {    MEMSET(&THIS->ctx, 0, sizeof(struct replace_many_context));    }       EXIT
pike.git/src/builtin.cmod:2757:    THIS->del = NULL;    }    if (THIS->to) {    free_string(THIS->to);    THIS->to = NULL;    }       if (!del) return;       if (!to) { -  SIMPLE_BAD_ARG_ERROR("String.SingleReplace->create", 2, "string"); +  SIMPLE_BAD_ARG_ERROR("replace", 2, "string");    }       if (del == to) {    /* No-op... */    return;    }       copy_shared_string(THIS->del, del);    copy_shared_string(THIS->to, to);   
pike.git/src/builtin.cmod:2816:    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 +     }       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:2885:    }       /*! @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:3002:    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:3194:    *! last whole second, respectively. (See also @[predef::time()])    *!    *! @note    *! Please note that these variables will continually update when    *! they are requested, there is no need to create new Time()    *! objects.    */       PIKEFUN int `sec()    { -  extern struct timeval current_time; +  struct timeval now;       if( THIS->hard_update ) -  GETTIMEOFDAY( &current_time ); +  ACCURATE_GETTIMEOFDAY( &now ); +  else +  INACCURATE_GETTIMEOFDAY( &now );    -  RETURN current_time.tv_sec; +  RETURN now.tv_sec;    }       PIKEFUN int `usec()    { -  extern struct timeval current_time; +  struct timeval now;       if( THIS->hard_update ) -  GETTIMEOFDAY( &current_time ); +  ACCURATE_GETTIMEOFDAY( &now ); +  else +  INACCURATE_GETTIMEOFDAY( &now );    -  RETURN current_time.tv_usec; +  RETURN now.tv_usec;    }       /*! @decl int usec_full    *!    *! The number of microseconds since the epoch. Please note that    *! pike needs to have been compiled with bignum support for this    *! variable to contain sensible values.    */       PIKEFUN int `usec_full()    { -  extern struct timeval current_time; +  struct timeval now;       if( THIS->hard_update ) -  GETTIMEOFDAY( &current_time ); +  ACCURATE_GETTIMEOFDAY( &now ); +  else +  INACCURATE_GETTIMEOFDAY( &now );    - #ifdef AUTO_BIGNUM -  push_int( current_time.tv_sec ); +  push_int( now.tv_sec );    push_int( 1000000 );    f_multiply( 2 ); -  push_int( current_time.tv_usec ); +  push_int( now.tv_usec );    f_add( 2 );    return; - #else -  RETURN (current_time.tv_sec * 1000000 + current_time.tv_usec); - #endif +     }       /*! @decl protected void create( int fast );    *!    *! If @[fast] is true, do not request a new time from the system,    *! instead use the global current time variable.    *!    *! This will only work in callbacks, but can save significant amounts    *! of CPU.    */
pike.git/src/builtin.cmod:3268:   PIKECLASS Timer   {    CVAR struct timeval last_time;    CVAR int hard_update;       /*! @decl float peek()    *! Return the time in seconds since the last time @[get] was called.    */    PIKEFUN float peek( )    { -  extern struct timeval current_time; +  struct timeval now;    FLOAT_TYPE res;    if( THIS->hard_update ) -  GETTIMEOFDAY( &current_time ); -  res = current_time.tv_sec-THIS->last_time.tv_sec + -  (current_time.tv_usec-THIS->last_time.tv_usec)/(FLOAT_TYPE) 1000000.0; +  ACCURATE_GETTIMEOFDAY( &now ); +  else +  INACCURATE_GETTIMEOFDAY( &now ); +  res = now.tv_sec-THIS->last_time.tv_sec + +  (now.tv_usec-THIS->last_time.tv_usec)/(FLOAT_TYPE) 1000000.0;    RETURN res;    }       /*! @decl float get()    *! Return the time in seconds since the last time get was called.    *! The first time this method is called the time since the object    *! was created is returned instead.    */    PIKEFUN float get( )    { -  extern struct timeval current_time; +     f_Timer_peek( 0 ); -  THIS->last_time = current_time; +  INACCURATE_GETTIMEOFDAY(&THIS->last_time);    return;    }       /*! @decl protected void create( int|void fast )    *! Create a new timer object. The timer keeps track of relative time    *! with sub-second precision.    *!    *! If @[fast] is specified, the timer will not do system calls to get    *! the current time but instead use the one maintained by pike. This    *! will result in faster but more or less inexact timekeeping.    *! The pike maintained time is only updated when a @[Pike.Backend]    *! object stops waiting and starts executing code.    */    PIKEFUN void create( int|zero|void fast )    flags ID_PROTECTED;    { -  extern struct timeval current_time; +     THIS->hard_update = !fast;    if( THIS->hard_update ) -  GETTIMEOFDAY( &current_time ); -  THIS->last_time = current_time; +  ACCURATE_GETTIMEOFDAY( &THIS->last_time ); +  else +  INACCURATE_GETTIMEOFDAY( &THIS->last_time );    }   }      /*! @endclass    */      /*! @endmodule    */       -  + /*! @module Builtin +  */ +  + /*! @class automap_marker +  *! +  *! This is an internal class used by @[__automap__()]. +  *! +  *! It may show up during module dumping or in backtraces +  *! and the like. +  *! +  *! It should in normal circumstances never be used directly. +  *! +  *! @seealso +  *! @[__automap__()], @[map()] +  */   PIKECLASS automap_marker   {    PIKEVAR array arg;    PIKEVAR int depth;    -  +  /*! @decl void create(array arr, int depth) +  *! +  *! @param arr +  *! 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;    }       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 +  */    -  + /*! @endmodule +  */ +    static void low_automap(int d,    int depth,    struct svalue *fun,    struct svalue *real_args,    INT32 args)   {    INT32 x,e,tmp,size=0x7fffffff;    struct svalue *tmpargs=Pike_sp - args;    struct array *ret;    TYPE_FIELD types;
pike.git/src/builtin.cmod:3378:    args,    tmpargs,    NULL,    "Automap on non-array.\n");    tmp=tmpargs[e].u.array->size;    if(tmp < size)    size=tmp;    }    }    - #ifdef PIKE_DEBUG +     if(size == 0x7fffffff) -  Pike_fatal("No automap markers found in low_automap\n"); - #endif +  Pike_error("No automap markers found in __automap__\n");       push_array(ret=allocate_array(size));    types = 0;       for(x=0;x<size;x++)    {    for(e=0;e<args;e++)    {    if(TYPEOF(real_args[e]) == T_OBJECT &&    real_args[e].u.object->prog == automap_marker_program &&
pike.git/src/builtin.cmod:3415:    apply_svalue(fun,args);    else    low_automap(d+1,depth,fun,real_args,args);    stack_pop_to_no_free (ITEM(ret) + x);    types |= 1 << TYPEOF(ITEM(ret)[x]);    }    ret->type_field = types;    stack_unlink(args);   }    -  + /*! @decl array __automap__(function fun, mixed ... args) +  *! +  *! Automap execution function. +  *! +  *! @param fun +  *! Function to call for each of the mapped arguments. +  *! +  *! @param args +  *! Arguments for @[fun]. Either +  *! @mixed +  *! @type Builtin.automap_marker +  *! Wrapper for an array to loop over. All of the +  *! arrays will be looped over in parallel. +  *! @type mixed +  *! All other arguments will be held constant during +  *! the automap, and sent as is to @[fun]. +  *! @endmixed +  *! +  *! @note +  *! This function is used by the compiler to implement the +  *! automap syntax, and should in normal circumstances never +  *! be used directly. +  *! +  *! It may however show up during module dumping and in +  *! backtraces. +  *! +  *! @note +  *! It is an error not to have any @[Builtin.automap_marker]s +  *! in @[args]. +  *! +  *! @seealso +  *! @[Builtin.automap_marker], @[map()] +  */   PIKEFUN array __automap__(mixed fun, mixed ... tmpargs)    efun;   {    int e,depth=-1;    check_stack(args);       for(e=0;e<args-1;e++)    {    if(TYPEOF(tmpargs[e]) == T_OBJECT &&    tmpargs[e].u.object->prog == automap_marker_program)
pike.git/src/builtin.cmod:3439:    ref_push_array(OBJ2_AUTOMAP_MARKER(tmpargs[e].u.object)->arg);    }else{    push_svalue(tmpargs+e);    }    }    check_stack(depth * (args+1));    low_automap(1,depth,fun,tmpargs,args-1);    stack_unlink(args);   }    - /* Linked list stuff. -  */ - #undef INIT_BLOCK - #define INIT_BLOCK(NODE) do { \ -  (NODE)->next = (NODE)->prev = NULL; \ -  (NODE)->refs = 1; \ -  SET_SVAL((NODE)->val, T_INT, NUMBER_UNDEFINED, \ -  integer, 0); \ -  } while(0) -  - #undef EXIT_BLOCK - #define EXIT_BLOCK(NODE) do { \ -  if ((NODE)->prev) { \ -  free_list_node((NODE)->prev); \ -  } \ -  if ((NODE)->next) { \ -  free_list_node((NODE)->next); \ -  } \ -  free_svalue(&(NODE)->val); \ -  } while(0) -  - BLOCK_ALLOC_FILL_PAGES(pike_list_node, 4); -  - PMOD_EXPORT void free_list_node(struct pike_list_node *node) - { -  if (!sub_ref(node)) { -  really_free_pike_list_node(node); -  } - } -  - PMOD_EXPORT void unlink_list_node(struct pike_list_node *n) - { - #ifdef PIKE_DEBUG -  if (!n) { -  Pike_fatal("Unlinking NULL node.\n"); -  } -  if (!n->next || !n->prev) { -  Pike_fatal("Unlinking unlinked node.\n"); -  } - #endif /* PIKE_DEBUG */ -  if (n->prev->next == n) { - #ifdef PIKE_DEBUG -  if (n->next->prev != n) { -  Pike_fatal("Partially detached node.\n"); -  } - #endif /* PIKE_DEBUG */ -  n->prev->next = n->next; -  n->next->prev = n->prev; -  n->next = n->prev = NULL; -  -  /* We've lost two references. */ -  free_list_node(n); -  free_list_node(n); -  } else { - #ifdef PIKE_DEBUG -  if (n->next->prev == n) { -  Pike_fatal("Partially detached node.\n"); -  } - #endif /* PIKE_DEBUG */ -  /* The node is already detached. */ -  n->next = n->prev = NULL; -  } - } -  - PMOD_EXPORT void detach_list_node(struct pike_list_node *n) - { - #ifdef PIKE_DEBUG -  if (!n) { -  Pike_fatal("Detaching NULL node.\n"); -  } -  if (!n->next || !n->prev) { -  Pike_fatal("Detaching unlinked node.\n"); -  } - #endif /* PIKE_DEBUG */ -  if (n->prev->next == n) { - #ifdef PIKE_DEBUG -  if (n->next->prev != n) { -  Pike_fatal("Partially detached node.\n"); -  } - #endif /* PIKE_DEBUG */ -  n->prev->next = n->next; -  n->next->prev = n->prev; -  add_ref(n->next); -  add_ref(n->prev); -  -  /* We've lost two references. */ -  free_list_node(n); -  free_list_node(n); - #ifdef PIKE_DEBUG -  } else if (n->next->prev == n) { -  Pike_fatal("Partially detached node.\n"); - #endif /* PIKE_DEBUG */ -  } - } -  - PMOD_EXPORT void prepend_list_node(struct pike_list_node *node, -  struct pike_list_node *new_node) - { - #ifdef PIKE_DEBUG -  if (!node) { -  Pike_fatal("No node to prepend.\n"); -  } -  if (!node->prev) { -  Pike_fatal("Prepending unhooked node.\n"); -  } -  if (!new_node) { -  Pike_fatal("Prepending NULL node.\n"); -  } -  if (new_node->next || new_node->prev) { -  Pike_fatal("Prepending hooked node.\n"); -  } - #endif /* PIKE_DEBUG */ -  new_node->next = node; -  new_node->prev = node->prev; -  new_node->prev->next = node->prev = new_node; -  add_ref(new_node); -  add_ref(new_node); - } -  - PMOD_EXPORT void append_list_node(struct pike_list_node *node, -  struct pike_list_node *new_node) - { - #ifdef PIKE_DEBUG -  if (!node) { -  Pike_fatal("No node to append.\n"); -  } -  if (!node->next) { -  Pike_fatal("Appending unhooked node.\n"); -  } -  if (!new_node) { -  Pike_fatal("Appending NULL node.\n"); -  } -  if (new_node->next || new_node->prev) { -  Pike_fatal("Appending hooked node.\n"); -  } - #endif /* PIKE_DEBUG */ -  new_node->next = node->next; -  new_node->prev = node; -  new_node->next->prev = node->next = new_node; -  add_ref(new_node); -  add_ref(new_node); - } -  +    /*! @module Builtin    */      /*! @class Setter    *!    *! Internal class for implementing setters. -  +  *! +  *! This class is used by @[_get_setter()]. +  *! +  *! @seealso +  *! @[_get_setter()]    */   PIKECLASS Setter   {    PIKEVAR object o    flags ID_PROTECTED|ID_PRIVATE|ID_LOCAL;    CVAR int f; -  +  +  /*! @decl void `()(mixed val) +  *! +  *! Set the variable for the setter to @[val]. +  *! +  *! 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)   {    struct object *res = clone_object(Setter_program, 0);    struct Setter_struct *setter = OBJ2_SETTER(res);    add_ref(setter->o = o);    setter->f = f;    return res;   }    - /*! @decl function(mixed_void) _get_setter(object o, string s) + /*! @decl function(mixed_void) _get_setter(object o, string varname)    *! -  *! Get a setter for the variable named @[s] in object @[o]. +  *! Get a setter for the variable named @[varname] in object @[o].    *! -  +  *! @returns +  *! Returns a @[Setter()->`()()] for the variable if it exists, +  *! and @expr{UNDEFINED@} otherwise. +  *!    *! @seealso    *! @[object_variablep()]    */   PIKEFUN function(mixed:void) _get_setter(object o, string s)   {    struct program *p;    struct inherit *inh;    int f;    if (!(p = o->prog)) {    Pike_error("Indexing a destructed object.\n");
pike.git/src/builtin.cmod:3659:    if ((f >= 0) &&    IDENTIFIER_IS_VARIABLE(ID_FROM_INT(p, f)->identifier_flags)) {    f += inh->identifier_level;    push_function(get_setter(o, f), f_Setter_cq__backtick_28_29_fun_num);    } else {    push_undefined();    }    stack_pop_n_elems_keep_top(args);   }    - /*! @endclass -  */ -  +    /*! @class Null    *!    *! This class is used to implement the low-level aspects of @[Val.Null].    *!    *! @note    *! This class should typically not be used directly. Use    *! @[Val.Null] instead.    *!    *! @note    *! This class was previously available as @[Sql.Null]. Any such use
pike.git/src/builtin.cmod:3708:    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:3755:    }    }       /*! @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    */    -  + 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; +  +  /* Return -1 if any of the strings is wider than 8 bits: */ +  if (a->size_shift || b->size_shift) RETURN -1; +  +  /* Allocate two rows on the stack: */ +  n = b->len+1; +  lev_i = alloca(n*sizeof(int)); +  lev_p = alloca(n*sizeof(int)); +  if (!lev_i || !lev_p) RETURN -1; +  +  /* Initialise the first row */ +  for (j = 0; j < n; j++) lev_i[j] = j; +  +  for (i = 0; i < a->len; i++) +  { +  /* lev_p = row for i, lev_i = row for i+1: */ +  memcpy(lev_p, lev_i, n*sizeof(int)); +  lev_i[0] = i + 1; +  for (j = 0; j < b->len; j++) +  { +  int cost = (a->str[i] == b->str[j]) ? 0 : 1; +  int test, min_val = lev_i[j]+1; +  if ((test = lev_p[j+1]+1) < min_val) min_val = test; +  if ((test = lev_p[j]+cost) < min_val) min_val = test; +  lev_i[j+1] = min_val; +  } +  } +  RETURN lev_i[b->len]; + } +    /*! @endmodule    */      /*! @module Serializer    */      /*! @class Serializable    *!    *! 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:3861:    *! @[_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.    *!    *! @param serializer    *! Function to typically be called once for every variable    *! in the inheriting class.    *!    *! This function calls @[_serialize_variable()] once -  *! for every variable in the inheriting class. +  *! for every variable in the inheriting class, which +  *! in turn will call @[serializer] with the arguments: +  *! @dl +  *! @item Argument 1 +  *! The value of the variable. +  *! @item Argument 2 +  *! The name of the variable. +  *! @item Argument 3 +  *! The declared type of the variable. +  *! @enddl    *!    *! @note    *! The symbols will be listed in the order they were defined    *! in the class.    *!    *! @note    *! This function is typically called via @[Serializer.serialize()]. -  +  *! +  *! @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");    }    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:3969:    *! Selects one of the object types in case of a complex    *! @[symbol_type]. The selected type is NOT deterministic    *! in case there are multiple choices that satisfy the above.    *! @item    *! Is likely to throw errors if @tt{p()@} requires arguments.    *! @endul    *!    *! These issues can all be solved by overloading this function.    *!    *! @seealso -  *! @[_deserialize()], @[_serialize_variable()] +  *! @[_deserialize()], @[_serialize_variable()], @[Builtin.Setter]    */    PIKEFUN void _deserialize_variable(function(function(mixed:void),    string, type: void)    deserializer, function(mixed:void) setter,    string symbol,    type symbol_type)    flags ID_PROTECTED;    rawtype tFunc(tFunc(tFunc(tMix, tVoid) tStr tType(tMix), tVoid)    tFunc(tMix, tVoid) tStr tType(tMix), tVoid);    {
pike.git/src/builtin.cmod:3993:    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.    *!    *! @param deserializer    *! Function to typically be called once for every variable    *! in the inheriting class.    *!    *! This function calls @[_deserialize_variable()] once -  *! for every variable in the inheriting class. +  *! for every variable in the inheriting class, which +  *! in turn will call @[deserializer] with the arguments: +  *! @dl +  *! @item Argument 1 +  *! The setter for the variable. +  *! @item Argument 2 +  *! The name of the variable. +  *! @item Argument 3 +  *! The declared type of the variable. +  *! @enddl    *!    *! @note    *! The symbols will be listed in the order they were defined    *! in the class.    *!    *! @note    *! This function is typically called via @[Serializer.deserialize()]. -  +  *! +  *! @seealso +  *! @[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");    }    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:4100:    }    apply_low(o, fun + inh->identifier_level, args);   }      /*! @endmodule    */      /*! @module ADT    */    + /* Linked list stuff. +  */ + static struct block_allocator pike_list_node_allocator = BA_INIT_PAGES(sizeof(struct pike_list_node), 4); +  + ATTRIBUTE((malloc)) + static struct pike_list_node * alloc_pike_list_node() { +  struct pike_list_node * node = ba_alloc(&pike_list_node_allocator); +  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) { +  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) { +  free_list_node(node->next); +  } +  free_svalue(&node->val); +  ba_free(&pike_list_node_allocator, node); +  } + } +  + PMOD_EXPORT void unlink_list_node(struct pike_list_node *n) + { + #ifdef PIKE_DEBUG +  if (!n) { +  Pike_fatal("Unlinking NULL node.\n"); +  } +  if (!n->next || !n->prev) { +  Pike_fatal("Unlinking unlinked node.\n"); +  } + #endif /* PIKE_DEBUG */ +  if (n->prev->next == n) { + #ifdef PIKE_DEBUG +  if (n->next->prev != n) { +  Pike_fatal("Partially detached node.\n"); +  } + #endif /* PIKE_DEBUG */ +  n->prev->next = n->next; +  n->next->prev = n->prev; +  n->next = n->prev = NULL; +  +  /* We've lost two references. */ +  free_list_node(n); +  free_list_node(n); +  } else { + #ifdef PIKE_DEBUG +  if (n->next->prev == n) { +  Pike_fatal("Partially detached node.\n"); +  } + #endif /* PIKE_DEBUG */ +  /* The node is already detached. */ +  n->next = n->prev = NULL; +  } + } +  + PMOD_EXPORT void detach_list_node(struct pike_list_node *n) + { + #ifdef PIKE_DEBUG +  if (!n) { +  Pike_fatal("Detaching NULL node.\n"); +  } +  if (!n->next || !n->prev) { +  Pike_fatal("Detaching unlinked node.\n"); +  } + #endif /* PIKE_DEBUG */ +  if (n->prev->next == n) { + #ifdef PIKE_DEBUG +  if (n->next->prev != n) { +  Pike_fatal("Partially detached node.\n"); +  } + #endif /* PIKE_DEBUG */ +  n->prev->next = n->next; +  n->next->prev = n->prev; +  add_ref(n->next); +  add_ref(n->prev); +  +  /* We've lost two references. */ +  free_list_node(n); +  free_list_node(n); + #ifdef PIKE_DEBUG +  } else if (n->next->prev == n) { +  Pike_fatal("Partially detached node.\n"); + #endif /* PIKE_DEBUG */ +  } + } +  + PMOD_EXPORT void prepend_list_node(struct pike_list_node *node, +  struct pike_list_node *new_node) + { + #ifdef PIKE_DEBUG +  if (!node) { +  Pike_fatal("No node to prepend.\n"); +  } +  if (!node->prev) { +  Pike_fatal("Prepending unhooked node.\n"); +  } +  if (!new_node) { +  Pike_fatal("Prepending NULL node.\n"); +  } +  if (new_node->next || new_node->prev) { +  Pike_fatal("Prepending hooked node.\n"); +  } + #endif /* PIKE_DEBUG */ +  new_node->next = node; +  new_node->prev = node->prev; +  new_node->prev->next = node->prev = new_node; +  add_ref(new_node); +  add_ref(new_node); + } +  + PMOD_EXPORT void append_list_node(struct pike_list_node *node, +  struct pike_list_node *new_node) + { + #ifdef PIKE_DEBUG +  if (!node) { +  Pike_fatal("No node to append.\n"); +  } +  if (!node->next) { +  Pike_fatal("Appending unhooked node.\n"); +  } +  if (!new_node) { +  Pike_fatal("Appending NULL node.\n"); +  } +  if (new_node->next || new_node->prev) { +  Pike_fatal("Appending hooked node.\n"); +  } + #endif /* PIKE_DEBUG */ +  new_node->next = node->next; +  new_node->prev = node; +  new_node->next->prev = node->next = new_node; +  add_ref(new_node); +  add_ref(new_node); + } +    /*! @class List    *!    *! Linked list of values.    */   PIKECLASS List   {    CVAR struct pike_list_node *head;    CVAR INT32 head_sentinel_refs;    CVAR struct pike_list_node *tail; /* Always NULL. */    CVAR INT32 tail_sentinel_refs;
pike.git/src/builtin.cmod:4141:    * o Detach segment (requires new iterator implementation)    * o Iterator copy    * o _equal() for iterators and lists.    * o _values(), _search(), cast()    * o _sizeof()?, _indices()??    * o Support for reverse(), filter() and map().    * o Initialization from array.    * o Support for Pike.count_memory.    */    +  +  PIKEFUN int _size_object() +  { +  int q = THIS->num_elems; +  int res = q * sizeof(struct pike_list_node); +  struct mapping *m = NULL; +  struct pike_list_node *n = THIS->head; +  while( q-- ) +  { +  res += rec_size_svalue( &n->val, &m ); +  n = n->next; +  } +  if( m ) free_mapping( m ); +  RETURN res; +  } +     INIT    {    THIS->tail = NULL;    THIS->head = TAIL_SENTINEL(THIS);    THIS->tail_pred = HEAD_SENTINEL(THIS);    THIS->head_sentinel_refs = THIS->tail_sentinel_refs = 2;    THIS->num_elems = 0;    }       EXIT
pike.git/src/builtin.cmod:4247:    *!    *! 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:4308:    }       /*! @decl mixed tail()    *!    *! Get the element at the tail of the list.    *!    *! @throws    *! Throws an error if the list is empty.    *!    *! @seealso -  *! @[is_empty()], @[head()], @[pop()] +  *! @[is_empty()], @[head()], @[pop_back()]    */    PIKEFUN mixed tail()    { -  if (THIS->tail->prev) { -  push_svalue(&THIS->tail->val); +  struct pike_list_node * node = TAIL_SENTINEL(THIS); +  if (THIS->head->next) { +  push_svalue(&node->prev->val);    } else {    Pike_error("Empty list.\n");    }    }    -  +  static inline void pop_node(struct pike_list_node * node) { +  push_svalue(&node->val); +  if (node->refs == 2) { +  unlink_list_node(node); +  } else { +  detach_list_node(node); +  } +  THIS->num_elems--; +  } +     /*! @decl mixed pop()    *!    *! Pop the element at the head of the list from the list.    *!    *! @throws    *! Throws an error if the list is empty.    *!    *! @seealso -  *! @[is_empty()], @[head()], @[tail()] +  *! @[is_empty()], @[head()], @[tail()], @[pop_back()]    */    PIKEFUN mixed pop()    {    if (THIS->head->next) { -  push_svalue(&THIS->head->val); -  if (THIS->head->refs == 2) { -  unlink_list_node(THIS->head); +  pop_node(THIS->head);    } else { -  detach_list_node(THIS->head); +  Pike_error("Empty list.\n");    } -  THIS->num_elems--; +  } +  +  /*! @decl mixed pop_back() +  *! +  *! Pop the element at the tail of the list from the list. +  *! +  *! @throws +  *! Throws an error if the list is empty. +  *! +  *! @seealso +  *! @[is_empty()], @[head()], @[tail()], @[pop()] +  */ +  PIKEFUN mixed pop_back() +  { +  const struct pike_list_node * node = TAIL_SENTINEL(THIS); +  if (THIS->head->next) { +  pop_node(node->prev);    } else {    Pike_error("Empty list.\n");    }    }    -  +  /*! @decl array _values() +  *! +  *! Returns an array of elements in the list. +  */ +  PIKEFUN array _values() +  flags ID_PROTECTED; +  { +  struct array * a; +  push_int(THIS->num_elems); +  f_allocate(1); +  +  a = Pike_sp[-1].u.array; +  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 array cast(string type) +  *! +  *! Cast the lists. @expr{array@} is the only +  *! supported type. +  */ +  PIKEFUN array cast(string type) +  flags ID_PROTECTED; +  { +  pop_stack(); /* type as at least one more reference. */ +  if (type == literal_array_string) +  apply_current(f_List_cq__values_fun_num, 0); +  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"); +  +  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; +  } +  +  if (key->u.integer < 0) { +  node = TAIL_SENTINEL(THIS)->prev; +  while (n--) node = node->prev; +  } else { +  node = THIS->head; +  while (n--) node = node->next; +  } +  +  pop_n_elems(args); +  push_svalue(&node->val); +  } +     /*! @decl void append(mixed ... values)    *!    *! Append @[values] to the end of the list.    *!    *! @seealso    *! @[insert()]    */    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) +  unlink_list_node(THIS->head); +  else +  detach_list_node(THIS->head); +  } +  THIS->num_elems = 0; +  } +  } +     /*! @decl protected void create(mixed ... values)    *!    *! Create a new @[List], and initialize it with @[values]. -  *! -  *! @fixme -  *! Ought to reset the @[List] if called multiple times. +     */    PIKEFUN void create(mixed ... values)    flags ID_PROTECTED;    { -  /* FIXME: Reset the list? */ +  if (THIS->num_elems) +  apply_current(f_List_flush_fun_num, 0);    apply_current(f_List_append_fun_num, args);    }       /*! @class _get_iterator    *!    *! @[Iterator] that loops over the @[List].    */    PIKECLASS _get_iterator    program_flags PROGRAM_USES_PARENT;    flags ID_PROTECTED;
pike.git/src/builtin.cmod:4419:    /* NOTE: cur may never refer to an unlinked node.    * cur may however refer to a detached node, or to sentinels.    */       static struct List_struct *List__get_iterator_find_parent()    {    struct external_variable_context loc;       loc.o = Pike_fp->current_object;    loc.parent_identifier = Pike_fp->fun; -  loc.inherit = INHERIT_FROM_INT(loc.o->prog, loc.parent_identifier); +  loc.inherit = Pike_fp->context;    find_external_context(&loc, 1);    return (struct List_struct *)(loc.o->storage +    loc.inherit->storage_offset);    }       INIT    {    add_ref(THIS->cur = List__get_iterator_find_parent()->head);    THIS->ind = 0;    }
pike.git/src/builtin.cmod:4559:   #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:4639:    *! @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:4721:    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:4776:    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    */
pike.git/src/builtin.cmod:4864:   }      /*! @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:4900:   GET_VAL (false)   GET_VAL (null)      /* Kludge needed for the static null objects in the oracle module. It    * ought to be fixed to use dynamic lookup of them instead. */   PMOD_EXPORT struct program *get_sql_null_prog(void)   {    return Null_program;   }    + PIKECLASS __Backtrace_Tester__ + { +  INIT { +  Pike_error("__Backtrace_Tester__\n"); +  } + } +    void init_builtin(void)   { -  init_pike_list_node_blocks(); -  INIT +  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;   }      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.    */ -  free_all_pike_list_node_blocks(); +  ba_destroy(&pike_list_node_allocator);   #endif   #ifndef USE_SETENV    if (env_allocs) free_mapping (env_allocs);   #endif   }