#include "ace/OS_NS_stdlib.h"

#include "ace/Default_Constants.h"

#if !defined (ACE_HAS_INLINED_OSCALLS)
# include "ace/OS_NS_stdlib.inl"
#endif /* ACE_HAS_INLINED_OSCALLS */

#include "ace/OS_Memory.h"

#include "ace/OS_NS_unistd.h"
#include "ace/OS_NS_ctype.h"

#if defined (ACE_LACKS_MKTEMP) \
    || defined (ACE_LACKS_MKSTEMP) \
    || defined (ACE_LACKS_REALPATH)
#  include "ace/OS_NS_stdio.h"
#  include "ace/OS_NS_sys_stat.h"
#endif /* ACE_LACKS_MKTEMP || ACE_LACKS_MKSTEMP || ACE_LACKS_REALPATH */

#if defined (ACE_LACKS_MKSTEMP)
# include "ace/OS_NS_fcntl.h"
# include "ace/OS_NS_ctype.h"
# include "ace/OS_NS_sys_time.h"
# include "ace/OS_NS_Thread.h"
# include "ace/Numeric_Limits.h"
#endif  /* ACE_LACKS_MKSTEMP */

ACE_BEGIN_VERSIONED_NAMESPACE_DECL

ACE_EXIT_HOOK ACE_OS::exit_hook_ = 0;

void *
ACE_OS::calloc (size_t elements, size_t sizeof_elements)
{
#if !defined (ACE_HAS_WINCE)
  return ACE_CALLOC_FUNC (elements, sizeof_elements);
#else
  // @@ This will probably not work since it doesn't consider
  // alignment properly.
  return ACE_MALLOC_FUNC (elements * sizeof_elements);
#endif /* ACE_HAS_WINCE */
}

void
ACE_OS::exit (int status)
{
  ACE_OS_TRACE ("ACE_OS::exit");

#if defined (ACE_HAS_NONSTATIC_OBJECT_MANAGER) && !defined (ACE_HAS_WINCE) && !defined (ACE_DOESNT_INSTANTIATE_NONSTATIC_OBJECT_MANAGER)
  // Shut down the ACE_Object_Manager, if it had registered its exit_hook.
  // With ACE_HAS_NONSTATIC_OBJECT_MANAGER, the ACE_Object_Manager is
  // instantiated on the main's stack.  ::exit () doesn't destroy it.
  if (exit_hook_)
    (*exit_hook_) ();
#endif /* ACE_HAS_NONSTATIC_OBJECT_MANAGER && !ACE_HAS_WINCE && !ACE_DOESNT_INSTANTIATE_NONSTATIC_OBJECT_MANAGER */

#if defined (ACE_WIN32)
  ::ExitProcess ((UINT) status);
#else
  ::exit (status);
#endif /* ACE_WIN32 */
}

void
ACE_OS::free (void *ptr)
{
  ACE_FREE_FUNC (ACE_MALLOC_T (ptr));
}

// You may be asking yourself, why are we doing this?  Well, in winbase.h,
// MS didn't follow their normal Api_FunctionA and Api_FunctionW style,
// so we have to #undef their define to get access to the unicode version.
// And because we don't want to #undef this for the users code, we keep
// this method in the .cpp file.
#if defined (ACE_WIN32) && defined (UNICODE) && !defined (ACE_USES_TCHAR)
#undef GetEnvironmentStrings
#endif /* ACE_WIN32 && UNICODE !ACE_USES_TCHAR */

ACE_TCHAR *
ACE_OS::getenvstrings (void)
{
#if defined (ACE_LACKS_GETENVSTRINGS)
  ACE_NOTSUP_RETURN (0);
#elif defined (ACE_WIN32)
# if defined (ACE_USES_WCHAR)
  return ::GetEnvironmentStringsW ();
# else /* ACE_USES_WCHAR */
  return ::GetEnvironmentStrings ();
# endif /* ACE_USES_WCHAR */
#else /* ACE_WIN32 */
  ACE_NOTSUP_RETURN (0);
#endif /* ACE_WIN32 */
}

// Return a dynamically allocated duplicate of <str>, substituting the
// environment variables of form $VAR_NAME.  Note that the pointer is
// allocated with <ACE_OS::malloc> and must be freed by
// <ACE_OS::free>.

ACE_TCHAR *
ACE_OS::strenvdup (const ACE_TCHAR *str)
{
#if defined (ACE_HAS_WINCE)
  // WinCE doesn't have environment variables so we just skip it.
  return ACE_OS::strdup (str);
#elif defined (ACE_LACKS_STRENVDUP)
  ACE_UNUSED_ARG (str);
  ACE_NOTSUP_RETURN (0);
#else
  const ACE_TCHAR * start = 0;
  if ((start = ACE_OS::strchr (str, ACE_TEXT ('$'))) != 0)
    {
      ACE_TCHAR buf[ACE_DEFAULT_ARGV_BUFSIZ];
      size_t var_len = ACE_OS::strcspn (&start[1],
        ACE_TEXT ("$~!#%^&*()-+=\\|/?,.;:'\"`[]{} \t\n\r"));
      ACE_OS::strncpy (buf, &start[1], var_len);
      buf[var_len++] = ACE_TEXT ('\0');
#  if defined (ACE_WIN32)
      // Always use the ACE_TCHAR for Windows.
      ACE_TCHAR *temp = ACE_OS::getenv (buf);
#  else
      // Use char * for environment on non-Windows.
      char *temp = ACE_OS::getenv (ACE_TEXT_ALWAYS_CHAR (buf));
#  endif /* ACE_WIN32 */
      size_t buf_len = ACE_OS::strlen (str) + 1;
      if (temp != 0)
        buf_len += ACE_OS::strlen (temp) - var_len;
      ACE_TCHAR * buf_p = buf;
      if (buf_len > ACE_DEFAULT_ARGV_BUFSIZ)
        {
          buf_p =
            (ACE_TCHAR *) ACE_OS::malloc (buf_len * sizeof (ACE_TCHAR));
          if (buf_p == 0)
            {
              errno = ENOMEM;
              return 0;
            }
        }
      ACE_TCHAR * p = buf_p;
      size_t len = start - str;
      ACE_OS::strncpy (p, str, len);
      p += len;
      if (temp != 0)
        {
#  if defined (ACE_WIN32)
          p = ACE_OS::strecpy (p, temp) - 1;
#  else
          p = ACE_OS::strecpy (p, ACE_TEXT_CHAR_TO_TCHAR (temp)) - 1;
#  endif /* ACE_WIN32 */
        }
      else
        {
          ACE_OS::strncpy (p, start, var_len);
          p += var_len;
          *p = ACE_TEXT ('\0');
        }
      ACE_OS::strcpy (p, &start[var_len]);
      return (buf_p == buf) ? ACE_OS::strdup (buf) : buf_p;
    }
  else
    return ACE_OS::strdup (str);
#endif /* ACE_HAS_WINCE */
}

#if !defined (ACE_HAS_ITOA)
char *
ACE_OS::itoa_emulation (int value, char *string, int radix)
{
  char *e = string;
  char *b = string;

  // Short circuit if 0

  if (value == 0)
    {
      string[0] = '0';
      string[1] = 0;
      return string;
    }

  // If negative and base 10, print a - and then do the
  // number.

  if (value < 0 && radix == 10)
    {
      string[0] = '-';
      ++b;
      ++e; // Don't overwrite the negative sign.
      value = -value; // Drop negative sign so character selection is correct.
    }

  // Convert to base <radix>, but in reverse order

  while (value != 0)
    {
      int mod = value % radix;
      value = value / radix;

      *e++ = (mod < 10) ? '0' + mod : 'a' + mod - 10;
    }

  *e-- = 0;

  // Now reverse the string to get the correct result

  while (e > b)
    {
      char temp = *e;
      *e = *b;
      *b = temp;
      ++b;
      --e;
    }

  return string;
}
#endif /* !ACE_HAS_ITOA */

#if defined (ACE_HAS_WCHAR) && defined (ACE_LACKS_ITOW)
wchar_t *
ACE_OS::itow_emulation (int value, wchar_t *string, int radix)
{
  wchar_t *e = string;
  wchar_t *b = string;

  // Short circuit if 0

  if (value == 0)
    {
      string[0] = '0';
      string[1] = 0;
      return string;
    }

  // If negative and base 10, print a - and then do the
  // number.

  if (value < 0 && radix == 10)
    {
      string[0] = '-';
      b++;
    }

  // Convert to base <radix>, but in reverse order

  while (value != 0)
    {
      int mod = value % radix;
      value = value / radix;

      *e++ = (mod < 10) ? '0' + mod : 'a' + mod - 10;
    }

  *e-- = 0;

  // Now reverse the string to get the correct result

  while (e > b)
    {
      wchar_t temp = *e;
      *e = *b;
      *b = temp;
      ++b;
      --e;
    }

  return string;
}
#endif /* ACE_HAS_WCHAR && ACE_LACKS_ITOW */

void *
ACE_OS::malloc (size_t nbytes)
{
  return ACE_MALLOC_FUNC (nbytes);
}

#if defined (ACE_LACKS_MKTEMP) && !defined (ACE_DISABLE_MKTEMP)
ACE_TCHAR *
ACE_OS::mktemp (ACE_TCHAR *s)
{
  ACE_OS_TRACE ("ACE_OS::mktemp");
  if (s == 0)
    // check for null template string failed!
    return 0;

  ACE_TCHAR *xxxxxx = ACE_OS::strstr (s, ACE_TEXT ("XXXXXX"));
  if (xxxxxx == 0)
    // the template string doesn't contain "XXXXXX"!
    return s;

  // Find an unused filename for this process.  It is assumed
  // that the user will open the file immediately after
  // getting this filename back (so, yes, there is a race
  // condition if multiple threads in a process use the same
  // template).  This appears to match the behavior of the
  // SunOS 5.5 mktemp().
  bool found = false;
  for (ACE_TCHAR letter = ACE_TEXT ('a');
       letter <= ACE_TEXT ('z');
       ++letter)
    {
      ACE_stat sb;
      ACE_OS::sprintf (xxxxxx,
                       ACE_TEXT ("%05d%c"),
                       (int)ACE_OS::getpid () % 100000,
                       letter);
      if (ACE_OS::stat (s, &sb) < 0)
        {
          found = true;
          break;
        }
    }
  if (!found)
    // maximum of 26 unique files per template, per process
    ACE_OS::sprintf (xxxxxx, ACE_TEXT ("%s"), ACE_TEXT (""));
  return s;
}
#endif /* ACE_LACKS_MKTEMP && !ACE_DISABLE_MKTEMP */

void *
ACE_OS::realloc (void *ptr, size_t nbytes)
{
  return ACE_REALLOC_FUNC (ACE_MALLOC_T (ptr), nbytes);
}

#if defined (ACE_LACKS_REALPATH)
char *
ACE_OS::realpath (const char *file_name,
                  char *resolved_name)
{
  ACE_OS_TRACE ("ACE_OS::realpath");

  if (file_name == 0)
    {
      // Single Unix Specification V3:
      //   Return an error if parameter is a null pointer.
      errno = EINVAL;
      return 0;
    }

  if (*file_name == '\0')
    {
      // Single Unix Specification V3:
      //   Return an error if the file_name argument points
      //   to an empty string.
      errno = ENOENT;
      return 0;
    }

  char* rpath;

  if (resolved_name == 0)
    {
      // Single Unix Specification V3:
      //   Return an error if parameter is a null pointer.
      //
      // To match glibc realpath() and Win32 _fullpath() behavior,
      // allocate room for the return value if resolved_name is
      // a null pointer.
      rpath = static_cast<char*>(ACE_OS::malloc (PATH_MAX));
      if (rpath == 0)
        {
          errno = ENOMEM;
          return 0;
        }
    }
  else
    {
      rpath = resolved_name;
    }

  char* dest;

  if (*file_name != '/')
    {
      // file_name is relative path so CWD needs to be added
      if (ACE_OS::getcwd (rpath, PATH_MAX) == 0)
        {
          if (resolved_name == 0)
            ACE_OS::free (rpath);
          return 0;
        }
      dest = ACE_OS::strchr (rpath, '\0');
    }
  else
    {
      dest = rpath;
    }

#if !defined (ACE_LACKS_SYMLINKS)
  char expand_buf[PATH_MAX]; // Extra buffer needed to expand symbolic links
  int nlinks = 0;
#endif

  while (*file_name)
    {
      *dest++ = '/';

      // Skip multiple separators
      while (*file_name == '/')
        ++file_name;

      char* start = dest;

      // Process one path component
      while (*file_name && *file_name != '/')
        {
          *dest++ = *file_name++;
          if (dest - rpath > PATH_MAX)
            {
              errno = ENAMETOOLONG;
              if (resolved_name == 0)
                ACE_OS::free (rpath);
              return 0;
            }
        }

      if (start == dest) // Are we done?
        {
          if (dest - rpath > 1)
            --dest; // Remove trailing separator if not at root
          break;
        }
      else if (dest - start == 1 && *start == '.')
        {
          dest -= 2; // Remove "./"
        }
      else if (dest - start == 2 && *start == '.' && *(start +1) == '.')
        {
          dest -= 3; // Remove "../"
          if (dest > rpath) // Remove the last path component if not at root
            while (*--dest != '/')
              ;
        }
#  if !defined (ACE_LACKS_SYMLINKS)
      else
        {
          ACE_stat st;

          *dest = '\0';
          if (ACE_OS::lstat(rpath, &st) < 0)
            {
              if (resolved_name == 0)
              ACE_OS::free (rpath);
                return 0;
            }

          // Check if current path is a link
          if (S_ISLNK (st.st_mode))
            {
              if (++nlinks > MAXSYMLINKS)
                {
                  errno = ELOOP;
                  if (resolved_name == 0)
                    ACE_OS::free (rpath);
                  return 0;
                }

              char link_buf[PATH_MAX];

              ssize_t link_len = ACE_OS::readlink (rpath, link_buf, PATH_MAX);
              int tail_len = ACE_OS::strlen (file_name) + 1;

              // Check if there is room to expand link?
              if (link_len + tail_len > PATH_MAX)
                {
                  errno = ENAMETOOLONG;
                  if (resolved_name == 0)
                    ACE_OS::free (rpath);
                  return 0;
                }

              // Move tail and prefix it with expanded link
              ACE_OS::memmove (expand_buf + link_len, file_name, tail_len);
              ACE_OS::memcpy (expand_buf, link_buf, link_len);

              if (*link_buf == '/') // Absolute link?
                {
                  dest = rpath;
                }
              else // Relative link, remove expanded link component
                {
                  --dest;
                  while (*--dest != '/')
                    ;
                }
              file_name = expand_buf; // Source path is now in expand_buf
            }
        }
#  endif /* ACE_LACKS_SYMLINKS */
    }

  *dest = '\0';

  return rpath;
}
#endif /* ACE_LACKS_REALPATH */

#if defined (ACE_LACKS_STRTOL)
long
ACE_OS::strtol_emulation (const char *nptr, char **endptr, int base)
{
  ACE_REGISTER const char *s = nptr;
  ACE_REGISTER unsigned long acc;
  ACE_REGISTER int c;
  ACE_REGISTER unsigned long cutoff;
  ACE_REGISTER int neg = 0, any, cutlim;

  /*
   * Skip white space and pick up leading +/- sign if any.
   * If base is 0, allow 0x for hex and 0 for octal, else
   * assume decimal; if base is already 16, allow 0x.
   */
  do {
    c = *s++;
  } while (ACE_OS::ace_isspace(c));
  if (c == '-') {
    neg = 1;
    c = *s++;
  } else if (c == '+')
    c = *s++;
  if ((base == 0 || base == 16) &&
    c == '0' && (*s == 'x' || *s == 'X')) {
    c = s[1];
    s += 2;
    base = 16;
  }
  if (base == 0)
    base = c == '0' ? 8 : 10;

  /*
   * Compute the cutoff value between legal numbers and illegal
   * numbers.  That is the largest legal value, divided by the
   * base.  An input number that is greater than this value, if
   * followed by a legal input character, is too big.  One that
   * is equal to this value may be valid or not; the limit
   * between valid and invalid numbers is then based on the last
   * digit.  For instance, if the range for longs is
   * [-2147483648..2147483647] and the input base is 10,
   * cutoff will be set to 214748364 and cutlim to either
   * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
   * a value > 214748364, or equal but the next digit is > 7 (or 8),
   * the number is too big, and we will return a range error.
   *
   * Set any if any `digits' consumed; make it negative to indicate
   * overflow.
   */
  cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
  cutlim = cutoff % (unsigned long)base;
  cutoff /= (unsigned long)base;
  for (acc = 0, any = 0;; c = *s++) {
    if (ACE_OS::ace_isdigit(c))
      c -= '0';
    else if (ACE_OS::ace_isalpha(c))
      c -= ACE_OS::ace_isupper(c) ? 'A' - 10 : 'a' - 10;
    else
      break;
    if (c >= base)
      break;
    if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
      any = -1;
    else {
      any = 1;
      acc *= base;
      acc += c;
    }
  }
  if (any < 0) {
    acc = neg ? LONG_MIN : LONG_MAX;
    errno = ERANGE;
  } else if (neg)
    acc = -acc;
  if (endptr != 0)
    *endptr = any ? (char *)s - 1 : (char *)nptr;
  return (acc);
}
#endif /* ACE_LACKS_STRTOL */

#if defined (ACE_HAS_WCHAR) && defined (ACE_LACKS_WCSTOL)
long
ACE_OS::wcstol_emulation (const wchar_t *nptr,
        wchar_t **endptr,
        int base)
{
  ACE_REGISTER const wchar_t *s = nptr;
  ACE_REGISTER unsigned long acc;
  ACE_REGISTER int c;
  ACE_REGISTER unsigned long cutoff;
  ACE_REGISTER int neg = 0, any, cutlim;

  /*
   * Skip white space and pick up leading +/- sign if any.
   * If base is 0, allow 0x for hex and 0 for octal, else
   * assume decimal; if base is already 16, allow 0x.
   */
  do {
    c = *s++;
  } while (ACE_OS::ace_isspace(c));
  if (c == L'-') {
    neg = 1;
    c = *s++;
  } else if (c == L'+')
    c = *s++;
  if ((base == 0 || base == 16) &&
    c == '0' && (*s == L'x' || *s == L'X')) {
    c = s[1];
    s += 2;
    base = 16;
  }
  if (base == 0)
    base = c == L'0' ? 8 : 10;

  /*
   * See strtol for comments as to the logic used.
   */
  cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
  cutlim = cutoff % (unsigned long)base;
  cutoff /= (unsigned long)base;
  for (acc = 0, any = 0;; c = *s++) {
    if (ACE_OS::ace_isdigit(c))
      c -= L'0';
    else if (ACE_OS::ace_isalpha(c))
      c -= ACE_OS::ace_isupper(c) ? L'A' - 10 : L'a' - 10;
    else
      break;
    if (c >= base)
      break;
    if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
      any = -1;
    else {
      any = 1;
      acc *= base;
      acc += c;
    }
  }
  if (any < 0) {
    acc = neg ? LONG_MIN : LONG_MAX;
    errno = ERANGE;
  } else if (neg)
    acc = -acc;
  if (endptr != 0)
    *endptr = any ? (wchar_t *)s - 1 : (wchar_t *)nptr;
  return (acc);
}
#endif /* ACE_HAS_WCHAR && ACE_LACKS_WCSTOL */

#if defined (ACE_LACKS_STRTOUL)
unsigned long
ACE_OS::strtoul_emulation (const char *nptr,
                           char **endptr,
                           ACE_REGISTER int base)
{
  ACE_REGISTER const char *s = nptr;
  ACE_REGISTER unsigned long acc;
  ACE_REGISTER int c;
  ACE_REGISTER unsigned long cutoff;
  ACE_REGISTER int neg = 0, any, cutlim;

  /*
   * See strtol for comments as to the logic used.
   */
  do
    c = *s++;
  while (ACE_OS::ace_isspace(c));
  if (c == '-')
    {
      neg = 1;
      c = *s++;
    }
  else if (c == '+')
    c = *s++;
  if ((base == 0 || base == 16) &&
      c == '0' && (*s == 'x' || *s == 'X'))
    {
      c = s[1];
      s += 2;
      base = 16;
    }
  if (base == 0)
    base = c == '0' ? 8 : 10;
  cutoff = (unsigned long) ULONG_MAX / (unsigned long) base;
  cutlim = (unsigned long) ULONG_MAX % (unsigned long) base;

  for (acc = 0, any = 0;; c = *s++)
    {
      if (ACE_OS::ace_isdigit(c))
        c -= '0';
      else if (ACE_OS::ace_isalpha(c))
        c -= ACE_OS::ace_isupper(c) ? 'A' - 10 : 'a' - 10;
      else
        break;
      if (c >= base)
        break;
      if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
        any = -1;
      else
        {
          any = 1;
          acc *= base;
          acc += c;
        }
    }
  if (any < 0)
    {
      acc = ULONG_MAX;
      errno = ERANGE;
    }
  else if (neg)
    acc = -acc;
  if (endptr != 0)
    *endptr = any ? (char *) s - 1 : (char *) nptr;
  return (acc);
}
#endif /* ACE_LACKS_STRTOUL */


#if defined (ACE_HAS_WCHAR) && defined (ACE_LACKS_WCSTOUL)
unsigned long
ACE_OS::wcstoul_emulation (const wchar_t *nptr,
         wchar_t **endptr,
         int base)
{
  ACE_REGISTER const wchar_t *s = nptr;
  ACE_REGISTER unsigned long acc;
  ACE_REGISTER int c;
  ACE_REGISTER unsigned long cutoff;
  ACE_REGISTER int neg = 0, any, cutlim;

  /*
   * See strtol for comments as to the logic used.
   */
  do
    c = *s++;
  while (ACE_OS::ace_isspace(c));
  if (c == L'-')
    {
      neg = 1;
      c = *s++;
    }
  else if (c == L'+')
    c = *s++;
  if ((base == 0 || base == 16) &&
      c == L'0' && (*s == L'x' || *s == L'X'))
    {
      c = s[1];
      s += 2;
      base = 16;
    }
  if (base == 0)
    base = c == L'0' ? 8 : 10;
  cutoff = (unsigned long) ULONG_MAX / (unsigned long) base;
  cutlim = (unsigned long) ULONG_MAX % (unsigned long) base;

  for (acc = 0, any = 0;; c = *s++)
    {
      if (ACE_OS::ace_isdigit(c))
        c -= L'0';
      else if (ACE_OS::ace_isalpha(c))
        c -= ACE_OS::ace_isupper(c) ? L'A' - 10 : L'a' - 10;
      else
        break;
      if (c >= base)
        break;
      if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
        any = -1;
      else
        {
          any = 1;
          acc *= base;
          acc += c;
        }
    }
  if (any < 0)
    {
      acc = ULONG_MAX;
      errno = ERANGE;
    }
  else if (neg)
    acc = -acc;
  if (endptr != 0)
    *endptr = any ? (wchar_t *) s - 1 : (wchar_t *) nptr;
  return (acc);
}
#endif /* ACE_HAS_WCHAR && ACE_LACKS_WCSTOUL */

#if defined (ACE_LACKS_STRTOLL)
ACE_INT64
ACE_OS::strtoll_emulation (const char *nptr,
         char **endptr,
         ACE_REGISTER int base)
{
  ACE_REGISTER const char *s = nptr;
  ACE_REGISTER ACE_UINT64 acc;
  ACE_REGISTER int c;
  ACE_REGISTER ACE_UINT64 cutoff;
  ACE_REGISTER int neg = 0, any, cutlim;

  /*
   * Skip white space and pick up leading +/- sign if any.
   * If base is 0, allow 0x for hex and 0 for octal, else
   * assume decimal; if base is already 16, allow 0x.
   */
  do {
    c = *s++;
  } while (ACE_OS::ace_isspace(c));
  if (c == '-') {
    neg = 1;
    c = *s++;
  } else if (c == '+')
    c = *s++;
  if ((base == 0 || base == 16) &&
    c == '0' && (*s == 'x' || *s == 'X')) {
    c = s[1];
    s += 2;
    base = 16;
  }
  if (base == 0)
    base = c == '0' ? 8 : 10;

  /*
   * See strtol for comments as to the logic used.
   */
  cutoff = neg ? -(ACE_UINT64)ACE_INT64_MIN : ACE_INT64_MAX;
  cutlim = cutoff % (ACE_UINT64)base;
  cutoff /= (ACE_UINT64)base;
  for (acc = 0, any = 0;; c = *s++) {
    if (ACE_OS::ace_isdigit(c))
      c -= '0';
    else if (ACE_OS::ace_isalpha(c))
      c -= ACE_OS::ace_isupper(c) ? 'A' - 10 : 'a' - 10;
    else
      break;
    if (c >= base)
      break;
    if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
      any = -1;
    else {
      any = 1;
      acc *= base;
      acc += c;
    }
  }
  if (any < 0) {
    acc = neg ? ACE_INT64_MIN : ACE_INT64_MAX;
    errno = ERANGE;
  } else if (neg)
    acc = -acc;
  if (endptr != 0)
    *endptr = any ? (char *)s - 1 : (char *)nptr;
  return (acc);
}
#endif /* ACE_LACKS_STRTOLL */

#if defined (ACE_HAS_WCHAR) && defined (ACE_LACKS_WCSTOLL)
ACE_INT64
ACE_OS::wcstoll_emulation (const wchar_t *nptr,
         wchar_t **endptr,
         int base)
{
  ACE_REGISTER const wchar_t *s = nptr;
  ACE_REGISTER ACE_UINT64 acc;
  ACE_REGISTER int c;
  ACE_REGISTER ACE_UINT64 cutoff;
  ACE_REGISTER int neg = 0, any, cutlim;

  /*
   * Skip white space and pick up leading +/- sign if any.
   * If base is 0, allow 0x for hex and 0 for octal, else
   * assume decimal; if base is already 16, allow 0x.
   */
  do {
    c = *s++;
  } while (ACE_OS::ace_isspace(c));
  if (c == L'-') {
    neg = 1;
    c = *s++;
  } else if (c == L'+')
    c = *s++;
  if ((base == 0 || base == 16) &&
    c == L'0' && (*s == L'x' || *s == L'X')) {
    c = s[1];
    s += 2;
    base = 16;
  }
  if (base == 0)
    base = c == L'0' ? 8 : 10;

  /*
   * See strtol for comments as to the logic used.
   */
  cutoff = neg ? -(ACE_UINT64)ACE_INT64_MIN : ACE_INT64_MAX;
  cutlim = cutoff % (ACE_UINT64)base;
  cutoff /= (ACE_UINT64)base;
  for (acc = 0, any = 0;; c = *s++) {
    if (ACE_OS::ace_isdigit(c))
      c -= L'0';
    else if (ACE_OS::ace_isalpha(c))
      c -= ACE_OS::ace_isupper(c) ? L'A' - 10 : L'a' - 10;
    else
      break;
    if (c >= base)
      break;
    if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
      any = -1;
    else {
      any = 1;
      acc *= base;
      acc += c;
    }
  }
  if (any < 0) {
    acc = neg ? ACE_INT64_MIN : ACE_INT64_MAX;
    errno = ERANGE;
  } else if (neg)
    acc = -acc;
  if (endptr != 0)
    *endptr = any ? (wchar_t *)s - 1 : (wchar_t *)nptr;
  return (acc);
}
#endif /* ACE_HAS_WCHAR && ACE_LACKS_WCSTOLL */


#if defined (ACE_LACKS_STRTOULL)
ACE_UINT64
ACE_OS::strtoull_emulation (const char *nptr,
                            char **endptr,
                            ACE_REGISTER int base)
{
  ACE_REGISTER const char *s = nptr;
  ACE_REGISTER ACE_UINT64 acc;
  ACE_REGISTER int c;
  ACE_REGISTER ACE_UINT64 cutoff;
  ACE_REGISTER int neg = 0, any, cutlim;

  /*
   * See strtol for comments as to the logic used.
   */
  do
    c = *s++;
  while (ACE_OS::ace_isspace(c));
  if (c == '-')
    {
      neg = 1;
      c = *s++;
    }
  else if (c == '+')
    c = *s++;
  if ((base == 0 || base == 16) &&
      c == '0' && (*s == 'x' || *s == 'X'))
    {
      c = s[1];
      s += 2;
      base = 16;
    }
  if (base == 0)
    base = c == '0' ? 8 : 10;

  cutoff = (ACE_UINT64) ACE_UINT64_MAX / (ACE_UINT64) base;
  cutlim = (ACE_UINT64) ACE_UINT64_MAX % (ACE_UINT64) base;

  for (acc = 0, any = 0;; c = *s++)
    {
      if (ACE_OS::ace_isdigit(c))
        c -= '0';
      else if (ACE_OS::ace_isalpha(c))
        c -= ACE_OS::ace_isupper(c) ? 'A' - 10 : 'a' - 10;
      else
        break;
      if (c >= base)
        break;
      if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
        any = -1;
      else
        {
          any = 1;
          acc *= base;
          acc += c;
        }
    }
  if (any < 0)
    {
      acc = ACE_UINT64_MAX;
      errno = ERANGE;
    }
  else if (neg)
    acc = -acc;
  if (endptr != 0)
    *endptr = any ? (char *) s - 1 : (char *) nptr;
  return (acc);
}
#endif /* ACE_LACKS_STRTOULL */

#if defined (ACE_HAS_WCHAR) && defined (ACE_LACKS_WCSTOULL)
ACE_UINT64
ACE_OS::wcstoull_emulation (const wchar_t *nptr,
          wchar_t **endptr,
          int base)
{
  ACE_REGISTER const wchar_t *s = nptr;
  ACE_REGISTER ACE_UINT64 acc;
  ACE_REGISTER int c;
  ACE_REGISTER ACE_UINT64 cutoff;
  ACE_REGISTER int neg = 0, any, cutlim;

  /*
   * See strtol for comments as to the logic used.
   */
  do
    c = *s++;
  while (ACE_OS::ace_isspace(c));
  if (c == L'-')
    {
      neg = 1;
      c = *s++;
    }
  else if (c == L'+')
    c = *s++;
  if ((base == 0 || base == 16) &&
      c == L'0' && (*s == L'x' || *s == L'X'))
    {
      c = s[1];
      s += 2;
      base = 16;
    }
  if (base == 0)
    base = c == L'0' ? 8 : 10;

  cutoff = (ACE_UINT64) ACE_UINT64_MAX / (ACE_UINT64) base;
  cutlim = (ACE_UINT64) ACE_UINT64_MAX % (ACE_UINT64) base;

  for (acc = 0, any = 0;; c = *s++)
    {
      if (ACE_OS::ace_isdigit(c))
        c -= L'0';
      else if (ACE_OS::ace_isalpha(c))
        c -= ACE_OS::ace_isupper(c) ? L'A' - 10 : L'a' - 10;
      else
        break;
      if (c >= base)
        break;
      if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
        any = -1;
      else
        {
          any = 1;
          acc *= base;
          acc += c;
        }
    }
  if (any < 0)
    {
      acc = ACE_UINT64_MAX;
      errno = ERANGE;
    }
  else if (neg)
    acc = -acc;
  if (endptr != 0)
    *endptr = any ? (wchar_t *) s - 1 : (wchar_t *) nptr;
  return (acc);
}
#endif /* ACE_HAS_WCHAR && ACE_LACKS_WCSTOULL */

#if defined (ACE_LACKS_MKSTEMP)
ACE_HANDLE
ACE_OS::mkstemp_emulation (ACE_TCHAR * s)
{
  if (s == 0)
    {
      errno = EINVAL;
      return ACE_INVALID_HANDLE;
    }

  // The "XXXXXX" template to be filled in.
  ACE_TCHAR * const t  = ACE_OS::strstr (s, ACE_TEXT ("XXXXXX"));

  if (t == 0)
    {
      errno = EINVAL;
      return ACE_INVALID_HANDLE;
    }

  static unsigned int const NUM_RETRIES = 50;
  static unsigned int const NUM_CHARS   = 6;  // Do not change!

  // Use ACE_Time_Value::msec(ACE_UINT64&) as opposed to
  // ACE_Time_Value::msec(void) to avoid truncation.
  ACE_UINT64 msec;

  // Use a const ACE_Time_Value to resolve ambiguity between
  // ACE_Time_Value::msec (long) and ACE_Time_Value::msec(ACE_UINT64&) const.
  ACE_Time_Value const now = ACE_OS::gettimeofday();
  now.msec (msec);

  // Add the process and thread ids to ensure uniqueness.
  msec += ACE_OS::getpid();
  msec += (size_t) ACE_OS::thr_self();

  // ACE_thread_t may be a char* (returned by ACE_OS::thr_self()) so
  // we need to use a C-style cast as a catch-all in order to use a
  // static_cast<> to an integral type.
  unsigned int seed = static_cast<unsigned int> (msec);

  // We only care about UTF-8 / ASCII characters in generated
  // filenames.  A UTF-16 or UTF-32 character could potentially cause
  // a very large space to be searched in the below do/while() loop,
  // greatly slowing down this mkstemp() implementation.  It is more
  // practical to limit the search space to UTF-8 / ASCII characters
  // (i.e. 127 characters).
  //
  // Note that we can't make this constant static since the compiler
  // may not inline the return value of ACE_Numeric_Limits::max(),
  // meaning multiple threads could potentially initialize this value
  // in parallel.
  float const MAX_VAL =
    static_cast<float> (ACE_Numeric_Limits<char>::max ());

  // Use high-order bits rather than low-order ones (e.g. rand() %
  // MAX_VAL).  See Numerical Recipes in C: The Art of Scientific
  // Computing (William  H. Press, Brian P. Flannery, Saul
  // A. Teukolsky, William T. Vetterling; New York: Cambridge
  // University Press, 1992 (2nd ed., p. 277).
  //
  // e.g.: MAX_VAL * rand() / (RAND_MAX + 1.0)

  // Factor out the constant coefficient.
  float const coefficient =
    static_cast<float> (MAX_VAL / (RAND_MAX + 1.0f));

  // @@ These nested loops may be ineffecient.  Improvements are
  //    welcome.
  for (unsigned int i = 0; i < NUM_RETRIES; ++i)
    {
      for (unsigned int n = 0; n < NUM_CHARS; ++n)
        {
          ACE_TCHAR r;

          // This do/while() loop allows this alphanumeric character
          // selection to work for EBCDIC, as well.
          do
            {
              r = static_cast<ACE_TCHAR> (coefficient * ACE_OS::rand_r (&seed));
            }
          while (!ACE_OS::ace_isalnum (r));

          t[n] = r;
        }

      static int const perms =
#if defined (ACE_WIN32)
        0;      /* Do not share while open. */
#else
        0600;   /* S_IRUSR | S_IWUSR */
#endif  /* ACE_WIN32 */

      // Create the file with the O_EXCL bit set to ensure that we're
      // not subject to a symbolic link attack.
      //
      // Note that O_EXCL is subject to a race condition over NFS
      // filesystems.
      ACE_HANDLE const handle = ACE_OS::open (s,
                                              O_RDWR | O_CREAT | O_EXCL,
                                              perms);

      if (handle != ACE_INVALID_HANDLE)
        return handle;
    }

  errno = EEXIST;  // Couldn't create a unique temporary file.
  return ACE_INVALID_HANDLE;
}
#endif /* ACE_LACKS_MKSTEMP */

#if !defined (ACE_HAS_GETPROGNAME) && !defined (ACE_HAS_SETPROGNAME)
static const char *__progname = "";
#endif /* !ACE_HAS_GETPROGNAME && !ACE_HAS_SETPROGNAME */

#if !defined (ACE_HAS_GETPROGNAME)
const char*
ACE_OS::getprogname_emulation ()
{
    return __progname;
}
#endif /* !ACE_HAS_GETPROGNAME */

#if !defined (ACE_HAS_SETPROGNAME)
void
ACE_OS::setprogname_emulation (const char* progname)
{
  const char *p = ACE_OS::strrchr (progname, '/');
  if (p != 0)
    __progname = p + 1;
  else
    __progname = progname;
}
#endif /* !ACE_HAS_SETPROGNAME */

ACE_END_VERSIONED_NAMESPACE_DECL
