lpc4/lib/
lpc4/lib/doc/efun/
lpc4/lib/doc/lfun/
lpc4/lib/doc/operators/
lpc4/lib/doc/simul_efuns/
lpc4/lib/doc/types/
lpc4/lib/etc/
lpc4/lib/include/
lpc4/lib/include/arpa/
lpc4/lib/obj/d/
lpc4/lib/save/
lpc4/lib/secure/
lpc4/lib/std/
lpc4/lib/std/living/
#include "global.h"
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <signal.h>
#include "array.h"
#include "interpret.h"
#include "object.h"
#include "comm1.h"
#include "exec.h"
#include "call_out.h"
#include "sent.h"
#include "main.h"
#include "stralloc.h"
#include "dynamic_buffer.h"
#include "hash.h"
#include "simulate.h"
#include "socket_efuns.h"
#include "simul_efun.h"
#include "operators.h"
#include "save_objectII.h"

/*
 * stralloc.c - string management.
 *
 * All strings are stored in an extensible hash table, with reference counts.
 * free_string decreases the reference count; if it gets to zero, the string
 * will be deallocated.  add_string increases the ref count if it finds a
 * matching string, or allocates it if it cant.  There is no way to allocate
 * a string of a particular size to fill later (hash wont work!), so you'll
 * have to copy things freom a static (or malloced and later freed) buffer -
 * that is, if you want to avoid space leaks...
 *
 * Current overhead (on a 32 bit system):
 *      next pointer: 4 bytes
 *      length:       4 bytes
 *      references:   4 bytes
 *      sum:         12 bytes + malloc overhead
 */

#ifdef DEBUG
/* #define ALERT "s" */
#endif

extern int d_flag;
#ifdef ALERT
void Alert(char *s)
{
  fprintf(stderr,"Alert: '%s' done.\n",s);
}
void Alert2(char *s)
{
  if(d_flag>6) Alert(s);
}
#endif


/*
 * there is also generic hash table management code, but strings can be shared
 * (that was the point of this code), will be unique in the table,
 * require a reference count, and are malloced, copied and freed at
 * will by the string manager.  Besides, I wrote this code first :-).
 * Look at htable.c for the other code.  It uses the Hash() function
 * defined here, and requires hashed objects to have a pointer to the
 * next element in the chain (which you specify when you call the functions).
 */

static int search_len = 0;
static int num_str_searches = 0;

#ifdef DEBUG
int refchecked=0;
#endif

/*
 * hash table - list of pointers to heads of string chains.
 * Each string in chain has a pointer to the next string and a
 * reference count (char *, int) stored just before the start of the string.
 * HTABLE_SIZE is in config.h, and should be a prime, probably between
 * 1000 and 5000.
 */

static char *base_table[HTABLE_SIZE];

/*
 * generic hash function.  This is probably overkill; I haven't checked the
 * stats for different prime numbers, etc.
 */

INLINE static unsigned int StrHash(const char *s,int len)
{
  return hashmem(s, len, 20) % HTABLE_SIZE;
}

/*
 * Looks for a string in the table.  If it finds it, returns a pointer to
 * the start of the string part, and moves the entry for the string to
 * the head of the pointer chain.
 */

static char *internal_findstring(const char *s,int len,int h)
{
  char * curr, *prev;

#ifdef MALLOC_DEBUG
  check_sfltable();
#endif

  curr = base_table[h];
  prev = 0;
  num_str_searches++;

  while (curr)
  {
    search_len++;
    if (len==SIZE(curr) && !MEMCMP(curr, s,len)) /* found it */
    {
      if (prev)	/* not at head of list */
      {
	NEXT(prev) = NEXT(curr);
	NEXT(curr) = base_table[h];
	base_table[h] = curr;
      }
#if defined(DEBUG) && defined(STRALLOC_REFS)
      if(REFS(curr)<1)
	fatal("String with no references.\n");
#endif
      return curr;		/* pointer to string */
    }
    prev = curr;
    curr = NEXT(curr);
  }
  return 0;		/* not found */
}

char *findstring(const char *foo)
{
  int l;
  char *s;
  l=strlen(foo);
  s=internal_findstring(foo,l,StrHash(foo,l));
#ifdef WARN
  if(foo==s)
    warn(3,"findstring on already shared string.\n");
#endif  
  return s;
}

/* this one looks for strings that was already shared only */
static char *find_shared_string(const char *s,int h)
{
  char * curr, *prev;

  curr = base_table[h];
  prev = 0;

  num_str_searches++;
  while(curr)
  {
    search_len++;
    if (curr == s) /* found it */
    {
      if (prev)   /* not at head of list */
      {	
	NEXT(prev) = NEXT(curr);
	NEXT(curr) = base_table[h];
	base_table[h] = curr;
      }

#if defined(DEBUG) && defined(STRALLOC_REFS)
      if(REFS(curr)<1)
	fatal("String with no references.\n");
#endif

      return curr;		/* pointer to string */
    }
    prev = curr;
    curr = NEXT(curr);
  }
  
return 0;			/* not found */
}

char *debug_findstring(const char *foo)
{
  return find_shared_string(foo,StrHash(foo,SIZE(foo)));
}

/* note that begin_shared_string expects the _exact_ size of the string,
 * not the maximum size
 */
char *begin_shared_string(int len)
{
  struct shared *t;
  t=(struct shared *)xalloc(len + sizeof(struct shared));
  t->size=len;
  return t->str;
}

/*
 * Make a space for a string.  This is rather nasty, as we want to use
 * alloc/free, but malloc overhead is generally severe.  Later, we
 * will have to compact strings...
 */

static char * alloc_new_string(const char *string,int len,int h)
{
  char *s;
  s=begin_shared_string(len);
  MEMCPY(s, string,len);
  s[len]=0;

#ifndef STRALLOC_GC
  REFS(s) = 0;

#ifdef DEBUG
  XREF(s)=0;
#endif
#endif
  NEXT(s) = base_table[h];
  base_table[h] = s;
  return s;
}

char *end_shared_string(char *s)
{
  int len,h;
  char *s2;

  len=SIZE(s);
  h=StrHash(s,len);
  
  if((s2=internal_findstring(s,len,h)))
  {
    free((char *)REFERENCE(s,));
    s=s2;
  }else{
    s[len]=0;
#ifndef STRALLOC_GC
    REFS(s) = 0;

#ifdef DEBUG
    XREF(s)=0;
#endif
#endif
    NEXT(s) = base_table[h];
    base_table[h] = s;
  }
#ifndef STRALLOC_GC
  REFS(s)++;

#ifdef DEBUG
  XREF(s)++;
#endif
#endif
  return s;
}

char *make_shared_string(const char *str)
{
  char * s;
  int len=strlen(str);
  int h=StrHash(str,len);

#ifdef ALERT
  if(!strcmp(str,ALERT)) Alert("make_shared_string");
#endif

  s = internal_findstring(str,len,h);
#ifdef WARN
  if(s==str) warn(2,"make_shared_string on already shared string.\n");
#endif  
  if (!s) s = alloc_new_string(str,len,h);
#ifndef STRALLOC_GC
  REFS(s)++;
#ifdef DEBUG
  XREF(s)++;
#endif
#endif
  return s;
}

char * make_shared_binary_string(const char *str,int len)
{
  char * s;
  int h=StrHash(str,len);

#ifdef ALERT
  if(!strcmp(str,ALERT)) Alert("make_shared_binary_string");
#endif

  s = internal_findstring(str,len,h);
#ifdef WARN
  if(s==str) warn(2,"make_shared_binary_string on already shared string.\n");
#endif  
  if (!s) s = alloc_new_string(str,len,h);
#ifndef STRALLOC_GC
  REFS(s)++;
#ifdef DEBUG
  XREF(s)++;
#endif
#endif
  return(s);
}

#ifdef DEBUG
int my_strlen2(const union storage_union u)
{
  if(debug_findstring(strptr2(u))!=strptr2(u))
    fatal("Non shared string to internal.\n");
  return u.string->size;
}

int my_strlen(const struct svalue *s)
{
  if(s->type!=T_STRING)
    fatal("Bad type to internal strlen.\n");

  return my_strlen2(s->u);
}
#endif

INLINE int my_string_is_equal(const struct svalue *a,const struct svalue *b)
{
#ifdef DEBUG
  if(a->type!=T_STRING)
    fatal("Bad type to internal strcmp.\n");
  if(b->type!=T_STRING)
    fatal("Bad type to internal strcmp.\n");
#endif
  return a->u.string == b->u.string;
}

INLINE int my_strcmp(const struct svalue *a,const struct svalue *b)
{
  int tmp;
#ifdef DEBUG
  if(a->type!=T_STRING)
    fatal("Bad type to internal strcmp.\n");
  if(b->type!=T_STRING)
    fatal("Bad type to internal strcmp.\n");
#endif
  if(my_string_is_equal(a,b)) return 0;

  if(my_strlen(a)>my_strlen(b))
  {
    tmp=MEMCMP(strptr(a),strptr(b),my_strlen(b));
    if(tmp) return tmp;
    return 1;
  }else if(my_strlen(a)<my_strlen(b)){
    tmp=MEMCMP(strptr(a),strptr(b),my_strlen(a));
    if(tmp) return tmp;
    return -1;
  }else{
    return MEMCMP(strptr(a),strptr(b),my_strlen(a));
  }
}

#ifndef copy_shared_string
char * copy_shared_string(char *str)
{
#ifdef ALERT
  if(!strcmp(str,ALERT)) Alert("copy_shared_string");
#endif

#ifdef DEBUG
   if(debug_findstring(str)!=str)
    fatal("Copy shared string on non shared string.\n");
#endif

#ifndef STRALLOC_GC
  REFS(str)++;
#ifdef DEBUG
  XREF(str)++;
#endif
#endif

  return(str);
}
#endif

INLINE static void terminally_free_string(char **prev,char *s)
{
  /* It will be at the head of the hash chain */
  *prev = NEXT(s);

  free((char *)REFERENCE(s,));
}

/*
 * free_string - reduce the ref count on a string.  Various sanity
 * checks applied, the best of them being that a add_string allocated
 * string will point to a word boundary + sizeof(char *)+sizeof(int),
 * since malloc always allocates on a word boundary.
 * On systems where a short is 1/2 a word, this gives an easy check
 * to see whather we did in fact allocate it.
 */

#ifdef DEBUG
void free_string(char *str)
{
#if defined(DEBUG) || defined(STRALLOC_REFS)
  char * s;
  int h;
#endif

#ifdef ALERT
  if(!strcmp(str,ALERT)) Alert("free_string");
#endif


#ifndef	DEBUG
#ifndef STRALLOC_GC
  if(--REFS(str)) return;
#endif

#ifdef STRALLOC_REFS
  h=StrHash(str,SIZE(str));
  s = find_shared_string(str,h); /* moves it to head of table if found */
#endif

#else

  h=StrHash(str,SIZE(str));
  s = find_shared_string(str,h); /* moves it to head of table if found */
  if (!s)
  {
    fatal("Free string: not found in string table! (%s)", str);
    return;
  }
  if (s != str)
  {
    fatal("Free string: string didnt hash to the same spot! (%s)", str);
    return;
  }

#ifndef STRALLOC_GC
  XREF(s)--;
  REFS(s)--;

  if (REFS(s)<0)
  {
    fatal("Freeing string too many times. (%s)", str);
    return;
  }
/*
  if(REFS(s) != XREF(s) && refchecked)
  {
    fprintf(stderr,"Freeing string with strange xrefs '%s'!\n",s);
  }
*/
  if (REFS(s) > 0) return;
#endif

#endif /* DEBUG */

#ifdef STRALLOC_REFS
  terminally_free_string(base_table+h,s);
#endif
}

#else

#ifndef STRALLOC_GC
INLINE void really_free_string(char *str)
{
  int h;
  h=StrHash(str,SIZE(str));
  terminally_free_string(base_table+h,find_shared_string(str,h));
}
#endif

#endif

/*
 * you think this looks bad!  and we didn't even tell them about the
 * GNU malloc overhead!  tee hee!
 */

char *add_string_status(int verbose)
{
  char b[200];

  init_buf();

  if (verbose)
  {
#ifndef STRALLOC_GC
    int allocd_strings=0,allocd_bytes=0;
#endif
    int num_distinct_strings=0,bytes_distinct_strings=0;
    int overhead_bytes=0;
    int e;
    char *p;
    for(e=0;e<HTABLE_SIZE;e++)
    {
      for(p=base_table[e];p;p=NEXT(p))
      {
	num_distinct_strings++;
	bytes_distinct_strings+=((SIZE(p)+3)&~3);
#ifndef STRALLOC_GC
	allocd_strings+=REFS(p);
	allocd_bytes=REFS(p)*((SIZE(p)+3)&~3);
#endif
      }
    }
    overhead_bytes=(sizeof(struct shared)-1)*num_distinct_strings;
    my_strcat("\nShared string hash table:\n");
    my_strcat("-------------------------\t Strings    Bytes\n");

#ifndef STRALLOC_GC
    sprintf(b,"Total asked for\t\t\t%8d %8d\n",
	    allocd_strings, allocd_bytes);
    my_strcat(b);
#endif
    sprintf(b,"Strings malloced\t\t%8d %8d + %d overhead\n",
	    num_distinct_strings, bytes_distinct_strings, overhead_bytes);
    my_strcat(b);
    sprintf(b,"Space actually required/total string bytes %d%%\n",
	    (bytes_distinct_strings + overhead_bytes)*100 / allocd_bytes);
    my_strcat(b);
  }
  sprintf(b,"Searches: %d    Average search length: %6.3f\n",
	  num_str_searches, (double)search_len / num_str_searches);
  my_strcat(b);
  return free_buf();
  /*  return(bytes_distinct_strings + overhead_bytes); */
}

/* only used in debug mode, or when stralloc uses garbage collect */
int ref_cycle=0;

#ifdef DEBUG
static void *string_to_find;
int check_ref_cycle=0;
#endif

#ifdef DEBUG
#define RETTYPE int
#define RETDECL RETTYPE ret=0
#define CCALL(X,Y) if X { ret=1; Y; }
#define RETURN return ret
#else
#define RETTYPE void
#define RETDECL
#define CCALL(X,Y) X
#define RETURN return
#endif
static RETTYPE check_svalues(struct svalue *s,int number);
static RETTYPE check_short_svalue(union storage_union *u,int type);

INLINE static RETTYPE check_string(char *c)
{
  char *t;
  RETDECL;
  if(c)
  {
    t=debug_findstring(c);
    if(!t) fatal("Shared string not shared.\n");
    if(t!=c) fatal("Shared string not same as one in hashtable.\n");
#ifdef DEBUG
    if(check_ref_cycle) RETURN;
    if(string_to_find)
    {
      if(string_to_find==c)
      {
	fprintf(stderr,"Found '%s'\n",c);
	ret=1;
      }
    }else
#endif
    {
#ifdef STRALLOC_GC
      if(SIZE(c)<0) SIZE(c)=-SIZE(c); /* mark */
#else
#ifdef DEBUG
      XREF(c)++;
      ret=0;
#endif
#endif
    } 
  }
  RETURN;
}

static RETTYPE check_array(struct vector *v)
{
  RETDECL;

#ifdef DEBUG
  if(v==string_to_find)
  {
    fprintf(stderr,"Array found...\n");
    ret=1;
  }
  if(v->ref<1) fatal("Zero refs array.\n");
  if(v->size>MAX_ARRAY_SIZE && !batch_mode)
    fatal("Too large array.\n");

  if(v->malloced_size<v->size)
    fatal("Impossible array.\n");

  if(check_ref_cycle)
  {
    if((v->flags & O_REF_CYCLE) != ref_cycle)
      fatal("Error in ref cycle of program.\n");
    if(!v->extra_ref) RETURN;
    v->extra_ref=0;
  }else
#endif
  {
#ifdef DEBUG
    if(!string_to_find)
      v->extra_ref++;
#endif
    if((v->flags & O_REF_CYCLE) == ref_cycle) RETURN;
    v->flags&=~O_REF_CYCLE;
    v->flags|=ref_cycle;
  }

  if(v->size>0 && v->item[0].type==T_ALIST_PART)
    check_alist_for_destruct(v);

  CCALL((check_svalues(v->item,v->size)), );
  RETURN;
}

static RETTYPE check_program(struct program *p)
{
  int e;
  RETDECL;

  if(!p) RETURN;

#ifdef DEBUG
  if(p==string_to_find)
  {
    fprintf(stderr,"Program found...\n");
    ret=1;
  }
  if(check_ref_cycle)
  {
    if((p->flags & O_REF_CYCLE) != ref_cycle)
      fatal("Error in ref cycle of program.\n");
    if(!p->extra_ref) RETURN;
    p->extra_ref=0;
  }else
#endif
  {
#ifdef DEBUG
    if(!string_to_find)
      p->extra_ref++;
#endif
    if((p->flags & O_REF_CYCLE) == ref_cycle) RETURN;
    p->flags&=~O_REF_CYCLE;
    p->flags|=ref_cycle;
  }

  CCALL((check_string(p->name)),fprintf(stderr,"Was the name of a program.\n"));

  for(e=0;e<p->num_variables;e++)
  {
    CCALL((check_string(p->variable_names[e].name)),fprintf(stderr,"It's the name of a variable in %s.\n",p->name));
  }

  for(e=0;e<p->num_functions;e++)
  {
    CCALL((check_string(p->functions[e].name)),fprintf(stderr,"It's the name of a function in %s.\n",p->name));
  }

  for(e=0;e<p->num_strings;e++)
  {
    CCALL((check_string(p->strings[e])),fprintf(stderr,"It's a string in %s.\n",p->name));
  }

  if(p->inherit[0].prog!=p) fatal("p->inherited[0] != p.\n");

  for(e=1;e<p->num_inherited;e++)
  {
    CCALL((check_program(p->inherit[e].prog)),fprintf(stderr,"Was inherited by '%s'.\n",p->name));
  }

  for(e=0;e<p->num_switch_mappings;e++)
  {
    CCALL((check_array(p->switch_mappings[e])),fprintf(stderr,"Was in a switch mapping of %s.\n",p->name));
  }

  CCALL((check_svalues(p->constants,p->num_constants)),fprintf(stderr,"Was in a program constant of %s.\n",p->name));
  RETURN;
}

static RETTYPE check_object(struct object *ob)
{
  int e;
  RETDECL;

  if(!ob) RETURN;
#ifdef DEBUG
  if(ob==string_to_find)
  {
    fprintf(stderr,"Object found...\n");
    ret=1;
  }
  if(check_ref_cycle)
  {
    if((ob->flags & O_REF_CYCLE) != ref_cycle)
      fatal("Error in ref cycle of object.\n");
    if(!ob->extra_ref) return 0;
    ob->extra_ref=0;
  }else
#endif
  {
#ifdef DEBUG
    if(!string_to_find)
      ob->extra_ref++;
#endif
    if((ob->flags & O_REF_CYCLE) == ref_cycle) RETURN;
    ob->flags&=~O_REF_CYCLE;
    ob->flags|=ref_cycle;
  }

  if(ob->flags & O_DESTRUCTED) RETURN;

  for(e=0;e<ob->prog->num_variables;e++)
  {
    CCALL((check_short_svalue(ob->variables+e, ob->prog->variable_names[e].rttype)),fprintf(stderr,"Found in variable\n"));
  }

  CCALL((check_string(ob->obj_index)),fprintf(stderr,"It's the index of an object.\n"));
  CCALL((check_string(ob->user)),fprintf(stderr,"Found in uid.\n"));
  CCALL((check_string(ob->eff_user)),fprintf(stderr,"Found in euid.\n"));

  if(ob->sentences)
  {
    for(e=0;e<ob->sentences->slots;e++)
    {
      if(!ob->sentences->sent[e].match) continue;
      
      CCALL((check_string(ob->sentences->sent[e].match)),fprintf(stderr,"It's match in an add_action\n"));
      CCALL((check_svalues(&(ob->sentences->sent[e].function),1)),fprintf(stderr,"It's the function name in an add_action\n"));
    }
  }
  CCALL((check_program(ob->prog)),fprintf(stderr,"Found in program %s\n",ob->prog->name));

  RETURN;
}

static RETTYPE check_svalues(struct svalue *s,int number)
{
  int e;
  RETDECL;

  for(e=0;e<number;e++,s++)
  {
    switch(s->type)
    {
    case T_STRING:
      CCALL((check_string(strptr(s))),fprintf(stderr,"In shared string svalue.\n"));
      break;

    case T_MAPPING:
    case T_LIST:
    case T_POINTER:
    case T_ALIST_PART:
      CCALL((check_array(s->u.vec)),
	switch(s->type)
	{
	case T_MAPPING: fprintf(stderr,"In mapping.\n"); break;
	case T_LIST:  fprintf(stderr,"In list.\n"); break;
	case T_POINTER: fprintf(stderr,"In array.\n"); break;
	case T_ALIST_PART: fprintf(stderr,"In alist part.\n"); break;
	}
	    );
      break;

    case T_LVALUE:
      CCALL((check_svalues(&(s->u.lvalue->ind),1)),fprintf(stderr,"Found in lvalue index\n"));

    case T_OBJECT:
    case T_FUNCTION:
      CCALL((check_object(s->u.ob)),fprintf(stderr,"Found in object %s\n",s->u.ob->prog->name));
      if(s->u.ob->flags & O_DESTRUCTED)
      {
	short tmp;
	tmp=s->type=T_OBJECT;
        free_svalue(s);
	s->subtype=tmp?NUMBER_DESTRUCTED_OBJECT:NUMBER_DESTRUCTED_FUNCTION;
      }
    }
  }
  RETURN;
}

static RETTYPE check_short_svalue(union storage_union *u,int type)
{
  RETDECL;

  if(u->number)
  {
    switch(type)
    {
    case T_ANY:
      CCALL((check_svalues((struct svalue *)u,1)),);
    case T_NOTHING:
      break;

    case T_STRING:
      CCALL((check_string(strptr2(*u))),fprintf(stderr,"In shared string svalue.\n"));
      break;

    case T_MAPPING:
    case T_LIST:
    case T_POINTER:
    case T_ALIST_PART:
      CCALL((check_array(u->vec)),
	    switch(type)
	  {
	  case T_MAPPING: fprintf(stderr,"In mapping.\n"); break;
	  case T_LIST:  fprintf(stderr,"In list.\n"); break;
	  case T_POINTER: fprintf(stderr,"In array.\n"); break;
	  case T_ALIST_PART: fprintf(stderr,"In alist part.\n"); break;
	  }
	    );
      break;

    case T_OBJECT:
    case T_FUNCTION:
      CCALL((check_object(u->ob)),fprintf(stderr,"Found in object %s\n",u->ob->prog->name));
      if(u->ob->flags & O_DESTRUCTED)
      {
	free_short_svalue(u,type);
      }
    }
  }
  RETURN;
}

static RETTYPE check_str_ref_from_call_outs()
{
  int e;
  RETDECL;
  for(e=0;e<num_pending_calls;e++)
  {
    CCALL((check_object(pending_calls[e]->command_giver)),fprintf(stderr,"Was command giver in call_out.\n"));
    CCALL((check_array(pending_calls[e]->args)),fprintf(stderr,"Was in argument list for call_out.\n"));
  }
 RETURN;
}

extern struct svalue start_of_stack[EVALUATOR_STACK_SIZE];
extern struct lpc_socket lpc_socks[MAX_EFUN_SOCKS];
extern struct svalue *sp;
extern struct object *obj_list;
extern struct object *obj_list_destruct;
extern char **inc_list;
extern int inc_list_size;
extern struct svalue ret_value;
extern struct program *obj_table[OTABLE_SIZE];
extern struct keyword predefs[],reswords[];
extern int real_number_predefs,number_reswords;
extern char *last_file;
extern struct object *master_ob;
extern struct vector null_vector;
extern int batch_mode;

static void check_even_more_ref_counts2()
{
  int e,q;
  struct object *ob;
  struct program *p;
#ifdef DEBUG
  struct vector *v;
#endif
  RETDECL;

  check_str_ref_from_call_outs();
  q=sp-start_of_stack+1;

  for(e=0;e<num_lfuns;e++)
  {
    CCALL((check_string(lfuns[e])),fprintf(stderr,"Was an lfun\n"));
  }
  
  CCALL((check_svalues(start_of_stack,q)),fprintf(stderr,"Was found on stack\n"));

#ifdef YYOPT
  for(e=0;e<real_number_predefs;e++)
  {
    CCALL((check_string(predefs[e].word)),fprintf(stderr,"Was efun name\n"));
  }

  for(e=0;e<number_reswords;e++)
  {
    CCALL((check_string(reswords[e].word)),fprintf(stderr,"Was a reserved word\n"));
  }
#endif
  for(e=0;e<OTABLE_SIZE;e++)
  {
    for(p=obj_table[e];p;p=p->next_hash)
    {
      CCALL((check_program(p)),fprintf(stderr,"Found in prog %s\n",p->name));
    }
  }

  for(ob=obj_list; ob; ob = ob->next_all)
  {
    CCALL((check_object(ob)),fprintf(stderr,"Found in object %s\n",ob->prog->name));
  }

  for(ob=obj_list_destruct; ob; ob = ob->next_all)
  {
    CCALL((check_object(ob)),fprintf(stderr,"Found in destructed object %s\n",ob->prog->name));
  }

  CCALL((check_object(master_ob)),fprintf(stderr,"Found master object\n"));

  for(e=0;e<inc_list_size;e++)
  {
    CCALL((check_string(inc_list[e])),fprintf(stderr,"Found in include path.\n"));
  }

  for(e=0;e<num_simul_efuns;e++)
  {
    CCALL((check_string(simul_efuns[e].name)),fprintf(stderr,"Is the name of a simul_efun.\n"));
    CCALL((check_svalues(&(simul_efuns[e].fun),1)),fprintf(stderr,"Found in a simul efun svalue.\n"));
  }

  CCALL((check_svalues(&ret_value,1)),fprintf(stderr,"Was last returned value.\n"));
    
  CCALL((check_svalues(&const_empty_string,1)),fprintf(stderr,"Was the empty string.\n"));
  CCALL((check_string(hostname)),fprintf(stderr,"Was hostname.\n"));
  CCALL((check_string(last_file)),fprintf(stderr,"Was filename of last database.\n"));

#ifdef ADD_CACHE_SIZE
  for(e=0;e<ADD_CACHE_SIZE;e++)
  {
    CCALL((check_string(add_cache[e].a)),fprintf(stderr,"Was in add cache.\n"));
    CCALL((check_string(add_cache[e].b)),fprintf(stderr,"Was in add cache.\n"));
    CCALL((check_string(add_cache[e].sum)),fprintf(stderr,"Was in add cache.\n"));
  }
#endif
  for(e=0;e<MAX_EFUN_SOCKS;e++)
  {
    CCALL((check_svalues(&(lpc_socks[e].read_callback),1)),fprintf(stderr,"Was read callback svalue.\n"));
    CCALL((check_svalues(&(lpc_socks[e].write_callback),1)),fprintf(stderr,"Was write callback svalue.\n"));
    CCALL((check_svalues(&(lpc_socks[e].close_callback),1)),fprintf(stderr,"Was close callback svalue.\n"));
  }
  CCALL((check_array(&null_vector)),fprintf(stderr,"Was null vector.\n"));
#ifdef DEBUG
  for(v=&null_vector;v;v=v->next)
  {
    CCALL((check_array(v)),fprintf(stderr,"Was in an array.\n"));
  }
#endif
}

static void check_even_more_ref_counts()
{
  if(ref_cycle)
  {
    ref_cycle=0;
  }else{
    ref_cycle=O_REF_CYCLE;
  }
  check_even_more_ref_counts2();
}


static last_gc;

void stralloc_gc(int force)
{
#if !defined(STRALLOC_REFS) || defined(DEBUG)
  char *c;
  int e;
#endif
  extern int current_time;
#ifdef DEBUG
  struct program *p;
  struct object *ob;
  struct vector *v;
#endif

  if(d_flag<3 && !force && current_time-last_gc>STRALLOC_GC_TIME) return;
  last_gc=current_time;
#ifdef ADD_CACHE_SIZE
  clean_add_cache();
#endif
#ifdef DEBUG
  for(e=0;e<OTABLE_SIZE;e++)
    for(p=obj_table[e];p;p=p->next_hash)
      p->extra_ref=-1;

  for(ob=obj_list; ob; ob = ob->next_all)
    ob->extra_ref=-1;

  for(v=&null_vector; v; v = v->next)
    v->extra_ref=-1;

  check_ref_cycle=1;
  check_even_more_ref_counts2();
  check_ref_cycle=0;

  string_to_find=0;
  refchecked=1;
#endif

#ifdef STRALLOC_GC
  for(e=0;e<HTABLE_SIZE;e++)
  {
    char **prev;
    for(prev=base_table+e;(c=*prev);prev=&(NEXT(c)))
      SIZE(c)=-SIZE(c);
  }
#endif

#if defined(DEBUG) && !defined(STRALLOC_GC)
  for(e=0;e<HTABLE_SIZE;e++)
  {
    char **prev;
    for(prev=base_table+e;(c=*prev);prev=&(NEXT(c)))
      XREF(c)=0;
  }
#endif

#if !defined(STRALLOC_GC) && !defined(DEBUG)
  if(force)
#endif
    check_even_more_ref_counts();

#ifdef DEBUG

  for(e=0;e<OTABLE_SIZE;e++)
  {
    for(p=obj_table[e];p;p=p->next_hash)
    {
      if(p->extra_ref!=p->ref)
      {
        fprintf(stderr,"Warning: program '%s' had %d refs, expected %d\n",p->name,p->ref,p->extra_ref);
	string_to_find=p;
	check_even_more_ref_counts();
	if(d_flag>4) fatal("Error in object refs.\n");
      }
    }
  }


  for(ob=obj_list; ob; ob = ob->next_all)
  {
    if(ob->extra_ref!=ob->ref)
    {
      fprintf(stderr,"Warning: object %s#%d had %d refs, expected %d\n",ob->prog->name,ob->clone_number,ob->ref,ob->extra_ref);
      string_to_find=ob;
      check_even_more_ref_counts();
      if(d_flag>4) fatal("Error in object refs.\n");
    }
  }

  for(v=&null_vector; v; v = v->next)
  {
    if(v->extra_ref-1!=v->ref)
    {
      fprintf(stderr,"Warning: array had %d refs, expected %d\n",v->ref,v->extra_ref-1);
      init_buf();
      my_strcat("({");
      debug_save_svalue_list(v);
      my_strcat("})");
      fprintf(stderr,"%s\n",return_buf());
      string_to_find=v;
      check_even_more_ref_counts();
      if(d_flag>4) fatal("Error in array refs.\n");
    }
  }


#ifndef STRALLOC_GC
  for(e=0;e<HTABLE_SIZE;e++)
  {
    for(c=base_table[e];c;c=NEXT(c))
    {
      if(XREF(c)!=REFS(c))
      {
        fprintf(stderr,"Warning: '%s' had %d refs, expected %d\n",c,REFS(c),XREF(c));
	string_to_find=c;
	check_even_more_ref_counts();
	if(d_flag>4) fatal("Error in string refs.\n");
      }
    }
  }
#endif
#endif


#ifndef STRALLOC_REFS
  for(e=0;e<HTABLE_SIZE;e++)
  {
    char **prev;
    for(prev=base_table+e;(c=*prev);prev=&(NEXT(c)))
    {
#ifdef STRALLOC_HYBRID
      if(!REFS(c))
#else
      if(SIZE(c)<0)
#endif
      {
	terminally_free_string(prev,c);
      }else{
	prev=&(NEXT(c));
      }
    }
  }
#endif
}

void not_backend_stralloc_gc()
{
  /* we can't run gc here because pointers might be in local
   * variables and such. *sigh*
   */

  last_gc=0;
}

void dump_stralloc_strings()
{
  int e;
  char *p;
  for(e=0;e<HTABLE_SIZE;e++)
    for(p=base_table[e];p;p=NEXT(p))
      printf("%d refs \"%s\"",REFS(p),p);
}