/* 
   Copyright 1994-2003 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222
*/
/* Handles symbol tables 
 */
#include        <stdio.h>
#include				<malloc.h>
#include				<string.h>
#include        "lists.h"
#include        "expr.h"
#include        "c.h"
#include 				"errors.h"
#include				"lists.h"
#define ROTR(x,bits) (((x << (16 - bits)) | (x >> bits)) & 0xffff)
#define ROTL(x,bits) (((x << bits) | (x >> (16 - bits))) & 0xffff)
#define HASHTABLESIZE 1023

extern int infile ;
extern TABLE oldlsym;
extern int prm_cplusplus,prm_cmangle;
extern SYM *declclass, *typequal, *currentfunc ;
HASHREC **defhash=0;

LIST *orderednamespaces;
NAMESPACE *thisnamespace;
HASHREC **gsyms; /*everywhere else this is a table so I don't have to typecast */
TABLE lsyms,defsyms;
int unique_namespace_num ;

void symini(void)
{
	lsyms.head = lsyms.tail = defsyms.head = defsyms.tail = 0;
	if (!defhash) {
  	gsyms = (HASHREC **)malloc(HASHTABLESIZE * sizeof(HASHREC *));
  	defhash = (HASHREC **)malloc(HASHTABLESIZE * sizeof(HASHREC *));
	}
  memset(defhash,0,HASHTABLESIZE * sizeof(HASHREC *));
  memset(gsyms,0,HASHTABLESIZE * sizeof(HASHREC *));
   thisnamespace = 0;
	orderednamespaces = 0;
   unique_namespace_num = 1 ;
}
static void pushnamespace(NAMESPACE *q)
{
	LIST *list = xalloc(sizeof(LIST));
	list->link = orderednamespaces;
	list->data = q;
	orderednamespaces = list;
	thisnamespace = q;
}
static void popnamespace(void)
{
   if (orderednamespaces->link) {
      orderednamespaces = orderednamespaces->link ;
      thisnamespace = orderednamespaces->data;
   } else {
      orderednamespaces = 0 ;
      thisnamespace = 0 ;
   }
	
}
void switchtonamespace(char *name)
{
   NAMESPACE *q ;
   char buf[256] ;
   int isused = 0 ;
   SYM *s ;
   if (name == 0) {  
      int i = 0 ;
      name = infile ;
      buf[i++] = '_' ;
      buf[i++] = '_' ;
      while(*name) {
         if (isalnum(name))
            buf[i++] = *name++ ;
         else
            name++ ;
         sprintf(buf+i,"%d%d_",unique_namespace_num++,rand() %99) ;
      }
      isused = 1 ;
   } else
      strcpy(buf,name) ;
   s = search(buf,&gsyms) ;
   if (!s) {
      s = xalloc(sizeof(SYM)) ; 
      s->name = litlate(buf) ;
      s->storage_class = sc_namespace ;
      if (thisnamespace)
         s->parentclass = s->originalparentclass = thisnamespace->nsp ;
      q = xalloc(sizeof(NAMESPACE));
      q->hashsize = HASHTABLESIZE ;
      q->table = (HASHREC **)xalloc(HASHTABLESIZE * sizeof(HASHREC *));
      q->nsp = s ;
      q->autouse = isused ;
      s->enumlist = q ;
   } else
      q = s->enumlist ;
   pushnamespace(q);
}
void switchfromnamespace(void)
{
   NAMESPACE *q = thisnamespace ;
	popnamespace();
   if (q->autouse) ; // fixme
}



/* Sym tab hash function */	
static unsigned int ComputeHash(char *string,int size)
{
  unsigned int len = strlen(string), rv;
  char *pe = len + string;
  unsigned char blank = ' ';

  rv = len | blank;
  while(len--) {
    unsigned char cback = (unsigned char)(*--pe) | blank;
    rv = ROTR(rv,2) ^ cback;
  }
  return(rv % size);
}
/* Add a hash item to the table */
HASHREC *AddHash(HASHREC *item,HASHREC **table,int size)
{
  int index = ComputeHash(item->key,size);
  HASHREC **p;

  item->link = 0;

  if (*(p = &table[index])) {
    HASHREC *q = *p,*r = *p;
    while (q) {
			r = q;
      if (!strcmp(r->key,item->key))
				return(r);
			q = q->link;
		}
		r->link = item;
  }
  else
    *p = item;
  return(0);
}
/*
 * Find something in the hash table
 */
HASHREC **LookupHash(char *key, HASHREC **table, int size)
{
  int index = ComputeHash(key,size);
  HASHREC **p;

  if (*(p = &table[index])) {
    HASHREC *q= *p;
    while (q) {
      if (!strcmp(q->key, key))
				return(p);
			p = *p;
			q=q->link;
		}
	}
	return(0);
}
int isaccessible(SYM *sp)
{
#ifdef CPLUSPLUS
   if (!currentfunc)
      return TRUE ;
	if ((sp->value.classdata.cppflags & PF_MEMBER) && sp->parentclass) {
		if (sp->value.classdata.cppflags & PF_UNREACHABLE)
         return FALSE ;
		if (sp->parentclass != declclass) {
			if (declclass){
				if (!(sp->value.classdata.cppflags & (PF_PUBLIC | PF_PROTECTED)))
               return FALSE ;
			}else if (!(sp->value.classdata.cppflags & PF_PUBLIC))
            return FALSE ;
		}
	}
#endif
   return TRUE ;
}
/*
 * Some tables use hash tables and some use linked lists
 * This is the global symbol search routine
 */
SYM     *basesearch(char *na,TABLE *table, int checkaccess)
{
	SYM *thead = table->head;
   SYM **p, *sp;
	if (table == gsyms) {
		p=((SYM **)LookupHash(na,gsyms,HASHTABLESIZE));
		if (p) {
			p = *p;
//         if (checkaccess)
//            if (!isaccessible(p) && !isfriend(p,table)) 
//               genclasserror(ERR_NOTACCESSIBLE,sp->name);
		}
		return (SYM *) p;
	}
	else if (table == &defsyms) {
		p=((SYM **)LookupHash(na,defhash,HASHTABLESIZE));
		if (p) {
			p = *p;
//         if (checkaccess)
//            if (!isaccessible(p) && !isfriend(p,table)) 
//               genclasserror(ERR_NOTACCESSIBLE,sp->name);
		}
		return (SYM *) p;
	}
	else
	       while( thead != 0) {
               if(strcmp(thead->name,na) == 0) {
                        if (checkaccess)
                           if (!isaccessible(thead) && !isfriend(thead,table)) 
                              genclasserror(ERR_NOTACCESSIBLE,fullcppmangle(thead->tp->sp->parentclass,thead->name,0));
                      return thead;
								}
                thead = thead->next;
                }
        return 0;
}
SYM *search(char *na, TABLE *table)
{
	return basesearch(na,table,TRUE);
}
SYM *losearch(char *na)
{
	SYM *sp;
		sp = search(na,&lsyms);
	if (!sp && declclass)
		 sp = search(na,&declclass->tp->lst);
	return sp;
}
SYM *tcsearch(char *na, int checkaccess)
{
	SYM *sp=0;
	if (!typequal || !(sp = basesearch(na,&typequal->tp->lst,checkaccess))) ;
	return sp;
}
SYM     *gsearch(char *na)
{       SYM     *sp;
        if( (sp = losearch(na)) == 0)
                sp = search(na,gsyms);
        return sp;
}
/* The global symbol insert routine */
void insert(SYM *sp,TABLE *table)

{
   SYM *sp1 ;
	if (table == gsyms) {
		if (AddHash(sp,gsyms,HASHTABLESIZE))
			gensymerror(ERR_DUPSYM,sp->name);
	}
  else if (table == &defsyms) {
		AddHash(sp,defhash,HASHTABLESIZE);
	}
	else if (table == &lsyms) {
      SYM *thead = table->head,*qhead = 0,**oldhead = &table->head;
		/* Only check the current local block... */
	  while( thead != oldlsym.head) {
        if(strcmp(thead->name,sp->name) == 0) {
              qhead = thead;
					 		break;
               }  
        oldhead = &thead->next ;
        thead = thead->next;
        }
      if (qhead && !(qhead->value.classdata.cppflags & PF_INHERITED)) 
       gensymerror(ERR_DUPSYM,sp->name);
		else {
         if (qhead)
            *oldhead = qhead->next ;
		/* Putting local symbols in backwards */
      if( table->head == 0) {
        table->head = table->tail = sp;
      	sp->next = 0;
			}
      else    {
				sp->next = table->head;
				table->head = sp;
      }
		}
	}
   else if( (sp1 = search(sp->name,table)) == 0 || (sp1->value.classdata.cppflags & PF_INHERITED)) {
                if( table->head == 0)
                        table->head = table->tail = sp;
                else    {
                        table->tail->next = sp;
                        table->tail = sp;
                        }
                sp->next = 0;
                if (sp1) {
                  SYM*sp2 = table->head, **spo = &table->head ;
                  while (sp2) {
                     if (sp2 == sp1) {
                        *spo = sp2->next ;
                        break ;
                     }
                     spo = &sp2->next ;
                     sp2 = sp2->next ;
                  }
                }
                }
        else
                gensymerror(ERR_DUPSYM,sp->name);
}