/*
 * Copyright (c) 2001-2003 Shiman Associates Inc. All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include <stdio.h>
#include <string.h>
#include "mas_internal.h"
#include "assembler.h"
#include "mas/mas_dpi.h"
#include <dlfcn.h>

/* device loading, unloading, and symbol resolution. */

int32
unload_plugin_library( void* handle )
{
    int err = -1;

    err = dlclose( handle );
    
    if (err != 0)
    {
	masc_log_message(MAS_VERBLVL_ERROR, "Error closing plugin library");
	return mas_error(MERR_ERROR);
    }
    
    return 0;
}

int32
load_plugin_library( char* fname, char path_list[][MAX_FNAME_LENGTH], void** handle_ptr )
{
    void* handle;
    char  filename[MAX_FNAME_LENGTH];
    int   i;
    FILE* file;
    int   last_attempt = FALSE;
    char  slash[] = "/";
    i = 0;

    masc_entering_log_level( "asm: loading device");
    while (!last_attempt)
    {
	file = 0;
	while ( ( path_list[i][0] != 0 ) && !file ) /* try each path entry */
	{
	    filename[0] = 0;
	    if (fname[0] != '.' && fname[0] != slash[0])
	    {
		strncat(filename, path_list[i], MAX_FNAME_LENGTH);
		strncat(filename, slash, MAX_FNAME_LENGTH);
	    }
	    strncat(filename, fname, MAX_FNAME_LENGTH);

	    file = fopen(filename, "r");
	    i++;
	}
	
	if (file)
        {
#ifdef DEBUG
	    masc_log_message( MAS_VERBLVL_DEBUG, "asm: loading device library: '%s'", filename);
#endif
            fclose(file); /* we located the file, try this one.*/
        }
	else 
	{
	    /* otherwise, rely on the dynamic lib to find it from the RPATH or
	       LD_LIBRARY_PATH */
	    strncpy(filename, fname, MAX_FNAME_LENGTH);
	    last_attempt = TRUE;

#ifdef DEBUG
	    masc_log_message( MAS_VERBLVL_DEBUG, "asm: trying to load device library '%s' with default paths.", filename);
#endif

	}
	
	/* for now, we're using dlopen().  eventually, we'll move to the
	   Metro Link/XF86 loadable module code. */ 
	handle = dlopen(filename, RTLD_NOW);

        if (handle != 0)
        {
            *handle_ptr = handle;
            masc_exiting_log_level();
            return 0;
        }

        /* no matches could be found */
        if (handle == 0)
        {
            masc_log_message(MAS_VERBLVL_ERROR, "");
            masc_log_message(MAS_VERBLVL_ERROR, "asm: [ERROR] Can't load plugin library %s",filename);
            masc_log_message(MAS_VERBLVL_ERROR, "asm: [ERROR] The dynamic loader reports:");
            masc_log_message(MAS_VERBLVL_ERROR, "asm: [ERROR] '%s'", dlerror());
            if ( last_attempt )
            {
                masc_log_message(MAS_VERBLVL_INFO, "");
                masc_entering_log_level("asm: Troubleshooting tips:");
                masc_log_message(MAS_VERBLVL_INFO, "asm: [INFO]  1. Set your MAS_PATH environment variable to the MAS installation directory.");
                masc_log_message(MAS_VERBLVL_INFO, "asm: [INFO]     (e.g. /usr/local/mas/lib)");
                masc_log_message(MAS_VERBLVL_INFO, "asm: [INFO]  2. Make sure the device is installed in that directory.");
                masc_log_message(MAS_VERBLVL_INFO, "asm: [INFO]  3. If possible, make sure the device has been compiled correctly.");
                masc_log_message(MAS_VERBLVL_INFO, "");
                masc_exiting_log_level();
            }
        }
        
        if ( !last_attempt)
        {
            masc_log_message( MAS_VERBLVL_WARNING, "asm: [warning] I couldn't load the device library '%s'", filename );
            masc_log_message( MAS_VERBLVL_WARNING, "asm: [warning] I will try other potential locations." );
            masc_log_message( MAS_VERBLVL_WARNING, "");
        }
    } /* loop until last_attempt */
    

    masc_exiting_log_level();
    return 0;
}

void*
resolve_symbol_by_name( void* handle, char* name )
{
    void*       symbol;
    const char* error;
    
    symbol = dlsym(handle, name);
    if ((error = dlerror()) != NULL)
    {
        masc_log_message(MAS_VERBLVL_WARNING, "asm: [warning] Could not find symbol \"%s\" in library.", name);
        return 0;
    }

    return symbol;
}
