// -*- C++ -*-
#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_WIN32) && defined (ACE_HAS_PHARLAP)
# include "ace/OS_NS_stdio.h"
#endif

#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_BEGIN_VERSIONED_NAMESPACE_DECL

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_TEXT ("_fini")), void *, ptr);

  if (ptr != 0)
    (*((int (*)(void)) ptr)) (); // Call _fini hook explicitly.
# endif /* ACE_HAS_AUTOMATIC_INIT_FINI */
  return ::dlclose (handle);
#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;
  return ::shl_unload (handle);
#else
  ACE_UNUSED_ARG (handle);
  ACE_NOTSUP_RETURN (-1);
#endif /* ACE_HAS_SVR4_DYNAMIC_LINKING */
}

ACE_INLINE ACE_TCHAR *
ACE_OS::dlerror ()
{
  ACE_OS_TRACE ("ACE_OS::dlerror");
# if defined (ACE_HAS_SVR4_DYNAMIC_LINKING)
  const char *err = 0;
  ACE_OSCALL (::dlerror (), const char *, err);
  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 (ACE_VXWORKS)
  //FUZZ: disable check_for_lack_ACE_OS
  return ::strerror(errno);
  //FUZZ: enable check_for_lack_ACE_OS
# 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 */
}

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

# if defined (ACE_HAS_SVR4_DYNAMIC_LINKING)
  void *handle;
  ACE_OSCALL (::dlopen (ACE_TEXT_ALWAYS_CHAR (fname), mode), void *, handle);
#   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_TEXT ("_init")), void *, 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 (fname), ACE_SHLIB_HANDLE, 0);
# elif defined (__hpux)
  return ::shl_load(fname, mode, 0L);
# elif defined (ACE_VXWORKS) && !defined (__RTP__)
  ACE_UNUSED_ARG (mode);
  MODULE* handle = 0;
  // Open readonly
  ACE_HANDLE filehandle = ACE_OS::open (fname,
                                        O_RDONLY,
                                        ACE_DEFAULT_FILE_PERMS);

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

      if ( (loaderror != 0) && (handle != 0) )
        {
          // ouch something went wrong most likely unresolved externals
          if (handle)
            ::unldByModuleId ( handle, 0 );
          handle = 0;
        }
    }
  else
    {
      // couldn't open file
      handle = 0;
    }
  return handle;
# else
  ACE_UNUSED_ARG (fname);
  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");

  // Check if the handle is valid before making any calls using it.
  if (handle == ACE_SHLIB_INVALID_HANDLE)
    return nullptr;

  // Get the correct OS type.
#if defined (ACE_HAS_WINCE)
  // CE (at least thru Pocket PC 2003) offers GetProcAddressW, not ...A, so
  // we always need a wide-char string.
  const wchar_t *symbolname = 0;
#  if defined (ACE_USES_WCHAR)
  symbolname = sname;
#  else
  ACE_Ascii_To_Wide sname_xlate (sname);
  symbolname = sname_xlate.wchar_rep ();
#  endif /* ACE_USES_WCHAR */
#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_VXWORKS)
  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 const l = ACE_OS::strlen (symbolname) + 2;
  char *asm_symbolname {};
  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 *, ace_result);
  delete [] asm_symbolname;
  return ace_result;
#   else
  return ::dlsym (handle, symbolname);
#   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 = 0;
  shl_t _handle = handle;
  ACE_OSCALL (::shl_findsym(&_handle, symbolname, TYPE_UNDEFINED, &value), int, status);
  return status == 0 ? value : nullptr;

# elif defined (ACE_VXWORKS) && !defined (__RTP__)

  // 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);
#if (ACE_VXWORKS < 0x690)
  SYM_TYPE symtype;
  char *value = 0;
  STATUS status;
  ACE_OSCALL (::symFindByName(sysSymTbl, symbolname, &value, &symtype), int, status);

  return status == OK ? reinterpret_cast <void*>(value) : nullptr;
#else
  STATUS status;

  SYMBOL_DESC symbolDesc;     /* symFind() descriptor */
  ACE_OS::memset (&symbolDesc, 0, sizeof (SYMBOL_DESC));
  symbolDesc.mask = SYM_FIND_BY_NAME;
  symbolDesc.name = symbolname;

  ACE_OSCALL (::symFind(sysSymTbl, &symbolDesc), int, status);

  return status == OK ? reinterpret_cast <void*>(symbolDesc.value) : nullptr;
#endif /* (ACE_VXWORKS < 0x690) */

# else
  ACE_UNUSED_ARG (handle);
  ACE_UNUSED_ARG (symbolname);
  ACE_NOTSUP_RETURN (nullptr);
# endif /* ACE_HAS_SVR4_DYNAMIC_LINKING */
}

ACE_END_VERSIONED_NAMESPACE_DECL
