//
// Gestionnaire de lien dynamique   -   R. Ansari 12/98
// LAL (Orsay) / IN2P3-CNRS  DAPNIA/SPP (Saclay) / CEA
//

#include "pdlmgr.h"
#include <stdio.h>
#include <stdlib.h>

#include <iostream.h>


// Extension de noms de fichiers Shared libs
static const char* sofext = ".so";
static const char* sofext_HPUX = ".sl";

// Variables et methodes static
int PDynLinkMgr::numSO = 0;
string*  PDynLinkMgr::tmpDir = NULL;

/*!
  \class SOPHYA::PDynLinkMgr
  \ingroup SysTools
  This classes handles the run-time operations related to using shared
  libraries. The present version has been adapted for different Unix 
  flavours (Linux, Compaq/Digital Unix, SGI IRIX, IBM AIX, Sun Solaris).
  The example here shows the linking of shared library named "mylib.so" 
  containing a function \c double \c myfunction(double x). 
  \code
  #include "pdlmgr.h"
  typedef double (* AFunctionOfX) (double x);
  {
  // ...
  string soname = "mylib.so";
  string funcname = "myfunction";
  PDynLinkMgr dyl(son);
  AFunctionOfX f = (AFunctionOfX)dyl.GetFunction(funcname);
  double x = 3.1425;
  if (f != NULL) 
    cout << " X= " << x << " myfunction(x)=" << f(x) << endl;
  // ...
  }
  \endcode
*/

/* --Methode-Static-- */
/*! Sets the path for a temporary space where shared libraries are copied.
    The path is appended to \b LD_LIBRARY_PATH
*/
void PDynLinkMgr::SetTmpDir(string const & path)
{
if ( (path.length() > 0) && (path[path.length()-1] != '/')  ) GetTmpDir() = path + '/';
else GetTmpDir() = path;
#if defined(OSF1) || defined(Linux) || defined(SunOS) || defined(IRIX64)
  char* varenv=NULL;
#if !defined(IRIX64)
  string cmd = "LD_LIBRARY_PATH=";
  varenv=getenv("LD_LIBRARY_PATH");
#else
#ifdef SGI_ARCH64
  string cmd = "LD_LIBRARYN32_PATH=";
  varenv=getenv("LD_LIBRARYN32_PATH");
#else
  string cmd = "LD_LIBRARYN64_PATH=";
  varenv=getenv("LD_LIBRARYN64_PATH");
#endif
#endif

  if (varenv == NULL) { 
    cmd += '.';  
    if (path.length() > 0)  cmd += ':' + path; 
    }
  else { 
    if (varenv[0] != '.') cmd += ".:";
    if (path.length() > 0)  cmd += path + ':';
    cmd += varenv;
    putenv(const_cast<char *>(cmd.c_str()));
    }
#elif defined(AIX)
  string cmd = "LIBPATH=";
  char* varenv=NULL;
  varenv=getenv("LIBPATH");
  if (varenv == NULL) { 
    cmd += '.';  
    if (path.length() > 0)  cmd += ':' + path; 
    cmd += ":/usr/lib:/lib";
    }
  else { 
    if (varenv[0] != '.') cmd += ".:";
    if (path.length() > 0)  cmd += path + ':';
    cmd += varenv;
    putenv(const_cast<char *>(cmd.c_str()));
    }

#endif
return;
}

/* --Methode-Static-- */
/*! Returns the temporary space path */
string& PDynLinkMgr::GetTmpDir()
{
if (tmpDir == NULL) { 
  tmpDir = new string("");
  char* varenv;
  if ( (varenv=getenv("TMPDIR")) != NULL )  { 
    *tmpDir = varenv; 
    if ((*tmpDir)[tmpDir->length()-1] != '/') (*tmpDir) += '/';
  }
}
return(*tmpDir);
}

/* --Methode-Static-- */
/*! Compiles the C source file named \b fname and creates the 
    corresponding shared library linking against the standard 
    C library (-lc) and the math library (-lm).
    Returns a pointer to the created PDynLinkMgr object (by new).
    Returns the NULL pointer in case of errors. 
*/
PDynLinkMgr* PDynLinkMgr::BuildFromCFile(string const & fname)
{
size_t l = fname.length();
if (l < 1)  return(NULL);
string fnameobj = GetTmpDir()+"tmp_pdl.o";

string cmd;
int rc;

// Compilation du fichier
#ifndef __mac__
#ifdef SGI_ARCH64
cmd = "cc -64 -c -o " + fnameobj + " " + fname;
#else
cmd = "cc -c -o " + fnameobj + " " + fname;
#endif
#else 
cmd = "Il faut compiler !!!" + fnameobj + " " + fname;
#endif
rc = system(cmd.c_str());
if (rc != 0)  { 
  cerr << "PDynLinkMgr::BuildFromCFile() Error Rc(" << cmd <<")= "<< rc << endl; 
  return(NULL);
  }

char buff[32];
numSO++;
#ifndef HPUX
sprintf(buff,"pdlmgr%d%s", numSO,sofext);
#endif
string fnameso = GetTmpDir()+buff;

// Creation du shared-lib
#if defined(OSF1) 
  cmd = "ld -shared -o " + fnameso + " -all " + fnameobj + " -none -lm -lc";
#elif defined(Linux)
  cmd = "ld -shared -o " + fnameso + " " + fnameobj + " -lm -lc";
#elif defined(SunOS)
  cmd = "ld -G -o " + fnameso + " " + fnameobj + " -lm -lc";
#elif defined(IRIX64)
#ifdef SGI_ARCH64
  cmd = "ld -64 -shared -o " + fnameso + " " + fnameobj + " -lm -lc";
#else
  cmd = "ld -shared -o " + fnameso + " " + fnameobj + " -lm -lc";
#endif
#elif defined(AIX)
  cmd = "ld -G -bnogc -bexpall -bM:1L -o " + fnameso + " " + fnameobj;
#elif defined(HPUX)
  cmd = "ld -b -o " + fnameso + " " + fnameobj + " -lm -lc";
#else 
  cmd = "ld -o " + fnameso + " " + fnameobj + " -lm -lc";
#endif
rc = system(cmd.c_str());
if (rc != 0)  { 
  cerr << "PDynLinkMgr::BuildFromCFile() Error Rc(" << cmd <<")= "<< rc << endl; 
  return(NULL);
  }
PDynLinkMgr* rdyn = new PDynLinkMgr(fnameso, false);
rdyn->copy = true;
return(rdyn);

}

/* --Methode-- */
/*! The constructor. 
    \param soname : Name of the shared library. ".so" is appended
    to the name if no dot "." is found in the name.
    \param cp : if true, copies the shared library in the temporary space.
*/ 
PDynLinkMgr::PDynLinkMgr(string& soname, bool cp)
{
dlhandle = NULL;
soName = "";

if (soname.find_last_of(".") > soname.length())  
#ifdef HPUX
  soname += sofext_HPUX;
#else
  soname += sofext;
#endif

string fnameso;
if (cp) {
  numSO++;
  char buff[32];
#ifndef HPUX
  sprintf(buff,"pdlmgr%d%s", numSO,sofext);
#else
  sprintf(buff,"pdlmgr%d%s", numSO,sofext_HPUX);
#endif
  fnameso = GetTmpDir()+buff;
  string cmd = "cp " + soname + " " + fnameso;
  int rc = system(cmd.c_str());
  if (rc != 0)  { 
    cerr << "PDynLinkMgr::PDynLinkMgr() Error Rc(" << cmd <<")= "<< rc << endl; 
    return;
    }
  } 
else fnameso = soname;
copy = cp;
soName = fnameso;

#if defined(HPUX)
dlhandle = NULL;
cerr << "PDynLinkMgr::PDynLinkMgr() Not yet available on HP-UX " << endl;
return;
#else
dlhandle =  dlopen(fnameso.c_str(), RTLD_NOW);
if (dlhandle == NULL) { 
  cerr << "PDynLinkMgr::PDynLinkMgr(): Error opening SO " << fnameso 
       << " (" << soname << ")" << endl; 
  string sn = dlerror();
  cerr << "Loader Error (dlerror()) :" << sn << endl;
  return; 
  }
#endif
}

/* --Methode-- */
/*! Destructor. Closes the shared library. Removes the file if it had been
    copied in the temporary space, or generated by \b BuildFromCFile */
PDynLinkMgr::~PDynLinkMgr()
{
#if defined(HPUX)
cerr << "PDynLinkMgr::~PDynLinkMgr() Not yet available on HP-UX " << endl;
return;
#else
if (dlhandle) dlclose(dlhandle);    dlhandle = NULL;
if (copy) {
  string cmd = "rm -f " + soName;
  system(cmd.c_str());
  }
#endif
}

/* --Methode-- */
/*! Returns a handle to the function named \b funcname.
    Returns the NULL pointer in case of error          */
DlFunction PDynLinkMgr::GetFunction(string const & funcname)
{
DlFunction f = NULL;
#if defined(HPUX)
cerr << "PDynLinkMgr::GetFunction() Not yet available on HP-UX " << endl;
return f;
#else
if (dlhandle != NULL)  
  f = (DlFunction)dlsym(dlhandle, funcname.c_str());
if (f == NULL) cerr << "PDynLinkMgr::GetFunction(): Error linking " << funcname << endl;
return(f);
#endif
}

