/* 
   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 name mangling
 */
#include        <stdio.h>
#include				<malloc.h>
#include				<ctype.h>
#include				<string.h>
#include        "lists.h"
#include        "expr.h"
#include        "c.h"
#include 				"errors.h"

extern TABLE oldlsym,lsyms,*gsyms;
extern int prm_cplusplus,prm_cmangle;
extern SYM *declclass;
extern SYM *typequal;

#define HASHTABLESIZE 1023

#ifdef CPLUSPLUS
char *tn_void = "void ";
char *tn_char = "char ";
char *tn_int = "int ";
char *tn_long = "long long " ;
char *tn_short = "short ";
char *tn_unsigned = "unsigned ";
char *tn_ellipse = "... ";
char *tn_float = "float ";
char *tn_double = "double ";
char *tn_longdouble = "long double ";
char *tn_vol = "volatile ";
char *tn_const = "const ";
char *tn_class = "class ";
char *tn_struct = "struct ";
char *tn_union = "union ";
char *tn_fcomplex = "float complex ";
char *tn_rcomplex = "double complex ";
char *tn_lrcomplex = "long double complex ";

char *cpp_funcname_tab[] = {
	"$octr",
  "$odtr",
  "$onew",
  "$odel",
	"$oadd","$osub","$omul","$odiv","$oshl","$oshr","$omod","$oequ","$oneq",
	"$olt","$oleq","$ogt","$ogeq","$oasn","$oasadd","$oassub","$oasmul","$oasdiv",
	"$oasmod","$oasshl","$osasshr","$oasand","$oasor","$oasxor","$oasinc","$oasdec",
	"$oarray","$ocall","$opstar","$oarrow","$olor",
	"$oland","$onot","$oor","$oand","$oxor","$ocpl",
   "$onwa","$odla",
  
};
char *xlate_tab[] = {
	0,0,"new","delete","+","-","*","/","<<",">>","%","==","!=",
	"<","<=",">",">=","=","+=","-=","*=","/=",
	"%=","<<=",">>=","&=","|=","^=","++","--",
	"[]","()","->*","->","||","&&","!","|","&","^","~",
   "new[]","delete[]"
};
#define IT_THRESHOLD 2
#define IT_OV_THRESHOLD 4
#define IT_SIZE (sizeof(cpp_funcname_tab)/sizeof(char *))
#endif /* CPLUSPLUS */
 
static char *cppargs(char *buf,SYM *sp);
static char * unmangcppfunc(char *buf, char *name, int firsttime);


#ifdef CPLUSPLUS
static void cpp_unmang_intrins(char **buf, char **name, char *last)
{
	char cur[40],*p=cur,*q;
	int i;
	*p++ = *(*name)++;
	while (**name != '@' && **name != '$' && **name)
		*p++ = *(*name)++;
	*p = 0;
	for(i=0; i < IT_SIZE; i++)
		if (!strcmp(cur,cpp_funcname_tab[i]))
			break;
	if (i >= IT_SIZE)
		strcpy(*buf,cur);
	else {
		if (i < IT_THRESHOLD) {
			switch (i) {
				case 1:
					*(*buf)++ = '~';
				case 0:
					strcpy(*buf,last);
					break;
			}
		}
		else {
				strcpy(*buf,"operator ");
				strcat(*buf,xlate_tab[i]);
		}
	}
	*buf += strlen(*buf);
		
}
#endif
/* Insert an overloaded function ref into the function table
 */
void funcrefinsert(char *name, SYM *refsym, TABLE *tbl, SYM *insp)
{
#ifdef CPLUSPLUS
	char buf[100];
	SYM *sp,*sp1;
	sp1 = search(name,tbl);
   if (sp1)
      insert(refsym,&sp1->tp->lst) ;
   else {
      sp = xalloc(sizeof(SYM));
      sp->storage_class = sc_defunc;
      sp->name = litlate(name);
      sp->tp = maketype(bt_defunc,0);
      sp->tp->sp = sp;
      sp->tp->lst.head = sp->tp->lst.tail = refsym;
      sp->parentclass = insp;
		insert(sp,tbl);
   }
#endif
}
#ifdef CPLUSPLUS
int matchone(SYM *sp1, SYM *sp2)
{
	int rv = 1;
	while (sp1 && sp1 != (SYM *)-1 && sp2 && sp2 != (SYM *)-1) {
		if (!exactype(sp1->tp,sp2->tp)) {
			TYP *typ1 = sp1->tp;
			TYP *typ2 = sp2->tp;
			if (typ1->type == bt_ref)
				typ1 = typ1->btp;
			if (typ2->type == bt_ref)
				typ2 = typ1->btp;
			if (isscalar(typ1) && isscalar(typ2))
				rv = 2;
			else
				return 0;
		}
		sp1 = sp1->next;
		sp2 = sp2->next;
	}
	/* If we get here one of the lists has quit.  If it isn't sp2
   * or if there is no defalt value, bail
   */
	if (sp1 == (SYM *)-1 || sp1 == 0)
		if (sp2 == 0 || sp2 == (SYM *)-1)
			return rv;
  if (sp2)
		return 0;
	if (sp1 && sp1 != (SYM *) -1 && sp1->value.classdata.defalt)
		return rv;
	return 0;
}
#endif
/* Search the tables looking for a match for an argument type list */
SYM *funcovermatch(SYM *tbsym, TYP *tp, int exact)
{
#ifdef CPLUSPLUS
   SYM *sp1 = tbsym->tp->lst.head;
	SYM *sp2 = 0;
	SYM *sp3=0, *sp4=0;
	while (sp1) {
      switch(matchone(sp1->tp->lst.head,tp->lst.head)) {
			case 0:
			default:
				break;
			case 1:
				if (sp2) {
               genfunc2error(ERR_AMBIGFUNC,sp1->name,sp2->name);
					return sp2 ;
				}
            else sp2 = sp1;
				break;
			case 2:
				if (!sp3)
               sp3 = sp1;
				else
					if (!sp4)
                  sp4 = sp1;
				break;
		}
		sp1 = sp1->next;
	}
   if (sp2 || exact)
		return sp2;
	if (sp3 && sp4)
		genfunc2error(ERR_AMBIGFUNC,sp3->name,sp4->name);
	return sp3;
#endif
}
#ifdef CPLUSPLUS
/* Mangle one C++ argument */
static void mname(char *buf, SYM *sp)
{
	char *v;
	if (!sp)
		return;
	if (sp->parentclass) {
		mname(buf,sp->parentclass);
		strcat(buf,"@");
		buf = buf + strlen(buf);
	}
	v = sp->name;
	if (prm_cmangle)
		v++;
	strcpy(buf,v);
}
static char * putvc(char *buf, TYP *tp)
{
	if (tp->cflags & DF_CONST)
		*buf++ = 'x';
	if (tp->cflags & DF_VOL)
		*buf++ = 'y';
	return buf;
}
static char *cpponearg(char *buf,TYP *tp)
{
	char nm[200];
start:
	if (tp->type != bt_pointer || tp->btp->type != bt_func)
		buf = putvc(buf,tp);
	switch (tp->type) {
		case bt_memberptr:
			*buf++ = 'M';
			mname(nm,tp->sp);
			sprintf(buf,"%d%s",strlen(nm),nm);
			buf = buf + strlen(buf);
			break;
		case bt_struct:
		case bt_union:
		case bt_class:
		case bt_enum:
			mname(nm,tp->sp);
			sprintf(buf,"%d%s",strlen(nm),nm);
			buf = buf + strlen(buf);
			break;
		case bt_bool:
			strcpy(buf,"4bool");
			buf+=5;
			break;
		case bt_unsignedshort:
			*buf++ = 'u';
		case bt_short:
			*buf++ = 's';
			break;
		case bt_unsigned:
			*buf++ = 'u';
		case bt_int:
			*buf++ = 'i';
			break;
		case bt_unsignedlong:
			*buf++ = 'u';
		case bt_long:
         *buf++ = 'l';
			break;
		case bt_unsignedchar:
			*buf++ = 'u';
		case bt_char:
			*buf++ = 'c';
			break;
      case bt_fcomplex:
         *buf++ = 'F' ;
         break ;
      case bt_rcomplex:
         *buf++ = 'R' ;
         break ;
      case bt_lrcomplex:
         *buf++ = 'G' ;
         break ;
		case bt_float:
			*buf++ = 'f';
			break;
		case bt_double:
			*buf++ = 'd';
			break;
		case bt_longdouble:
			*buf++ = 'g';
			break;
		case bt_pointer:
			if (tp->btp->type == bt_func) {
				buf = putvc(buf,tp->btp);
				*buf++ = 'q';
				buf = cppargs(buf,tp->lst.head);
				*buf++ = '$';
				tp = tp->btp->btp;
			} else {
				*buf++ = 'p';
				tp = tp->btp;
			}
			goto start;
		case bt_ref:
			*buf++ = 'r';
			tp = tp->btp;
			goto start;
		case bt_ellipse:
			*buf++ = 'e';
			break;
		case bt_void:
			*buf++ = 'v';
			break;
	}
	*buf = 0;
	return buf;
}
/* Mangle an entire C++ function */
static char *cppargs(char *buf,SYM *sp)
{
	if (sp == (SYM *)-1) {
		*buf++ = 'v';
	}
	else while (sp) {
		buf = cpponearg(buf,sp->tp);
		sp = sp->next;
	}
	*buf=0;
	return buf;
}
/* Wrapper for function name mangling */
char * cppmangle(char *name, TYP *tp)
{
	char buf[100],*p=buf;
	if (*name == 0)
		return 0;
	if (prm_cmangle && *name == '_')
		name++;
	if (tp) {
		sprintf(p,"@%s$",name);
		p = p + strlen(p);
		p = putvc(p,tp);
		*p++ = 'q';
		cppargs(p,tp->lst.head);
	}
	else
		sprintf(p,"@%s",name);
	return(litlate(buf));
}
static void mangleclass(char *buf, SYM *sl)
{
	char *p;
	if (!sl)
		return;
	mangleclass(buf,sl->parentclass);
	p = buf + strlen(buf);
	*p++ = '@';
	if (prm_cmangle)
		strcpy(p,sl->name+1);
	else
		strcpy(p,sl->name);
}
/* Wrapper for function name mangling */
char * fullcppmangle(SYM *class, char *name, TYP *tp)
{
	char buf[100],*p=buf;
	*p = 0;
	if (*name == 0)
		return 0;
	if (prm_cmangle && *name == '_')
		name++;
	if (class)
		mangleclass(buf,class);
	else if (typequal)
		mangleclass(buf,typequal);
	else if (declclass)
		mangleclass(buf,declclass);
	p = buf + strlen(buf);
	if (tp && (tp->type == bt_func || tp->type == bt_ifunc)) {
		sprintf(p,"@%s$",name);
		p = p + strlen(p);
		p = putvc(p,tp);
		*p++ = 'q';
		cppargs(p,tp->lst.head);
	}
	else
		sprintf(p,"@%s",name);
	return(litlate(buf));
}
	
			
/* Argument unmangling for C++ */
static char *unmang1(char *buf, char *name, int firsttime)
{
	int v;
	int cvol = 0, cconst = 0;
	while (*name == 'x' || *name == 'y') {
		if (*name == 'y')
			cvol++;
		if (*name == 'x')
			cconst++;
		name++;
	}
start:
		if (isdigit(*name)) {
			v = *name++ - '0';
			while (isdigit(*name)) 
				v = v*10+ *name++ - '0';
			while (v--) 
				*buf++ = *name++;
			*buf = 0;
		}
		else switch (*name++) {
			case 'q': {
					if (!firsttime) {
						strcpy(buf," (*) ");
						buf+=5;
					}
				  name = unmangcppfunc(buf,name,FALSE);
				}
				break;	
			case 'u':
				strcpy(buf,"unsigned ");
				buf = buf+9;
				switch(*name++) {
					case 'i':
						strcpy(buf,tn_int);
						break;
					case 'l':
						strcpy(buf,tn_long);
						break;
					case 's':
						strcpy(buf,tn_short);
						break;
					case 'c':
						strcpy(buf,tn_char);
						break;
				}
				break;
			case 'M':
				v = *name++ - '0';
				while (isdigit(*name)) 
					v = v*10+ *name++ - '0';
				while (v--) {
					if (*name == '@') {
						name++;
						*buf++ = ':';
						*buf++ = ':';
					} else
						*buf++ = *name++;
					*buf = 0;
				}
				strcpy(buf,"::*");
				buf = buf + strlen(buf);
				break;
			case 'v':
				strcpy(buf,tn_void);
				break;
         case 'F':
            strcpy(buf,tn_fcomplex);
            break;
         case 'D':
            strcpy(buf,tn_rcomplex);
            break;
         case 'G':
            strcpy(buf,tn_lrcomplex);
            break;
			case 'f':
				strcpy(buf,tn_float);
				break;
			case 'd':
				strcpy(buf,tn_double);
				break;
			case 'g':
				strcpy(buf,tn_longdouble);
				break;
			case 'i':
				strcpy(buf,tn_int);
				break;
			case 'l':
				strcpy(buf,tn_long);
				break;
			case 's':
				strcpy(buf,tn_short);
				break;
			case 'c':
				strcpy(buf,tn_char);
				break;
			case 'p':
				name = unmang1(buf, name,FALSE);
				buf = buf + strlen(buf);
				*buf++='*';
				*buf++=' ';
				*buf = 0;
				break;
			case 'r':
				name = unmang1(buf, name,FALSE);
				buf = buf + strlen(buf);
				*buf++='&';
				*buf++=' ';
				*buf = 0;
				break;
			case 'e':
				strcpy(buf,tn_ellipse);
				break;
			case '$':
				name--;
				return name;
		}
	if (cconst)
		strcat(buf, tn_const);
	if (cvol)
		strcat(buf, tn_vol);
	return name;
}
/* Unmangle an entire C++ function */
static char * unmangcppfunc(char *buf, char *name, int firsttime)
{
	int i;
	*buf++ = '(';
	while (*name && *name != '$') {
		name = unmang1(buf,name,firsttime);
		buf = buf+strlen(buf);
		if (*name && *name != '$') {
			*buf++ = ',';
			*buf++ = ' ';
		}	
		else {
			*buf++ = ')';	
			*buf++ = ' ';	
		}
	}
	if (*name && *name == '$')
		name++;
	*buf=0;
	return name;
}
static void xlate_cppname(char **buf, char **name, char *lastname)
{
	char classname[40],*p=classname;
	*p = 0;
	if (**name == '@')
		(*name)++;
	if (**name == '$')
		cpp_unmang_intrins(buf,name,lastname);
	else {
		while (**name != '$' && **name) {
			if (**name == '@') {
				*(*buf)++ = ':';
				*(*buf)++ = ':';
				*p = 0;
		 		xlate_cppname(buf,name,classname);
			}
			else *p++ = *(*buf)++ = *(*name)++;
		}
	}
	*p = 0;
}
#endif
/* Name unmangling in general */
void unmangle(char *buf, char *name)
{
	char classname[40];
	classname[0] = 0;
	if (name[0] == '_' && prm_cmangle) {
		strcpy(buf,&name[1]);
	}
#ifdef CPLUSPLUS
	else 
		if (name[0] != '@')
			strcpy(buf,name);
		else {
			xlate_cppname(&buf,&name,0);
			if (*name == '$') {
				name++;
				unmang1(buf,name,TRUE);
			}
			else *buf++ = 0;
		}
#else
	else
		strcpy(buf,&name[0]);
#endif
}