/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  Symbol name mangling routines.
*
****************************************************************************/

#include <ctype.h>

#include "globals.h"
#include "memalloc.h"
#include "parser.h"
#include "mangle.h"

#if MANGLERSUPP
#if !defined(__GNUC__) && !defined(__POCC__)
#define tolower(c) ((c >= 'A' && c <= 'Z') ? c | 0x20 : c )
#endif
#endif

typedef int (*mangle_func)( const struct asym *, char * );

static int ms32_decorate( const struct asym *sym, char *buffer );
#if OWFC_SUPPORT
static int ow_decorate( const struct asym *sym, char *buffer );
#endif
#if AMD64_SUPPORT
static int ms64_decorate( const struct asym *sym, char *buffer );
#endif

/* table of FASTCALL types.
 * order must match the one of enum fastcall_type
 * also see proc.c and invoke.c!
 */

static const mangle_func fcmanglers[] = {
    ms32_decorate, /* FCT_MSC */
#if OWFC_SUPPORT
    ow_decorate,   /* FCT_WATCOMC */
#endif
#if AMD64_SUPPORT
    ms64_decorate  /* FCT_WIN64 */
#endif
};

/* VoidMangler: no change to symbol name */

static int VoidMangler( const struct asym *sym, char *buffer )
/************************************************************/
{
    memcpy( buffer, sym->name, sym->name_size + 1 );
    return( sym->name_size );
}

/* UCaseMangler: convert symbol name to upper case */

static int UCaseMangler( const struct asym *sym, char *buffer )
/*************************************************************/
{
    memcpy( buffer, sym->name, sym->name_size + 1 );
    _strupr( buffer );
    return( sym->name_size );
}

/* UScoreMangler: add '_' prefix to symbol name */

static int UScoreMangler( const struct asym *sym, char *buffer )
/**************************************************************/
{
    buffer[0] = '_';
    memcpy( buffer+1, sym->name, sym->name_size + 1 );
    return( sym->name_size + 1 );
}

/* StdcallMangler: add '_' prefix and '@size' suffix to proc names */
/*                 add '_' prefix to other symbols */

static int StdcallMangler( const struct asym *sym, char *buffer )
/***************************************************************/
{
    const struct dsym *dir = (struct dsym *)sym;

    if( Options.stdcall_decoration == STDCALL_FULL && sym->isproc ) {
        return( sprintf( buffer, "_%s@%d", sym->name, dir->e.procinfo->parasize ) );
    } else {
        return( UScoreMangler( sym, buffer ) );
    }
}

/* MS FASTCALL or VECTORCALL 32bit */

static int ms32_decorate( const struct asym *sym, char *buffer )
/**************************************************************/
{
   // return ( sprintf( buffer, "@%s@%u", sym->name, ((struct dsym *)sym)->e.procinfo->parasize ) );
	const struct dsym *dir = (struct dsym *)sym;
	if ((sym->langtype == LANG_VECTORCALL) && (Options.vectorcall_decoration == VECTORCALL_FULL) && sym->isproc) {
		return(sprintf(buffer, "%s@@%d", sym->name, dir->e.procinfo->parasize));
	}
	else if (Options.fctype == FCT_MSC && sym->isproc) {
		return (sprintf(buffer, "@%s@%u", sym->name, ((struct dsym *)sym)->e.procinfo->parasize));
	}
	else {
		memcpy(buffer, sym->name, sym->name_size + 1);
		return(sym->name_size);
	}
}

#if OWFC_SUPPORT

/* flag values used by the OW fastcall name mangler ( changes ) */
enum changes {
    NORMAL           = 0,
    USCORE_FRONT     = 1,
    USCORE_BACK      = 2
};

/* FASTCALL OW style:
 *  add '_' suffix to proc names and labels
 *  add '_' prefix to other symbols
 */

static int ow_decorate( const struct asym *sym, char *buffer )
/************************************************************/
{
    char                *name;
    enum changes        changes = NORMAL;

    if( sym->isproc ) {
        changes |= USCORE_BACK;
    } else {
        switch( sym->mem_type ) {
        case MT_NEAR:
        case MT_FAR:
        case MT_EMPTY:
            changes |= USCORE_BACK;
            break;
        default:
            changes |= USCORE_FRONT;
        }
    }

    name = buffer;

    if( changes & USCORE_FRONT )
        *name++ = '_';
    memcpy( name, sym->name, sym->name_size + 1 );
    name += sym->name_size;
    if( changes & USCORE_BACK ) {
        *name++ = '_';
        *name = NULLC;
    }
    return( name - buffer );
}
#endif

#if AMD64_SUPPORT

/* MS FASTCALL or VECTORCALL 64bit */

static int ms64_decorate( const struct asym *sym, char *buffer )
/**************************************************************/
{
    //memcpy( buffer, sym->name, sym->name_size + 1 );
    //return( sym->name_size );
	const struct dsym *dir = (struct dsym *)sym;
	if ((sym->langtype == LANG_VECTORCALL) && (Options.vectorcall_decoration == VECTORCALL_FULL) && sym->isproc) {
		return(sprintf(buffer, "%s@@%d", sym->name, dir->e.procinfo->parasize));
	}
	else {
		memcpy(buffer, sym->name, sym->name_size + 1);
		return(sym->name_size);
	}
}
#endif

#if MANGLERSUPP
static char *CMangler( const struct asym *sym, char *buffer )
/***********************************************************/
{
    if( Options.naming_convention == NC_ADD_USCORES ) {
        return( UScoreMangler( sym, buffer ) );
    } else {
        return( VoidMangler( sym, buffer ) );
    }
}

static mangle_func GetMangler( const char *mangle_type )
/******************************************************/
{
    if( mangle_type != NULL && mangle_type[1] == NULLC ) {
        switch ( tolower( *mangle_type ) ) {
        case 'c':
            return( Options.xxx ? CMangler : ow_decorate );
        case 'n':
            return( VoidMangler );
        }
    }
    if ( mangle_type )
        EmitErr( UNKNOWN_MANGLER, mangle_type );

    return( NULL );
}
#endif

int Mangle( struct asym *sym, char *buffer )
/******************************************/
{
    mangle_func mangler;

    switch( sym->langtype ) {
    case LANG_C:
        /* leading underscore for C? */
        mangler = Options.no_cdecl_decoration ? VoidMangler : UScoreMangler;
        break;
    case LANG_SYSCALL:
        mangler = VoidMangler;
        break;
    case LANG_STDCALL:
        mangler = ( Options.stdcall_decoration == STDCALL_NONE ) ? VoidMangler : StdcallMangler;
        break;
    case LANG_PASCAL:
    case LANG_FORTRAN:
    case LANG_BASIC:
        mangler = UCaseMangler;
        break;
	case LANG_VECTORCALL:
    case LANG_FASTCALL:          /* registers passing parameters */
        mangler = fcmanglers[ModuleInfo.fctype];
        break;
    default: /* LANG_NONE */
#if MANGLERSUPP
        mangler = sym->mangler;
        if( mangler == NULL )
            mangler = GetMangler( Options.default_name_mangler );
        if( mangler == NULL )
#endif
            mangler = VoidMangler;
        break;
    }
#if MANGLERSUPP
    sym->mangler = mangler;
#endif
    return( mangler( sym, buffer ) );
}

/* the "mangle_type" is an extension inherited from OW Wasm
 * accepted are "C" and "N". It's NULL if MANGLESUPP == 0 (standard)
 */
void SetMangler( struct asym *sym, int langtype, const char *mangle_type )
/************************************************************************/
{
#if MANGLERSUPP
    mangle_func mangler;
#endif

    if( langtype != LANG_NONE )
        sym->langtype = langtype;

#if MANGLERSUPP
    mangler = GetMangler( mangle_type );
    if( mangler == NULL ) {
        /* nothing to do */
    } else if( sym->mangler == NULL ) {
        sym->mangler = mangler;
    } else if( sym->mangler != mangler ) {
        EmitErr( CONFLICTING_MANGLER, sym->name );
    }
#endif
}
