// -*- C++ -*-
// OS_NS_dlfcn.inl,v 1.10 2004/08/24 18:10:36 shuston Exp

#include "ace/OS_NS_macros.h"
#include "ace/OS_NS_errno.h"
#include "ace/OS_NS_fcntl.h"
#include "ace/OS_NS_string.h"
#include "ace/OS_NS_unistd.h"
#include "ace/Default_Constants.h"
#include "ace/os_include/os_fcntl.h"
#include "ace/os_include/os_string.h"

#if defined (ACE_USES_ASM_SYMBOL_IN_DLSYM)
#  include "ace/OS_Memory.h"
#  include "ace/OS_NS_string.h"
#endif /* ACE_USES_ASM_SYMBOL_IN_DLSYM */

ACE_INLINE int
ACE_OS::dlclose (ACE_SHLIB_HANDLE handle)
{
  ACE_OS_TRACE ("ACE_OS::dlclose");
#if defined (ACE_LACKS_DLCLOSE)
  ACE_UNUSED_ARG (handle);
  return 0;
#elif defined (ACE_HAS_SVR4_DYNAMIC_LINKING)

# if !defined (ACE_HAS_AUTOMATIC_INIT_FINI)
  // SunOS4 does not automatically call _fini()!
  void *ptr;

  ACE_OSCALL (::dlsym (handle, ACE_LIB_TEXT ("_fini")), void *, 0, ptr);

  if (ptr != 0)
    (*((int (*)(void)) ptr)) (); // Call _fini hook explicitly.
# endif /* ACE_HAS_AUTOMATIC_INIT_FINI */
#if defined (_M_UNIX)
  ACE_OSCALL_RETURN (::_dlclose (handle), int, -1);
#else /* _MUNIX */
    ACE_OSCALL_RETURN (::dlclose (handle), int, -1);
#endif /* _M_UNIX */
#elif defined (ACE_WIN32)
  ACE_WIN32CALL_RETURN (ACE_ADAPT_RETVAL (::FreeLibrary (handle), ace_result_), int, -1);
#elif defined (__hpux)
  // HP-UX 10.x and 32-bit 11.00 do not pay attention to the ref count
  // when unloading a dynamic lib.  So, if the ref count is more than
  // 1, do not unload the lib.  This will cause a library loaded more
  // than once to not be unloaded until the process runs down, but
  // that's life.  It's better than unloading a library that's in use.
  // So far as I know, there's no way to decrement the refcnt that the
  // kernel is looking at - the shl_descriptor is a copy of what the
  // kernel has, not the actual struct.  On 64-bit HP-UX using dlopen,
  // this problem has been fixed.
  struct shl_descriptor  desc;
  if (shl_gethandle_r (handle, &desc) == -1)
    return -1;
  if (desc.ref_count > 1)
    return 0;
# if defined(__GNUC__) || __cplusplus >= 199707L
  ACE_OSCALL_RETURN (::shl_unload (handle), int, -1);
# else
  ACE_OSCALL_RETURN (::cxxshl_unload (handle), int, -1);
# endif  /* aC++ vs. Hp C++ */
#else
  ACE_UNUSED_ARG (handle);
  ACE_NOTSUP_RETURN (-1);
#endif /* ACE_HAS_SVR4_DYNAMIC_LINKING */
}

ACE_INLINE ACE_TCHAR *
ACE_OS::dlerror (void)
{
  ACE_OS_TRACE ("ACE_OS::dlerror");
# if defined (ACE_HAS_SVR4_DYNAMIC_LINKING)
  const char *err;
#   if defined(_M_UNIX)
  ACE_OSCALL (::_dlerror (), const char *, 0, err);
#   else /* _M_UNIX */
  ACE_OSCALL (::dlerror (), const char *, 0, err);
#   endif /* _M_UNIX */
  if (err == 0)
    return 0;
#   if defined (ACE_USES_WCHAR)
  const size_t BufLen = 256;
  static wchar_t buf[BufLen];
  ACE_OS::strncpy (buf, ACE_TEXT_CHAR_TO_TCHAR (err), BufLen);
  return buf;
#   else
  return const_cast <char *> (err);
#   endif /* ACE_USES_WCHAR */
# elif defined (__hpux) || defined (VXWORKS)
  ACE_OSCALL_RETURN (::strerror(errno), char *, 0);
# elif defined (ACE_WIN32)
  static ACE_TCHAR buf[128];
#   if defined (ACE_HAS_PHARLAP)
  ACE_OS::sprintf (buf, "error code %d", GetLastError());
#   else
  ACE_TEXT_FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
                          0,
                          ::GetLastError (),
                          0,
                          buf,
                          sizeof buf / sizeof buf[0],
                          0);
#   endif /* ACE_HAS_PHARLAP */
  return buf;
# else
  ACE_NOTSUP_RETURN (0);
# endif /* ACE_HAS_SVR4_DYNAMIC_LINKING */
}

# if defined (ACE_HAS_CHARPTR_DL)
typedef ACE_TCHAR * ACE_DL_TYPE;
# else
typedef const ACE_TCHAR * ACE_DL_TYPE;
# endif /* ACE_HAS_CHARPTR_DL */

ACE_INLINE ACE_SHLIB_HANDLE
ACE_OS::dlopen (const ACE_TCHAR *fname,
                int mode)
{
  ACE_OS_TRACE ("ACE_OS::dlopen");

  // Get the correct OS type.
  ACE_DL_TYPE filename = const_cast<ACE_DL_TYPE> (fname);

# if defined (ACE_HAS_SVR4_DYNAMIC_LINKING)
  void *handle;
#   if defined (ACE_HAS_SGIDLADD)
  ACE_OSCALL
    (::sgidladd (ACE_TEXT_ALWAYS_CHAR (filename), mode), void *, 0, handle);
#   elif defined (_M_UNIX)
  ACE_OSCALL
    (::_dlopen (ACE_TEXT_ALWAYS_CHAR (filename), mode), void *, 0, handle);
#   else
  ACE_OSCALL
    (::dlopen (ACE_TEXT_ALWAYS_CHAR (filename), mode), void *, 0, handle);
#   endif /* ACE_HAS_SGIDLADD */
#   if !defined (ACE_HAS_AUTOMATIC_INIT_FINI)
  if (handle != 0)
    {
      void *ptr;
      // Some systems (e.g., SunOS4) do not automatically call _init(), so
      // we'll have to call it manually.

      ACE_OSCALL (::dlsym (handle, ACE_LIB_TEXT ("_init")), void *, 0, ptr);

      if (ptr != 0 && (*((int (*)(void)) ptr)) () == -1) // Call _init hook explicitly.
        {
          // Close down the handle to prevent leaks.
          ::dlclose (handle);
          return 0;
        }
    }
#   endif /* ACE_HAS_AUTOMATIC_INIT_FINI */
  return handle;
# elif defined (ACE_WIN32)
  ACE_UNUSED_ARG (mode);

  ACE_WIN32CALL_RETURN (ACE_TEXT_LoadLibrary (filename), ACE_SHLIB_HANDLE, 0);
# elif defined (__hpux)

#   if defined(__GNUC__) || __cplusplus >= 199707L
  ACE_OSCALL_RETURN (::shl_load(filename, mode, 0L), ACE_SHLIB_HANDLE, 0);
#   else
  ACE_OSCALL_RETURN (::cxxshl_load(filename, mode, 0L), ACE_SHLIB_HANDLE, 0);
#   endif  /* aC++ vs. Hp C++ */
# elif defined (VXWORKS)
  MODULE* handle;
  // Open readonly
  ACE_HANDLE filehandle = ACE_OS::open (filename,
                                        O_RDONLY,
                                        ACE_DEFAULT_FILE_PERMS);

  if (filehandle != ACE_INVALID_HANDLE)
    {
      ACE_OS::last_error(0);
      ACE_OSCALL ( ::loadModule (filehandle, mode ), MODULE *, 0, handle);
      int loaderror = ACE_OS::last_error();
      ACE_OS::close (filehandle);

      if ( (loaderror != 0) && (handle != 0) )
        {
          // ouch something went wrong most likely unresolved externals
          ::unldByModuleId ( handle, 0 );
          handle = 0;
        }
    }
  else
    {
      // couldn't open file
      handle = 0;
    }
  return handle;
# else
  ACE_UNUSED_ARG (filename);
  ACE_UNUSED_ARG (mode);
  ACE_NOTSUP_RETURN (0);
# endif /* ACE_HAS_SVR4_DYNAMIC_LINKING */
}

ACE_INLINE void *
ACE_OS::dlsym (ACE_SHLIB_HANDLE handle,
               const ACE_TCHAR *sname)
{
  ACE_OS_TRACE ("ACE_OS::dlsym");

#if defined (ACE_HAS_DLSYM_SEGFAULT_ON_INVALID_HANDLE)
  // Check if the handle is valid before making any calls using it.
  if (handle == ACE_SHLIB_INVALID_HANDLE)
    return 0;
#endif /* ACE_HAS_DLSYM_SEGFAULT_ON_INVALID_HANDLE */

  // Get the correct OS type.
#if defined (ACE_HAS_WINCE)
  const wchar_t *symbolname = sname;
#elif defined (ACE_USES_WCHAR)
  // WinCE is WCHAR always; other platforms need a char * symbol name
  ACE_Wide_To_Ascii w_sname (sname);
  char *symbolname = w_sname.char_rep ();
#elif defined (ACE_HAS_CHARPTR_DL)
  char *symbolname = const_cast<char *> (sname);
#else
  const char *symbolname = sname;
#endif /* ACE_HAS_WINCE */

# if defined (ACE_HAS_SVR4_DYNAMIC_LINKING)

#   if defined (ACE_USES_ASM_SYMBOL_IN_DLSYM)
  int l = ACE_OS::strlen (symbolname) + 2;
  char *asm_symbolname = 0;
  ACE_NEW_RETURN (asm_symbolname, char[l], 0);
  ACE_OS::strcpy (asm_symbolname, "_") ;
  ACE_OS::strcpy (asm_symbolname + 1, symbolname) ;
  void *ace_result;
  ACE_OSCALL (::dlsym (handle, asm_symbolname), void *, 0, ace_result);
  delete [] asm_symbolname;
  return ace_result;
#   elif defined (_M_UNIX)
  ACE_OSCALL_RETURN (::_dlsym (handle, symbolname), void *, 0);
#   else
  ACE_OSCALL_RETURN (::dlsym (handle, symbolname), void *, 0);
#   endif /* ACE_USES_ASM_SYMBOL_IN_DLSYM */

# elif defined (ACE_WIN32)

  ACE_WIN32CALL_RETURN (::GetProcAddress (handle, symbolname), void *, 0);

# elif defined (__hpux)

  void *value;
  int status;
  shl_t _handle = handle;
  ACE_OSCALL (::shl_findsym(&_handle, symbolname, TYPE_UNDEFINED, &value), int, -1, status);
  return status == 0 ? value : 0;

# elif defined (VXWORKS)

  // For now we use the VxWorks global symbol table
  // which resolves the most recently loaded symbols .. which resolve mostly what we want..
  ACE_UNUSED_ARG (handle);
  SYM_TYPE symtype;
  void *value = 0;
  STATUS status;
  ACE_OSCALL (::symFindByName(sysSymTbl, symbolname, (char **)&value, &symtype), int, -1, status);

  return status == OK ? value : 0;

# else

  ACE_UNUSED_ARG (handle);
  ACE_UNUSED_ARG (symbolname);
  ACE_NOTSUP_RETURN (0);

# endif /* ACE_HAS_SVR4_DYNAMIC_LINKING */
}
